diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 13a35b592..e7a63a1b5 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -55,6 +55,11 @@ namespace MWGui class DialogueWindow; } +namespace SFO +{ + class CursorChangeClient; +} + namespace MWBase { /// \brief Interface for widnow manager (implemented in MWGui) @@ -238,6 +243,8 @@ namespace MWBase virtual void startTraining(MWWorld::Ptr actor) = 0; virtual const Translation::Storage& getTranslationDataStorage() const = 0; + + virtual void setCursorChangeClient(SFO::CursorChangeClient* client) = 0; }; } diff --git a/apps/openmw/mwgui/cursorreplace.cpp b/apps/openmw/mwgui/cursorreplace.cpp index 2079538fc..f883a4ed7 100644 --- a/apps/openmw/mwgui/cursorreplace.cpp +++ b/apps/openmw/mwgui/cursorreplace.cpp @@ -5,9 +5,73 @@ #include #include +#include using namespace MWGui; + +#include "MyGUI_Precompiled.h" +#include "MyGUI_ResourceImageSetPointer.h" +#include "MyGUI_ImageBox.h" +#include "MyGUI_ResourceManager.h" + + + + ResourceImageSetPointerFix::ResourceImageSetPointerFix() : + mImageSet(nullptr) + { + } + + ResourceImageSetPointerFix::~ResourceImageSetPointerFix() + { + } + + void ResourceImageSetPointerFix::deserialization(xml::ElementPtr _node, Version _version) + { + Base::deserialization(_node, _version); + + // берем детей и крутимся, основной цикл + xml::ElementEnumerator info = _node->getElementEnumerator(); + while (info.next("Property")) + { + const std::string& key = info->findAttribute("key"); + const std::string& value = info->findAttribute("value"); + + if (key == "Point") + mPoint = IntPoint::parse(value); + else if (key == "Size") + mSize = IntSize::parse(value); + else if (key == "Resource") + mImageSet = ResourceManager::getInstance().getByName(value)->castType(); + } + } + + void ResourceImageSetPointerFix::setImage(ImageBox* _image) + { + if (mImageSet != nullptr) + _image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0)); + } + + void ResourceImageSetPointerFix::setPosition(ImageBox* _image, const IntPoint& _point) + { + _image->setCoord(_point.left - mPoint.left, _point.top - mPoint.top, mSize.width, mSize.height); + } + + ResourceImageSetPtr ResourceImageSetPointerFix:: getImageSet() + { + return mImageSet; + } + + IntPoint ResourceImageSetPointerFix::getHotSpot() + { + return mPoint; + } + + IntSize ResourceImageSetPointerFix::getSize() + { + return mSize; + } + CursorReplace::CursorReplace() { OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90); diff --git a/apps/openmw/mwgui/cursorreplace.hpp b/apps/openmw/mwgui/cursorreplace.hpp index 06fe28e39..402eda1ab 100644 --- a/apps/openmw/mwgui/cursorreplace.hpp +++ b/apps/openmw/mwgui/cursorreplace.hpp @@ -2,9 +2,44 @@ #define GAME_CURSORREPLACE_H #include +#include +#include + +using namespace MyGUI; namespace MWGui { + /// \brief A simple class that allows us to get the members of + /// ResourceImageSetPointer that we need. Use with + /// MyGUI + /// \example MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); + /// MyGUI::ResourceManager::getInstance().load("core.xml"); + class ResourceImageSetPointerFix : + public IPointer + { + MYGUI_RTTI_DERIVED( ResourceImageSetPointerFix ) + + public: + ResourceImageSetPointerFix(); + virtual ~ResourceImageSetPointerFix(); + + virtual void deserialization(xml::ElementPtr _node, Version _version); + + virtual void setImage(ImageBox* _image); + virtual void setPosition(ImageBox* _image, const IntPoint& _point); + + //and now for the whole point of this class, allow us to get + //the hot spot, the image and the size of the cursor. + virtual ResourceImageSetPtr getImageSet(); + virtual IntPoint getHotSpot(); + virtual IntSize getSize(); + + private: + IntPoint mPoint; + IntSize mSize; + ResourceImageSetPtr mImageSet; + }; + /// \brief MyGUI does not support rotating cursors, so we have to do it manually class CursorReplace { diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index d653b578a..7c99d004e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -4,10 +4,15 @@ #include #include "MyGUI_UString.h" +#include "MYGUI/MyGUI_IPointer.h" +#include "MYGUI/MyGUI_ResourceImageSetPointer.h" +#include "MyGUI_TextureUtility.h" #include #include +#include + #include #include @@ -53,6 +58,8 @@ #include "trainingwindow.hpp" #include "imagebutton.hpp" +#include "cursorreplace.hpp" + using namespace MWGui; WindowManager::WindowManager( @@ -108,12 +115,17 @@ WindowManager::WindowManager( , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mHudEnabled(true) , mTranslationDataStorage (translationDataStorage) + , mCursorChangeClient(NULL) { // Set up the GUI system mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, logpath); mGui = mGuiManager->getGui(); + //Use our own ResourceImageSetPointer class so we can get the texture and hotspot for a pointer + MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); + MyGUI::ResourceManager::getInstance().load("core.xml"); + //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); @@ -130,6 +142,7 @@ WindowManager::WindowManager( MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); + MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &WindowManager::onCursorChange); // Get size info from the Gui object assert(mGui); @@ -758,6 +771,44 @@ void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _r } } + void WindowManager::setCursorChangeClient(SFO::CursorChangeClient* client) + { + mCursorChangeClient = client; + onCursorChange(PointerManager::getInstance().getDefaultPointer()); + } + +void WindowManager::onCursorChange(const std::string &name) +{ + //we have no client, don't care. + if(!mCursorChangeClient) + return; + + //the client doesn't want any more info about this cursor + if(!mCursorChangeClient->cursorChanged(name)) + return; + //See if we can get the information we need out of the cursor resource + ResourceImageSetPointerFix* imgSetPtr = dynamic_cast(MyGUI::PointerManager::getInstance().getByName(name)); + if(imgSetPtr != NULL) + { + MyGUI::ResourceImageSet* imgSet = imgSetPtr->getImageSet(); + + std::string tex_name = imgSet->getIndexInfo(0,0).texture; + + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().getByName(tex_name); + + //everything looks good, send it to the client + if(!tex.isNull()) + { + Uint8 size_x = imgSetPtr->getSize().width; + Uint8 size_y = imgSetPtr->getSize().height; + Uint8 hotspot_x = imgSetPtr->getHotSpot().left; + Uint8 hotspot_y = imgSetPtr->getHotSpot().top; + + mCursorChangeClient->receiveCursorInfo(name, tex, size_x, size_y, hotspot_x, hotspot_y); + } + } +} + void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) { mHud->setFpsLevel(Settings::Manager::getInt("fps", "HUD")); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index e2a966660..bbcdbbb41 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -48,6 +48,11 @@ namespace OEngine } } +namespace SFO +{ + class CursorChangeClient; +} + namespace MWGui { class WindowBase; @@ -228,6 +233,8 @@ namespace MWGui virtual const Translation::Storage& getTranslationDataStorage() const; + virtual void setCursorChangeClient(SFO::CursorChangeClient* client); + private: OEngine::GUI::MyGUIManager *mGuiManager; HUD *mHud; @@ -282,6 +289,8 @@ namespace MWGui MyGUI::Gui *mGui; // Gui std::vector mGuiModes; + SFO::CursorChangeClient* mCursorChangeClient; + std::vector mGarbageDialogs; void cleanupGarbage(); @@ -310,6 +319,8 @@ namespace MWGui * so this method will retrieve the GMST with the name \a _tag and place the result in \a _result */ void onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result); + + void onCursorChange(const std::string& name); }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 7785167f8..c1f857b5f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -67,6 +67,8 @@ namespace MWInput mInputManager->setKeyboardEventCallback (this); mInputManager->setWindowEventCallback(this); + mWindows.setCursorChangeClient(mInputManager); + std::string file = userFileExists ? userFile : ""; mInputBinder = new ICS::InputControlSystem(file, true, this, NULL, A_Last); diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index e194719cb..1b996ce06 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -3,6 +3,8 @@ #include #include +#include +#include #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX # include @@ -13,6 +15,8 @@ namespace SFO { + /// \brief General purpose wrapper for OGRE applications around SDL's event + /// queue, mostly used for handling input-related events. InputWrapper::InputWrapper(Ogre::RenderWindow *window) : mWindow(window), mSDLWindow(NULL), @@ -58,6 +62,11 @@ namespace SFO if(mSDLWindow == NULL) return false; + //without this SDL will take ownership of the window and iconify it when + //we alt-tab away. + SDL_SetWindowFullscreen(mSDLWindow, 0); + + //translate our keypresses into text SDL_StartTextInput(); #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX @@ -160,6 +169,7 @@ namespace SFO return SDL_GetModState() & mod; } + /// \brief Moves the mouse to the specified point within the viewport void InputWrapper::warpMouse(int x, int y) { SDL_WarpMouseInWindow(mSDLWindow, x, y); @@ -168,14 +178,15 @@ namespace SFO mWarpY = y; } + /// \brief Locks the pointer to the window void InputWrapper::setGrabPointer(bool grab) { - SDL_bool sdlGrab = grab ? SDL_TRUE : SDL_FALSE; - mGrabPointer = grab; - SDL_SetWindowGrab(mSDLWindow, sdlGrab); + SDL_SetWindowGrab(mSDLWindow, grab ? SDL_TRUE : SDL_FALSE); } + /// \brief Set the mouse to relative positioning. Doesn't move the cursor + /// and disables mouse acceleration. void InputWrapper::setMouseRelative(bool relative) { if(mMouseRelative == relative) @@ -200,6 +211,121 @@ namespace SFO SDL_PeepEvents(dummy, 20, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION); } + bool InputWrapper::cursorChanged(const std::string &name) + { + CursorMap::const_iterator curs_iter = mCursorMap.find(name); + + //we have this cursor + if(curs_iter != mCursorMap.end()) + { + SDL_SetCursor(curs_iter->second); + return false; + } + else + { + //they should get back to use with more info + return true; + } + } + + void InputWrapper::receiveCursorInfo(const std::string& name, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) + { + _createCursorFromResource(name, tex, size_x, size_y, hotspot_x, hotspot_y); + } + + /// \brief creates an SDL cursor from an Ogre texture + void InputWrapper::_createCursorFromResource(const std::string& name, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) + { + Ogre::Image::Box box; + box.right = size_x; + box.bottom = size_y; + + //get the surfaces set up + Ogre::HardwarePixelBufferSharedPtr buffer = tex.get()->getBuffer(); + buffer.get()->lock(box, Ogre::HardwarePixelBuffer::HBL_READ_ONLY); + + std::string tempName = "_" + name + "_processing"; + + //we need to copy this to a temporary texture since Ogre doesn't like us using getColourAt + Ogre::TexturePtr tempTexture = Ogre::TextureManager::getSingleton().createManual( + tempName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + size_x, size_y, + 0, + Ogre::PF_FLOAT16_RGBA, + Ogre::TU_STATIC); + + tempTexture->getBuffer()->blit(buffer); + buffer->unlock(); + + Ogre::HardwarePixelBufferSharedPtr new_buffer = tempTexture.get()->getBuffer(); + //FIXME: Casting away constness is almost certainly the wrong thing to do here + Ogre::PixelBox& pixels = const_cast(new_buffer->lock(box, Ogre::HardwarePixelBuffer::HBL_READ_ONLY)); + + + SDL_Surface* surf = SDL_CreateRGBSurface(0,size_x,size_y,32,0,0,0,0); + + + //copy the Ogre texture to an SDL surface + for(size_t x = 0; x < size_x; ++x) + { + for(size_t y = 0; y < size_y; ++y) + { + Ogre::ColourValue clr = pixels.getColourAt(x, y, 0); + + //set the pixel on the SDL surface to the same value as the Ogre texture's + _putPixel(surf, x, y, SDL_MapRGBA(surf->format, clr.r, clr.g, clr.b, clr.a)); + } + } + + //set the cursor and store it for later + SDL_Cursor* curs = SDL_CreateColorCursor(surf, hotspot_x, hotspot_y); + SDL_SetCursor(curs); + mCursorMap.insert(CursorMap::value_type(std::string(name), curs)); + + new_buffer->unlock(); + + //clean up + SDL_FreeSurface(surf); + Ogre::TextureManager::getSingleton().remove(tempName); + } + + void InputWrapper::_putPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) + { + int bpp = surface->format->BytesPerPixel; + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch(bpp) { + case 1: + *p = pixel; + break; + + case 2: + *(Uint16 *)p = pixel; + break; + + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (pixel >> 16) & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = pixel & 0xff; + } else { + p[0] = pixel & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = (pixel >> 16) & 0xff; + } + break; + + case 4: + *(Uint32 *)p = pixel; + break; + } + } + + /// \brief Internal method for ignoring relative motions as a side effect + /// of warpMouse() bool InputWrapper::_handleWarpMotion(const SDL_MouseMotionEvent& evt) { if(!mWarpCompensate) @@ -215,6 +341,7 @@ namespace SFO return false; } + /// \brief Wrap the mouse to the viewport void InputWrapper::_wrapMousePointer(const SDL_MouseMotionEvent& evt) { //don't wrap if we don't want relative movements, support relative @@ -238,6 +365,7 @@ namespace SFO } } + /// \brief Package mouse and mousewheel motions into a single event MouseMotionEvent InputWrapper::_packageMouseMotion(const SDL_Event &evt) { MouseMotionEvent pack_evt; diff --git a/extern/sdl4ogre/sdlinputwrapper.hpp b/extern/sdl4ogre/sdlinputwrapper.hpp index eba2ed8d0..aac2bf566 100644 --- a/extern/sdl4ogre/sdlinputwrapper.hpp +++ b/extern/sdl4ogre/sdlinputwrapper.hpp @@ -9,13 +9,33 @@ #include "events.h" +namespace Ogre +{ + class Texture; +} + namespace SFO { - class InputWrapper + + + class CursorChangeClient + { + public: + /// \brief Tell the client that the cursor has changed, giving the + /// name of the cursor we changed to ("arrow", "ibeam", etc) + /// \return Whether the client is interested in more information about the cursor + virtual bool cursorChanged(const std::string &name) = 0; + + /// \brief Follow up a cursorChanged() call with enough info to create an SDL cursor. + virtual void receiveCursorInfo(const std::string &name, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) = 0; + }; + + class InputWrapper : + public CursorChangeClient { public: InputWrapper(Ogre::RenderWindow* window); - ~InputWrapper(); + virtual ~InputWrapper(); void setMouseEventCallback(MouseListener* listen) { mMouseListener = listen; } void setKeyboardEventCallback(KeyListener* listen) { mKeyboardListener = listen; } @@ -28,9 +48,13 @@ namespace SFO bool getMouseRelative() { return mMouseRelative; } void setGrabPointer(bool grab); + virtual bool cursorChanged(const std::string &name); + virtual void receiveCursorInfo(const std::string &name, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y); + OIS::KeyCode sdl2OISKeyCode(SDL_Keycode code); void warpMouse(int x, int y); + private: bool _start(); @@ -38,6 +62,9 @@ namespace SFO void _wrapMousePointer(const SDL_MouseMotionEvent &evt); MouseMotionEvent _packageMouseMotion(const SDL_Event& evt); + void _createCursorFromResource(const std::string &name, Ogre::TexturePtr tex, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y); + void _putPixel(SDL_Surface *surface, int x, int y, Uint32 pixel); + void _handleKeyPress(SDL_KeyboardEvent& evt); Uint32 _UTF8ToUTF32(const unsigned char *buf); void _setupOISKeys(); @@ -49,6 +76,9 @@ namespace SFO typedef boost::unordered_map KeyMap; KeyMap mKeyMap; + typedef std::map CursorMap; + CursorMap mCursorMap; + Uint16 mWarpX; Uint16 mWarpY; bool mWarpCompensate; @@ -63,6 +93,7 @@ namespace SFO Ogre::RenderWindow* mWindow; SDL_Window* mSDLWindow; }; + } #endif