Salome HOME
[bos #32216][CEA] GUI ergonomic with HDPI screens. kleontev/32216_GUI_ergonomic master 28/head
authorKonstantin Leontev <Konstantin.LEONTEV@opencascade.com>
Tue, 10 Oct 2023 10:09:15 +0000 (11:09 +0100)
committerKonstantin Leontev <Konstantin.LEONTEV@opencascade.com>
Fri, 17 May 2024 13:01:21 +0000 (14:01 +0100)
Added using of Qt::AA_UseHighDpiPixmaps attribute and device pixel ratio (usually set by QT_SCALE_FACTOR env var).
Fixed OCC and Vtk viewers display and selection issues with Qt HDPI scaling.
Fixed OCC and Vtk viewers selection and context menu offset.

src/OCCViewer/OCCViewer_ViewSketcher.cxx
src/OCCViewer/OCCViewer_ViewWindow.cxx
src/SVTK/SVTK_InteractorStyle.cxx
src/SVTK/SVTK_RenderWindowInteractor.cxx
src/Session/SALOME_Session_Server.cxx
src/ViewerTools/CMakeLists.txt
src/ViewerTools/ViewerTools_ScreenScaling.cxx [new file with mode: 0644]
src/ViewerTools/ViewerTools_ScreenScaling.h [new file with mode: 0644]

index b4621257770aa28042ead568820e370958916f94..d59345aa78a1fefaf1b3c476426a442c45b5d153 100644 (file)
@@ -20,6 +20,7 @@
 #include "OCCViewer_ViewSketcher.h"
 #include "OCCViewer_ViewWindow.h"
 #include "OCCViewer_ViewPort3d.h"
+#include "ViewerTools_ScreenScaling.h"
 
 #include "QtxRubberBand.h"
 
@@ -122,6 +123,13 @@ bool OCCViewer_ViewSketcher::eventFilter( QObject* o, QEvent* e )
 {
   OCCViewer_ViewPort3d* avp = mypViewWindow->getViewPort();
 
+  // We need to downscale only non-system events here
+  if (!e->spontaneous())
+  {
+    // Make a copy event with updated coordinates
+    e = ViewerTools_ScreenScaling::getDpiAwareEvent(e, false);
+  }
+
   QMouseEvent* me = (QMouseEvent*)e;
   SketchState state = EnTrain;
   bool ignore = false;
index 3a5336beeaf12e47f767a5c3a12bbfb15fda09d4..372b9145a350e7a8c5cad6968e0844d4067a689f 100644 (file)
@@ -40,6 +40,7 @@
 #include "OCCViewer_EnvTextureDlg.h"
 #include "OCCViewer_LightSourceDlg.h"
 #include "OCCViewer_Utilities.h"
+#include "ViewerTools_ScreenScaling.h"
 
 #include <SUIT_Desktop.h>
 #include <SUIT_Session.h>
@@ -373,6 +374,9 @@ OCCViewer_ViewWindow::getButtonState( QMouseEvent* theEvent, int theInteractionS
 bool OCCViewer_ViewWindow::eventFilter( QObject* watched, QEvent* e )
 {
   if ( watched == myViewPort ) {
+    // Makes a copy event with updated coordinates if we need so
+    e = ViewerTools_ScreenScaling::getDpiAwareEvent(e);
+
     int aType = e->type();
     switch(aType) {
     case QEvent::MouseButtonPress:
@@ -1080,8 +1084,10 @@ void OCCViewer_ViewWindow::vpMouseReleaseEvent(QMouseEvent* theEvent)
       emit mouseReleased(this, theEvent);
       if(theEvent->button() == Qt::RightButton && prevState == -1)
       {
+        // We need to pass unscaled coordinates to get a menu painted in a right place.
+        const double pixelRatio = ViewerTools_ScreenScaling::getPR();
         QContextMenuEvent aEvent( QContextMenuEvent::Mouse,
-                                  theEvent->pos(), theEvent->globalPos() );
+                                  theEvent->pos() / pixelRatio, theEvent->globalPos() / pixelRatio );
         emit contextMenuRequested( &aEvent );
       }
     }
@@ -3153,6 +3159,11 @@ void OCCViewer_ViewWindow::onSketchingFinished()
   {
     Handle(AIS_InteractiveContext) ic = myModel->getAISContext();
     bool append = mypSketcher->isHasShift();
+
+    // Sketcher uses unscaled coordinates to draw a rubber band,
+    // then we need to scale them here for proper selection.
+    const double pixelRatio = ViewerTools_ScreenScaling::getPR();
+
     switch( mypSketcher->type() )
     {
     case Rect:
@@ -3160,10 +3171,10 @@ void OCCViewer_ViewWindow::onSketchingFinished()
         QRect* aRect = (QRect*)mypSketcher->data();
         if ( aRect )
         {
-          int aLeft = aRect->left();
-          int aRight = aRect->right();
-          int aTop = aRect->top();
-          int aBottom = aRect->bottom();
+          int aLeft = aRect->left() * pixelRatio;
+          int aRight = aRect->right() * pixelRatio;
+          int aTop = aRect->top() * pixelRatio;
+          int aBottom = aRect->bottom() * pixelRatio;
 //           myRect = aRect;
 
           if( append )
@@ -3186,7 +3197,7 @@ void OCCViewer_ViewWindow::onSketchingFinished()
           for (int index = 1; it != itEnd; ++it, index++)
           {
             QPoint aPoint = *it;
-            anArray.SetValue(index, gp_Pnt2d(aPoint.x(), aPoint.y()));
+            anArray.SetValue(index, gp_Pnt2d(aPoint.x() * pixelRatio, aPoint.y() * pixelRatio));
           }
 
           if (append)
index 1701d907e825839b032da7150a0318bfe4c9ca64..57da3e4fdb9f137f0634a5297cf4f1bc04f1b514 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "SUIT_Tools.h"
 #include "SALOME_Actor.h"
+#include "ViewerTools_ScreenScaling.h"
 
 #include <vtkObjectFactory.h>
 #include <vtkMath.h>
@@ -646,7 +647,12 @@ void SVTK_InteractorStyle::OnLeftButtonUp(int vtkNotUsed(ctrl),
 {
   myShiftState = shift;
   if( myPoligonState == InProcess ) { // add a new point of polygon
-    myPolygonPoints.append( QPoint( x, y ) );
+    // The mouse events were already scaled up with a pixel ratio for a proper selection,
+    // but rubber band's implemented with QPainter scales them on its own.
+    // So, we need to pass unscaled coordinates to get a polygon painted in a right place.
+    const double pixelRatio = ViewerTools_ScreenScaling::getPR();
+
+    myPolygonPoints.append(QPoint(x / pixelRatio, y / pixelRatio));
     this->Interactor->GetEventPosition( mySelectionEvent->myX, mySelectionEvent->myY );
     mySelectionEvent->myPolygonPoints.append( QPoint( mySelectionEvent->myX, mySelectionEvent->myY ) );
     return;
@@ -1672,7 +1678,17 @@ void SVTK_InteractorStyle::drawRect()
     myRectBand = new QtxRectRubberBand( GetRenderWidget() );
 
   myRectBand->setUpdatesEnabled ( false );
-  QRect aRect = SUIT_Tools::makeRect(myPoint.x(), myPoint.y(), myOtherPoint.x(), myOtherPoint.y());
+
+  // The mouse events were already scaled up with a pixel ratio for a proper selection,
+  // but rubber band's implemented with QPainter scales them on its own.
+  // So, we need to pass unscaled coordinates to get a rectangle painted in a right place.
+  const double pixelRatio = ViewerTools_ScreenScaling::getPR();
+  QRect aRect = SUIT_Tools::makeRect(
+    myPoint.x() / pixelRatio,
+    myPoint.y() / pixelRatio,
+    myOtherPoint.x() / pixelRatio,
+    myOtherPoint.y() / pixelRatio);
+
   myRectBand->initGeometry( aRect );
 
   if ( !myRectBand->isVisible() )
@@ -1771,27 +1787,34 @@ bool isValid( const QPolygon* thePoints, const QPoint& theCurrent )
 */
 void SVTK_InteractorStyle::drawPolygon()
 {
+  // The mouse events were already scaled up with a pixel ratio for a proper selection,
+  // but rubber band's implemented with QPainter scales them on its own.
+  // So, we need to pass unscaled coordinates to get a polygon painted in a right place.
+  const double pixelRatio = ViewerTools_ScreenScaling::getPR();
+  const QPoint myPointCopy(myPoint.x() / pixelRatio, myPoint.y() / pixelRatio);
+  const QPoint myOtherPointCopy(myOtherPoint.x() / pixelRatio, myOtherPoint.y() / pixelRatio);
+
   QSize aToler( 5, 5 );
   if ( !myPolygonBand ) {
     myPolygonBand = new QtxPolyRubberBand( GetRenderWidget() );
     QPalette palette;
     palette.setColor( myPolygonBand->foregroundRole(), Qt::white );
     myPolygonBand->setPalette( palette );
-    myPolygonPoints.append( QPoint( myPoint.x(), myPoint.y() ) );
+    myPolygonPoints.append(myPointCopy);
   }
   myPolygonBand->hide();
 
   bool closed = false;
-  bool valid = GetRenderWidget()->rect().contains( QPoint( myOtherPoint.x(), myOtherPoint.y() ) );
+  bool valid = GetRenderWidget()->rect().contains(myOtherPointCopy);
   if ( !myPolygonPoints.at(0).isNull() )
   {
     QRect aRect( myPolygonPoints.at(0).x() - aToler.width(), myPolygonPoints.at(0).y() - aToler.height(),
                  2 * aToler.width(), 2 * aToler.height() );
-    closed = aRect.contains( QPoint( myOtherPoint.x(), myOtherPoint.y() ) );
+    closed = aRect.contains(myOtherPointCopy);
   }
 
   QPolygon* points = new QPolygon( myPolygonPoints );
-  valid = valid && isValid( points, QPoint( myOtherPoint.x(), myOtherPoint.y() ) );
+  valid = valid && isValid(points, myOtherPointCopy);
   myPoligonState = valid ? InProcess : NotValid;
   delete points;
   if ( closed && !valid )
@@ -1806,7 +1829,7 @@ void SVTK_InteractorStyle::drawPolygon()
   else
     GetRenderWidget()->setCursor( Qt::ForbiddenCursor );
 
-  myPolygonPoints.append( QPoint( myOtherPoint.x(), myOtherPoint.y() ) );
+  myPolygonPoints.append(myOtherPointCopy);
 
   QPolygon aPolygon( myPolygonPoints );
   myPolygonBand->initGeometry( aPolygon );
index c24945c22445e0db9d388a94c080aaad16b4e37b..4ac4dea5f21a8796034d230b637b7c7ea9aa6685 100644 (file)
@@ -30,6 +30,7 @@
 #include "SVTK_Renderer.h"
 #include "SVTK_Functor.h"
 #include "SALOME_Actor.h"
+#include "ViewerTools_ScreenScaling.h"
 
 // QT Includes
 // Put Qt includes before the X11 includes which #define the symbol None
@@ -175,7 +176,8 @@ QVTK_RenderWindowInteractor
 ::polish()
 {
   // Final initialization just before the widget is displayed
-  GetDevice()->SetSize(width(),height());
+  const double pixelRatio = ViewerTools_ScreenScaling::getPR();
+  GetDevice()->SetSize(width() * pixelRatio, height() * pixelRatio);
   if(!GetDevice()->GetInitialized() && GetDevice()->GetRenderWindow()){
     GetDevice()->Initialize();
     GetDevice()->ConfigureEvent();
@@ -208,13 +210,15 @@ QVTK_RenderWindowInteractor
 */
 void
 QVTK_RenderWindowInteractor
-::resizeEvent( QResizeEvent* /*theEvent*/ )
+::resizeEvent( QResizeEvent* /* theEvent */ )
 {
+
   int* aSize = getRenderWindow()->GetSize();
   int aWidth = aSize[0];
   int aHeight = aSize[1];
 
-  GetDevice()->UpdateSize(width(),height());
+  const double pixelRatio = ViewerTools_ScreenScaling::getPR();
+  GetDevice()->UpdateSize(width() * pixelRatio, height() * pixelRatio);
 
   if(isVisible() && aWidth && aHeight){
     if( aWidth != width() || aHeight != height() ) {
@@ -695,6 +699,7 @@ void
 SVTK_RenderWindowInteractor
 ::mouseMoveEvent( QMouseEvent* event ) 
 {
+  event = static_cast<QMouseEvent*>(ViewerTools_ScreenScaling::getDpiAwareEvent(event));
   QVTK_RenderWindowInteractor::mouseMoveEvent(event);
 
   if(GENERATE_SUIT_EVENTS)
@@ -709,6 +714,7 @@ void
 SVTK_RenderWindowInteractor
 ::mousePressEvent( QMouseEvent* event ) 
 {
+  event = static_cast<QMouseEvent*>(ViewerTools_ScreenScaling::getDpiAwareEvent(event));
   QVTK_RenderWindowInteractor::mousePressEvent(event);
 
   if(GENERATE_SUIT_EVENTS)
@@ -733,6 +739,7 @@ SVTK_RenderWindowInteractor
       isOperation = style->CurrentState() != VTK_INTERACTOR_STYLE_CAMERA_NONE;
   }
 
+  event = static_cast<QMouseEvent*>(ViewerTools_ScreenScaling::getDpiAwareEvent(event));
   QVTK_RenderWindowInteractor::mouseReleaseEvent(event);
 
   if ( style ) {
@@ -743,8 +750,10 @@ SVTK_RenderWindowInteractor
   if ( aRightBtn && !isOperation && !isPolygonalSelection &&
        !( event->modifiers() & Qt::ControlModifier ) &&
        !( event->modifiers() & Qt::ShiftModifier ) ) {
+    // We need to pass unscaled coordinates to get a menu painted in a right place.
+    const double pixelRatio = ViewerTools_ScreenScaling::getPR();
     QContextMenuEvent aEvent( QContextMenuEvent::Mouse,
-                              event->pos(), event->globalPos() );
+                              event->pos() / pixelRatio, event->globalPos() / pixelRatio);
     emit contextMenuRequested( &aEvent );
   }
   if(GENERATE_SUIT_EVENTS)
@@ -759,6 +768,8 @@ void
 SVTK_RenderWindowInteractor
 ::mouseDoubleClickEvent( QMouseEvent* event )
 {
+  event = static_cast<QMouseEvent*>(ViewerTools_ScreenScaling::getDpiAwareEvent(event));
+
   if( GetInteractorStyle() && event->button() == Qt::LeftButton ) {
     SVTK_InteractorStyle* style = dynamic_cast<SVTK_InteractorStyle*>( GetInteractorStyle() );
     if ( style )
@@ -779,6 +790,8 @@ void
 SVTK_RenderWindowInteractor
 ::wheelEvent( QWheelEvent* event )
 {
+  event = static_cast<QWheelEvent*>(ViewerTools_ScreenScaling::getDpiAwareEvent(event));
+
   QVTK_RenderWindowInteractor::wheelEvent(event);
 
   if(event->delta() > 0)
index b4bd5c6116f9d9207e3cf77581dc7db8152c0dd6..b291e1070405cec17322668750c4c6ed5d7b80d5 100644 (file)
@@ -477,6 +477,25 @@ int AbstractGUIAppMain(int argc, char **argv)
   QApplication::setApplicationName("salome");
   QApplication::setApplicationVersion(salomeVersion());
 
+  // supports HDPI
+  MESSAGE("Set QApplication attributes to supports HDPI...");
+
+  // Make QIcon::pixmap() generate high-dpi pixmaps that can be larger than the requested size.
+  // Such pixmaps will have devicePixelRatio() set to a value higher than 1.
+  // After setting this attribute, application code that uses pixmap sizes in layout geometry calculations
+  // should typically divide by devicePixelRatio() to get device-independent layout geometry.
+  QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
+
+  // The lines below do the same - enables automatic scaling, based on the monitor's pixel density.
+  // This won't change the size of point-sized fonts, since point is a physical measurement unit.
+  // Using them can make an impression of large icons among small fonts.
+  // qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
+  // QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // Doesn't scale fonts
+
+  // Keep this line as an example of using explicit scale factor.
+  // Defines a global scale factor for the whole application, including point-sized fonts.
+  // qputenv("QT_SCALE_FACTOR", "1.5"); // Scales everything
+
   // Install Qt debug messages handler
   MsgHandler msgHandler;
   qInstallMessageHandler(QtxMsgHandler);
index 44792cd1154868552a80bd1161d7e37de7c31e43..fa3791cc85ede57957fb3606cb059d4d553a5ee7 100644 (file)
@@ -31,7 +31,10 @@ INCLUDE_DIRECTORIES(
 ADD_DEFINITIONS(${QT_DEFINITIONS})
 
 # libraries to link to
-SET(_link_LIBRARIES ${QT_LIBRARIES} qtx)
+SET(_link_LIBRARIES
+  ${QT_LIBRARIES}
+  ${KERNEL_SALOMELocalTrace}
+  qtx)
 
 # --- headers ---
 
@@ -40,6 +43,7 @@ SET(_moc_HEADERS
   ViewerTools_CubeAxesDlgBase.h
   ViewerTools_DialogBase.h
   ViewerTools_FontWidgetBase.h
+  ViewerTools_ScreenScaling.h
 )
 
 # header files / no moc processing
@@ -67,6 +71,7 @@ SET(_other_SOURCES
   ViewerTools_CubeAxesDlgBase.cxx
   ViewerTools_DialogBase.cxx
   ViewerTools_FontWidgetBase.cxx
+  ViewerTools_ScreenScaling.cxx
 )
 
 # sources / to compile
diff --git a/src/ViewerTools/ViewerTools_ScreenScaling.cxx b/src/ViewerTools/ViewerTools_ScreenScaling.cxx
new file mode 100644 (file)
index 0000000..e4dd6d9
--- /dev/null
@@ -0,0 +1,167 @@
+// Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "ViewerTools_ScreenScaling.h"
+
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QEvent>
+#include <QMouseEvent>
+#include <QWheelEvent>
+
+#include "utilities.h"
+
+/*!
+ * Namespace   : ViewerTools_ScreenScaling
+ * Description : Tools for handle UI on screens with different scaling
+ */
+namespace ViewerTools_ScreenScaling
+{
+  // Returns pixel ratio for the current screen
+  double getPR()
+  {
+    auto getPixelRatio = []() -> double
+    {
+      // Returns the device pixel ratio for the device as a floating point number.
+      // Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" displays.
+      // Returns QT_SCALE_FACTOR if QT_SCALE_FACTOR was set.
+      // Returns 2 on HDPI if QApplication::setAttribute(Qt::AA_EnableHighDpiScaling) was set.
+      // Returns 2 * QT_SCALE_FACTOR if both of the features above were set.
+      // NOTE. QT_SCALE_FACTOR breaks UI in a set (-1.0, 0.0) and (0.0, 1.0).
+      // Negative values don't scale or break UI down, but end up with 1.0 pixel ratio.
+      const double pixelRatio = QApplication::desktop()->devicePixelRatioF();
+      MESSAGE("pixelRatio: " << pixelRatio);
+
+      // Keep commented block here for a test case where we use QT_SCALE_FACTOR value instead.
+      // QByteArray scaleFactorArr = qgetenv("QT_SCALE_FACTOR");
+      // const QString scaleFactorStr = QString::fromLocal8Bit(scaleFactorArr);
+
+      // bool isScale = false;
+      // const double scaleFactor = scaleFactorStr.toDouble(&isScale);
+      // if (isScale)
+      // {
+      //   MESSAGE("scaleFactor: " << scaleFactor);
+      //   return scaleFactor;
+      // }
+
+      return pixelRatio;
+    };
+
+    // TODO: check if we need to get it out of static to handle moving to another screen
+    static const double pixelRatio = getPixelRatio();
+    return pixelRatio;
+  }
+
+  // Check if we have pixel ratio != 1.0
+  bool isScaledPixelRatio()
+  {
+    auto isScaledPR = []() -> bool
+    {
+      // This an arbitrary value that seems to be meaningful for UI scaling.
+      // It's not clear if we need smaller values.
+      const double epsilon = 0.01;
+      const double pixelRatio = ViewerTools_ScreenScaling::getPR();
+
+      const bool isScaled = std::abs(pixelRatio - 1.0) > epsilon;
+      MESSAGE("isScaled: " << isScaled);
+
+      return isScaled;
+    };
+
+    static const bool isScaled = isScaledPR();
+    return isScaled;
+  }
+
+
+  // Returns a copy of the given event with the local coordinates
+  // updated with the current pixel ratio.
+  QEvent* getDpiAwareEvent(QEvent* e, bool toMultiply/*  = true */)
+  {
+    // Calculate a new position
+    auto getNewPos = [toMultiply](const QPointF& pos) -> QPointF
+    {
+      double pixelRatio = ViewerTools_ScreenScaling::getPR();
+      if (!toMultiply)
+      {
+        pixelRatio = 1.0 / pixelRatio;
+      }
+
+      const double x = pos.x() * pixelRatio;
+      const double y = pos.y() * pixelRatio;
+
+      // Commented because of bloated output for casual debug
+      // MESSAGE("New pos: " << x << ", " << y);
+
+      return QPointF(x, y);
+    };
+
+    // Scales mouse event
+    auto makeMouseEvent = [&getNewPos](QMouseEvent* e) -> QEvent*
+    {
+      const QPointF pos = e->localPos();
+      const QPointF newPos = getNewPos(pos);
+
+      const QPointF globalPos = e->globalPos();
+      const QPointF globalNewPos = getNewPos(globalPos);
+
+      // Commented because of bloated output for casual debug
+      // MESSAGE("type: " << e->type() << "; old: " << pos.x() << ", " << pos.y() << "; new: " << newPos.x() << ", " << newPos.y());
+
+      return new QMouseEvent(e->type(), newPos, globalNewPos, e->button(), e->buttons(), e->modifiers());
+    };
+
+    // Scales wheel event
+    auto makeWheelEvent = [&getNewPos](QWheelEvent* e) -> QEvent*
+    {
+      const QPointF pos = e->posF();
+
+      // Commented because of bloated output for casual debug
+      // MESSAGE("Old pos: " << pos.x() << ", " << pos.y());
+
+      return new QWheelEvent(
+        getNewPos(pos), e->globalPosF(), e->pixelDelta(), e->angleDelta(), e->buttons(), e->modifiers(), e->phase(), e->inverted(), e->source());
+    };
+
+    // Return on start if we don't have any scaling
+    if (!isScaledPixelRatio())
+      return e;
+
+    // Make a copy of the event with the new position
+    const int aType = e->type();
+    switch(aType)
+    {
+      case QEvent::MouseButtonPress:
+      case QEvent::MouseButtonRelease:
+      case QEvent::MouseMove:
+      case QEvent::MouseButtonDblClick:
+        return makeMouseEvent((QMouseEvent*)e);
+
+      case QEvent::Wheel:
+        return makeWheelEvent((QWheelEvent*)e);
+
+      default:
+        break;
+    }
+
+    return e;
+  }
+};
diff --git a/src/ViewerTools/ViewerTools_ScreenScaling.h b/src/ViewerTools/ViewerTools_ScreenScaling.h
new file mode 100644 (file)
index 0000000..12e70fb
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef VIEWERTOOLS_SCREENSCALING_H
+#define VIEWERTOOLS_SCREENSCALING_H
+
+#include "ViewerTools.h"
+
+class QEvent;
+
+/*!
+ * Namespace   : ViewerTools_ScreenScaling
+ * Description : Tools for handle UI on screens with different scaling
+ */
+namespace ViewerTools_ScreenScaling
+{
+  // Returns pixel ratio for the current screen
+  VIEWERTOOLS_EXPORT double getPR();
+
+  // Check if we have pixel ratio != 1.0
+  VIEWERTOOLS_EXPORT bool isScaledPixelRatio();
+
+  // Returns a copy of the given event with the local coordinates
+  // updated with the current pixel ratio.
+  VIEWERTOOLS_EXPORT QEvent* getDpiAwareEvent(QEvent* e, bool toMultiply = true);
+}
+
+#endif