// Created on: 2013-04-06 // Created by: Kirill Gavrilov // Copyright (c) 2013-2014 OPEN CASCADE SAS // // This file is part of Open CASCADE Technology software library. // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License version 2.1 as published // by the Free Software Foundation, with special exception defined in the file // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT // distribution for complete text of the license and disclaimer of any warranty. // // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. #include #include #include #include #include #if defined(HAVE_XLIB) #include #include // #include #endif #include IMPLEMENT_STANDARD_RTTIEXT(Xw_Window, Aspect_Window) //================================================================================================= Xw_Window::Xw_Window(const Handle(Aspect_DisplayConnection)& theXDisplay, const Standard_CString theTitle, const Standard_Integer thePxLeft, const Standard_Integer thePxTop, const Standard_Integer thePxWidth, const Standard_Integer thePxHeight) : Aspect_Window(), myXWindow(0), myFBConfig(NULL), myXLeft(thePxLeft), myYTop(thePxTop), myXRight(thePxLeft + thePxWidth), myYBottom(thePxTop + thePxHeight), myIsOwnWin(Standard_True) { myDisplay = theXDisplay; if (thePxWidth <= 0 || thePxHeight <= 0) { throw Aspect_WindowDefinitionError("Xw_Window, Coordinate(s) out of range"); } else if (theXDisplay.IsNull()) { throw Aspect_WindowDefinitionError("Xw_Window, X Display connection is undefined"); } #if defined(HAVE_XLIB) myFBConfig = theXDisplay->GetDefaultFBConfig(); XVisualInfo* aVisInfo = theXDisplay->GetDefaultVisualInfoX(); Display* aDisp = myDisplay->GetDisplay(); int aScreen = DefaultScreen(aDisp); Window aParent = RootWindow(aDisp, aScreen); XSetWindowAttributes aWinAttr; memset(&aWinAttr, 0, sizeof(aWinAttr)); aWinAttr.event_mask = ExposureMask | StructureNotifyMask; if (aVisInfo != NULL) { aWinAttr.colormap = XCreateColormap(aDisp, aParent, aVisInfo->visual, AllocNone); } aWinAttr.border_pixel = 0; aWinAttr.override_redirect = False; myXWindow = (Window)XCreateWindow(aDisp, aParent, myXLeft, myYTop, thePxWidth, thePxHeight, 0, aVisInfo != NULL ? aVisInfo->depth : CopyFromParent, InputOutput, aVisInfo != NULL ? aVisInfo->visual : CopyFromParent, CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &aWinAttr); if (myXWindow == 0) { throw Aspect_WindowDefinitionError("Xw_Window, Unable to create window"); } // if parent - desktop XSizeHints aSizeHints; aSizeHints.x = myXLeft; aSizeHints.y = myYTop; aSizeHints.flags = PPosition; aSizeHints.width = thePxWidth; aSizeHints.height = thePxHeight; aSizeHints.flags |= PSize; XSetStandardProperties(aDisp, (Window)myXWindow, theTitle, theTitle, None, NULL, 0, &aSizeHints); /*XTextProperty aTitleProperty; aTitleProperty.encoding = None; char* aTitle = (char* )theTitle; Xutf8TextListToTextProperty(aDisp, &aTitle, 1, XUTF8StringStyle, &aTitleProperty); XSetWMName (aDisp, (Window )myXWindow, &aTitleProperty); XSetWMProperties(aDisp, (Window )myXWindow, &aTitleProperty, &aTitleProperty, NULL, 0, NULL, NULL, NULL);*/ XFlush(aDisp); #else (void)theTitle; if (myXWindow == 0) { throw Aspect_WindowDefinitionError("Xw_Window, Unable to create window - not implemented"); } #endif } //================================================================================================= Xw_Window::Xw_Window(const Handle(Aspect_DisplayConnection)& theXDisplay, const Aspect_Drawable theXWin, const Aspect_FBConfig theFBConfig) : Aspect_Window(), myXWindow(theXWin), myFBConfig(theFBConfig), myXLeft(0), myYTop(0), myXRight(512), myYBottom(512), myIsOwnWin(Standard_False) { myDisplay = theXDisplay; if (theXWin == 0) { throw Aspect_WindowDefinitionError("Xw_Window, given invalid X window"); } else if (theXDisplay.IsNull()) { throw Aspect_WindowDefinitionError("Xw_Window, X Display connection is undefined"); } #if defined(HAVE_XLIB) Display* aDisp = myDisplay->GetDisplay(); XWindowAttributes aWinAttr; XGetWindowAttributes(aDisp, (Window)myXWindow, &aWinAttr); XVisualInfo aVisInfoTmp; aVisInfoTmp.visualid = aWinAttr.visual->visualid; aVisInfoTmp.screen = DefaultScreen(aDisp); int aNbItems = 0; XVisualInfo* aVisInfo = XGetVisualInfo(aDisp, VisualIDMask | VisualScreenMask, &aVisInfoTmp, &aNbItems); if (aVisInfo == NULL) { throw Aspect_WindowDefinitionError("Xw_Window, Visual is unavailable"); } XFree(aVisInfo); DoResize(); #else // throw Standard_NotImplemented("Xw_Window, not implemented"); #endif } //================================================================================================= Xw_Window::~Xw_Window() { if (myIsOwnWin && myXWindow != 0 && !myDisplay.IsNull()) { #if defined(HAVE_XLIB) XDestroyWindow(myDisplay->GetDisplay(), (Window)myXWindow); #endif } } //================================================================================================= Standard_Boolean Xw_Window::IsMapped() const { if (myXWindow == 0) { return false; } else if (IsVirtual()) { return Standard_True; } #if defined(HAVE_XLIB) XFlush(myDisplay->GetDisplay()); XWindowAttributes aWinAttr; XGetWindowAttributes(myDisplay->GetDisplay(), (Window)myXWindow, &aWinAttr); return aWinAttr.map_state == IsUnviewable || aWinAttr.map_state == IsViewable; #else return Standard_False; #endif } //================================================================================================= void Xw_Window::Map() const { if (IsVirtual() || myXWindow == 0) { return; } #if defined(HAVE_XLIB) XMapWindow(myDisplay->GetDisplay(), (Window)myXWindow); XFlush(myDisplay->GetDisplay()); #endif } //================================================================================================= void Xw_Window::Unmap() const { if (IsVirtual() || myXWindow == 0) { return; } #if defined(HAVE_XLIB) XIconifyWindow(myDisplay->GetDisplay(), (Window)myXWindow, DefaultScreen(myDisplay->GetDisplay())); #endif } //================================================================================================= Aspect_TypeOfResize Xw_Window::DoResize() { if (IsVirtual() || myXWindow == 0) { return Aspect_TOR_UNKNOWN; } #if defined(HAVE_XLIB) XFlush(myDisplay->GetDisplay()); XWindowAttributes aWinAttr; memset(&aWinAttr, 0, sizeof(aWinAttr)); XGetWindowAttributes(myDisplay->GetDisplay(), (Window)myXWindow, &aWinAttr); if (aWinAttr.map_state == IsUnmapped) { return Aspect_TOR_UNKNOWN; } Standard_Integer aMask = 0; Aspect_TypeOfResize aMode = Aspect_TOR_UNKNOWN; if (Abs(aWinAttr.x - myXLeft) > 2) aMask |= 1; if (Abs((aWinAttr.x + aWinAttr.width) - myXRight) > 2) aMask |= 2; if (Abs(aWinAttr.y - myYTop) > 2) aMask |= 4; if (Abs((aWinAttr.y + aWinAttr.height) - myYBottom) > 2) aMask |= 8; switch (aMask) { case 0: aMode = Aspect_TOR_NO_BORDER; break; case 1: aMode = Aspect_TOR_LEFT_BORDER; break; case 2: aMode = Aspect_TOR_RIGHT_BORDER; break; case 4: aMode = Aspect_TOR_TOP_BORDER; break; case 5: aMode = Aspect_TOR_LEFT_AND_TOP_BORDER; break; case 6: aMode = Aspect_TOR_TOP_AND_RIGHT_BORDER; break; case 8: aMode = Aspect_TOR_BOTTOM_BORDER; break; case 9: aMode = Aspect_TOR_BOTTOM_AND_LEFT_BORDER; break; case 10: aMode = Aspect_TOR_RIGHT_AND_BOTTOM_BORDER; break; default: break; } myXLeft = aWinAttr.x; myXRight = aWinAttr.x + aWinAttr.width; myYTop = aWinAttr.y; myYBottom = aWinAttr.y + aWinAttr.height; return aMode; #else return Aspect_TOR_UNKNOWN; #endif } //================================================================================================= Standard_Real Xw_Window::Ratio() const { if (IsVirtual() || myXWindow == 0) { return Standard_Real(myXRight - myXLeft) / Standard_Real(myYBottom - myYTop); } #if defined(HAVE_XLIB) XFlush(myDisplay->GetDisplay()); XWindowAttributes aWinAttr; memset(&aWinAttr, 0, sizeof(aWinAttr)); XGetWindowAttributes(myDisplay->GetDisplay(), (Window)myXWindow, &aWinAttr); return Standard_Real(aWinAttr.width) / Standard_Real(aWinAttr.height); #else return 1.0; #endif } //================================================================================================= void Xw_Window::Position(Standard_Integer& theX1, Standard_Integer& theY1, Standard_Integer& theX2, Standard_Integer& theY2) const { if (IsVirtual() || myXWindow == 0) { theX1 = myXLeft; theX2 = myXRight; theY1 = myYTop; theY2 = myYBottom; return; } #if defined(HAVE_XLIB) XFlush(myDisplay->GetDisplay()); XWindowAttributes anAttributes; memset(&anAttributes, 0, sizeof(anAttributes)); XGetWindowAttributes(myDisplay->GetDisplay(), (Window)myXWindow, &anAttributes); Window aChild; XTranslateCoordinates(myDisplay->GetDisplay(), anAttributes.root, (Window)myXWindow, 0, 0, &anAttributes.x, &anAttributes.y, &aChild); theX1 = -anAttributes.x; theX2 = theX1 + anAttributes.width; theY1 = -anAttributes.y; theY2 = theY1 + anAttributes.height; #endif } //================================================================================================= void Xw_Window::Size(Standard_Integer& theWidth, Standard_Integer& theHeight) const { if (IsVirtual() || myXWindow == 0) { theWidth = myXRight - myXLeft; theHeight = myYBottom - myYTop; return; } #if defined(HAVE_XLIB) XFlush(myDisplay->GetDisplay()); XWindowAttributes aWinAttr; memset(&aWinAttr, 0, sizeof(aWinAttr)); XGetWindowAttributes(myDisplay->GetDisplay(), (Window)myXWindow, &aWinAttr); theWidth = aWinAttr.width; theHeight = aWinAttr.height; #endif } //================================================================================================= void Xw_Window::SetTitle(const TCollection_AsciiString& theTitle) { if (myXWindow != 0) { #if defined(HAVE_XLIB) XStoreName(myDisplay->GetDisplay(), (Window)myXWindow, theTitle.ToCString()); #else (void)theTitle; #endif } } //================================================================================================= void Xw_Window::InvalidateContent(const Handle(Aspect_DisplayConnection)& theDisp) { if (myXWindow == 0) { return; } #if defined(HAVE_XLIB) const Handle(Aspect_DisplayConnection)& aDisp = !theDisp.IsNull() ? theDisp : myDisplay; Display* aDispX = aDisp->GetDisplay(); XEvent anEvent; memset(&anEvent, 0, sizeof(anEvent)); anEvent.type = Expose; anEvent.xexpose.window = (Window)myXWindow; XSendEvent(aDispX, (Window)myXWindow, False, ExposureMask, &anEvent); XFlush(aDispX); #else (void)theDisp; #endif } //================================================================================================= Aspect_VKey Xw_Window::VirtualKeyFromNative(unsigned long theKey) { #if defined(HAVE_XLIB) if (theKey >= XK_0 && theKey <= XK_9) { return Aspect_VKey(theKey - XK_0 + Aspect_VKey_0); } if (theKey >= XK_A && theKey <= XK_Z) { return Aspect_VKey(theKey - XK_A + Aspect_VKey_A); } if (theKey >= XK_a && theKey <= XK_z) { return Aspect_VKey(theKey - XK_a + Aspect_VKey_A); } if (theKey >= XK_F1 && theKey <= XK_F24) { if (theKey <= XK_F12) { return Aspect_VKey(theKey - XK_F1 + Aspect_VKey_F1); } return Aspect_VKey_UNKNOWN; } switch (theKey) { case XK_space: return Aspect_VKey_Space; case XK_apostrophe: return Aspect_VKey_Apostrophe; case XK_comma: return Aspect_VKey_Comma; case XK_minus: return Aspect_VKey_Minus; case XK_period: return Aspect_VKey_Period; case XK_semicolon: return Aspect_VKey_Semicolon; case XK_equal: return Aspect_VKey_Equal; case XK_bracketleft: return Aspect_VKey_BracketLeft; case XK_backslash: return Aspect_VKey_Backslash; case XK_bracketright: return Aspect_VKey_BracketRight; case XK_BackSpace: return Aspect_VKey_Backspace; case XK_Tab: return Aspect_VKey_Tab; // case XK_Linefeed: case XK_Return: case XK_KP_Enter: return Aspect_VKey_Enter; // case XK_Pause: // return Aspect_VKey_Pause; case XK_Escape: return Aspect_VKey_Escape; case XK_Home: return Aspect_VKey_Home; case XK_Left: return Aspect_VKey_Left; case XK_Up: return Aspect_VKey_Up; case XK_Right: return Aspect_VKey_Right; case XK_Down: return Aspect_VKey_Down; case XK_Prior: return Aspect_VKey_PageUp; case XK_Next: return Aspect_VKey_PageDown; case XK_End: return Aspect_VKey_End; // case XK_Insert: // return Aspect_VKey_Insert; case XK_Menu: return Aspect_VKey_Menu; case XK_Num_Lock: return Aspect_VKey_Numlock; // case XK_KP_Delete: // return Aspect_VKey_NumDelete; case XK_KP_Multiply: return Aspect_VKey_NumpadMultiply; case XK_KP_Add: return Aspect_VKey_NumpadAdd; // case XK_KP_Separator: // return Aspect_VKey_Separator; case XK_KP_Subtract: return Aspect_VKey_NumpadSubtract; // case XK_KP_Decimal: // return Aspect_VKey_Decimal; case XK_KP_Divide: return Aspect_VKey_NumpadDivide; case XK_Shift_L: case XK_Shift_R: return Aspect_VKey_Shift; case XK_Control_L: case XK_Control_R: return Aspect_VKey_Control; // case XK_Caps_Lock: // return Aspect_VKey_CapsLock; case XK_Alt_L: case XK_Alt_R: return Aspect_VKey_Alt; // case XK_Super_L: // case XK_Super_R: // return Aspect_VKey_Super; case XK_Delete: return Aspect_VKey_Delete; case 0x1008FF11: // XF86AudioLowerVolume return Aspect_VKey_VolumeDown; case 0x1008FF12: // XF86AudioMute return Aspect_VKey_VolumeMute; case 0x1008FF13: // XF86AudioRaiseVolume return Aspect_VKey_VolumeUp; case 0x1008FF14: // XF86AudioPlay return Aspect_VKey_MediaPlayPause; case 0x1008FF15: // XF86AudioStop return Aspect_VKey_MediaStop; case 0x1008FF16: // XF86AudioPrev return Aspect_VKey_MediaPreviousTrack; case 0x1008FF17: // XF86AudioNext return Aspect_VKey_MediaNextTrack; case 0x1008FF18: // XF86HomePage return Aspect_VKey_BrowserHome; case 0x1008FF26: // XF86Back return Aspect_VKey_BrowserBack; case 0x1008FF27: // XF86Forward return Aspect_VKey_BrowserForward; case 0x1008FF28: // XF86Stop return Aspect_VKey_BrowserStop; case 0x1008FF29: // XF86Refresh return Aspect_VKey_BrowserRefresh; } #else (void)theKey; #endif return Aspect_VKey_UNKNOWN; } //================================================================================================= bool Xw_Window::ProcessMessage(Aspect_WindowInputListener& theListener, XEvent& #if defined(HAVE_XLIB) // msvc before VS2015 had problems with (void )theMsg theMsg #endif ) { #if defined(HAVE_XLIB) Display* aDisplay = myDisplay->GetDisplay(); // Handle event for the chosen display connection switch (theMsg.type) { case ClientMessage: { if ((Atom)theMsg.xclient.data.l[0] == myDisplay->GetAtom(Aspect_XA_DELETE_WINDOW) && theMsg.xclient.window == (Window)myXWindow) { theListener.ProcessClose(); return true; } return false; } case FocusIn: case FocusOut: { if (theMsg.xfocus.window == (Window)myXWindow) { theListener.ProcessFocus(theMsg.type == FocusIn); } return true; } case Expose: { if (theMsg.xexpose.window == (Window)myXWindow) { theListener.ProcessExpose(); } // remove all the ExposureMask and process them at once for (int aNbMaxEvents = XPending(aDisplay); aNbMaxEvents > 0; --aNbMaxEvents) { if (!XCheckWindowEvent(aDisplay, (Window)myXWindow, ExposureMask, &theMsg)) { break; } } return true; } case ConfigureNotify: { // remove all the StructureNotifyMask and process them at once for (int aNbMaxEvents = XPending(aDisplay); aNbMaxEvents > 0; --aNbMaxEvents) { if (!XCheckWindowEvent(aDisplay, (Window)myXWindow, StructureNotifyMask, &theMsg)) { break; } } if (theMsg.xconfigure.window == (Window)myXWindow) { theListener.ProcessConfigure(true); } return true; } case KeyPress: case KeyRelease: { XKeyEvent* aKeyEvent = (XKeyEvent*)&theMsg; const KeySym aKeySym = XLookupKeysym(aKeyEvent, 0); const Aspect_VKey aVKey = Xw_Window::VirtualKeyFromNative(aKeySym); if (aVKey != Aspect_VKey_UNKNOWN) { const double aTimeStamp = theListener.EventTime(); if (theMsg.type == KeyPress) { theListener.KeyDown(aVKey, aTimeStamp); } else { theListener.KeyUp(aVKey, aTimeStamp); } theListener.ProcessInput(); } return true; } case ButtonPress: case ButtonRelease: { const Graphic3d_Vec2i aPos(theMsg.xbutton.x, theMsg.xbutton.y); Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE; Aspect_VKeyMouse aButton = Aspect_VKeyMouse_NONE; if (theMsg.xbutton.button == Button1) { aButton = Aspect_VKeyMouse_LeftButton; } if (theMsg.xbutton.button == Button2) { aButton = Aspect_VKeyMouse_MiddleButton; } if (theMsg.xbutton.button == Button3) { aButton = Aspect_VKeyMouse_RightButton; } if ((theMsg.xbutton.state & ControlMask) != 0) { aFlags |= Aspect_VKeyFlags_CTRL; } if ((theMsg.xbutton.state & ShiftMask) != 0) { aFlags |= Aspect_VKeyFlags_SHIFT; } if (theListener.Keys().IsKeyDown(Aspect_VKey_Alt)) { aFlags |= Aspect_VKeyFlags_ALT; } if (theMsg.xbutton.button == Button4 || theMsg.xbutton.button == Button5) { if (theMsg.type != ButtonPress) { return true; } const double aDeltaF = (theMsg.xbutton.button == Button4 ? 1.0 : -1.0); theListener.UpdateMouseScroll(Aspect_ScrollDelta(aPos, aDeltaF, aFlags)); } else if (theMsg.type == ButtonPress) { theListener.PressMouseButton(aPos, aButton, aFlags, false); } else { theListener.ReleaseMouseButton(aPos, aButton, aFlags, false); } theListener.ProcessInput(); return true; } case MotionNotify: { if (theMsg.xmotion.window != (Window)myXWindow) { return false; } // remove all the ButtonMotionMask and process them at once for (int aNbMaxEvents = XPending(aDisplay); aNbMaxEvents > 0; --aNbMaxEvents) { if (!XCheckWindowEvent(aDisplay, (Window)myXWindow, ButtonMotionMask | PointerMotionMask, &theMsg)) { break; } } Graphic3d_Vec2i aPos(theMsg.xmotion.x, theMsg.xmotion.y); Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE; Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE; if ((theMsg.xmotion.state & Button1Mask) != 0) { aButtons |= Aspect_VKeyMouse_LeftButton; } if ((theMsg.xmotion.state & Button2Mask) != 0) { aButtons |= Aspect_VKeyMouse_MiddleButton; } if ((theMsg.xmotion.state & Button3Mask) != 0) { aButtons |= Aspect_VKeyMouse_RightButton; } if ((theMsg.xmotion.state & ControlMask) != 0) { aFlags |= Aspect_VKeyFlags_CTRL; } if ((theMsg.xmotion.state & ShiftMask) != 0) { aFlags |= Aspect_VKeyFlags_SHIFT; } if (theListener.Keys().IsKeyDown(Aspect_VKey_Alt)) { aFlags |= Aspect_VKeyFlags_ALT; } theListener.UpdateMousePosition(aPos, aButtons, aFlags, false); theListener.ProcessInput(); return true; } } #else (void)theListener; #endif return false; }