// Copyright (C) 2002-2012 Nikolaus Gebhardt // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h // This device code is based on the original SDL device implementation // contributed by Shane Parker (sirshane). #pragma once #ifdef _IRR_COMPILE_WITH_SDL_DEVICE_ #include "IrrlichtDevice.h" #include "CIrrDeviceStub.h" #include "ICursorControl.h" #ifdef _IRR_EMSCRIPTEN_PLATFORM_ #include #endif #include // DirectFB is removed in SDL3, thou distribution as Alpine currently ships SDL2 // with enabled DirectFB, but requiring another fix at a top of SDL2. // We don't need DirectFB in Irrlicht/Minetest, so simply disable it here to prevent issues. #undef SDL_VIDEO_DRIVER_DIRECTFB #include #include namespace irr { class CIrrDeviceSDL : public CIrrDeviceStub { public: //! constructor CIrrDeviceSDL(const SIrrlichtCreationParameters ¶m); //! destructor virtual ~CIrrDeviceSDL(); //! runs the device. Returns false if device wants to be deleted bool run() override; //! pause execution temporarily void yield() override; //! pause execution for a specified time void sleep(u32 timeMs, bool pauseTimer) override; //! sets the caption of the window void setWindowCaption(const wchar_t *text) override; //! Sets the window icon. bool setWindowIcon(const video::IImage *img) override; //! returns if window is active. if not, nothing need to be drawn bool isWindowActive() const override; //! returns if window has focus. bool isWindowFocused() const override; //! returns if window is minimized. bool isWindowMinimized() const override; //! returns color format of the window. video::ECOLOR_FORMAT getColorFormat() const override; //! notifies the device that it should close itself void closeDevice() override; //! Sets if the window should be resizable in windowed mode. void setResizable(bool resize = false) override; //! Minimizes the window. void minimizeWindow() override; //! Maximizes the window. void maximizeWindow() override; //! Restores the window size. void restoreWindow() override; //! Checks if the window is maximized. bool isWindowMaximized() const override; //! Checks if the Irrlicht window is running in fullscreen mode /** \return True if window is fullscreen. */ bool isFullscreen() const override; //! Get the position of this window on screen core::position2di getWindowPosition() override; //! Activate any joysticks, and generate events for them. bool activateJoysticks(core::array &joystickInfo) override; //! Get the device type E_DEVICE_TYPE getType() const override { return EIDT_SDL; } //! Get the display density in dots per inch. float getDisplayDensity() const override; void SwapWindow(); //! Implementation of the linux cursor control class CCursorControl : public gui::ICursorControl { public: CCursorControl(CIrrDeviceSDL *dev) : Device(dev), IsVisible(true) { initCursors(); } //! Changes the visible state of the mouse cursor. void setVisible(bool visible) override { IsVisible = visible; if (visible) SDL_ShowCursor(SDL_ENABLE); else { SDL_ShowCursor(SDL_DISABLE); } } //! Returns if the cursor is currently visible. bool isVisible() const override { return IsVisible; } //! Sets the new position of the cursor. void setPosition(const core::position2d &pos) override { setPosition(pos.X, pos.Y); } //! Sets the new position of the cursor. void setPosition(f32 x, f32 y) override { setPosition((s32)(x * Device->Width), (s32)(y * Device->Height)); } //! Sets the new position of the cursor. void setPosition(const core::position2d &pos) override { setPosition(pos.X, pos.Y); } //! Sets the new position of the cursor. void setPosition(s32 x, s32 y) override { SDL_WarpMouseInWindow(Device->Window, x, y); if (SDL_GetRelativeMouseMode()) { // There won't be an event for this warp (details on libsdl-org/SDL/issues/6034) Device->MouseX = x; Device->MouseY = y; } } //! Returns the current position of the mouse cursor. const core::position2d &getPosition(bool updateCursor) override { if (updateCursor) updateCursorPos(); return CursorPos; } //! Returns the current position of the mouse cursor. core::position2d getRelativePosition(bool updateCursor) override { if (updateCursor) updateCursorPos(); return core::position2d(CursorPos.X / (f32)Device->Width, CursorPos.Y / (f32)Device->Height); } void setReferenceRect(core::rect *rect = 0) override { } virtual void setRelativeMode(bool relative) _IRR_OVERRIDE_ { // Only change it when necessary, as it flushes mouse motion when enabled if (relative != SDL_GetRelativeMouseMode()) { if (relative) SDL_SetRelativeMouseMode(SDL_TRUE); else SDL_SetRelativeMouseMode(SDL_FALSE); } } void setActiveIcon(gui::ECURSOR_ICON iconId) override { ActiveIcon = iconId; if (iconId > Cursors.size() || !Cursors[iconId]) { iconId = gui::ECI_NORMAL; if (iconId > Cursors.size() || !Cursors[iconId]) return; } SDL_SetCursor(Cursors[iconId].get()); } gui::ECURSOR_ICON getActiveIcon() const override { return ActiveIcon; } private: void updateCursorPos() { #ifdef _IRR_EMSCRIPTEN_PLATFORM_ EmscriptenPointerlockChangeEvent pointerlockStatus; // let's hope that test is not expensive ... if (emscripten_get_pointerlock_status(&pointerlockStatus) == EMSCRIPTEN_RESULT_SUCCESS) { if (pointerlockStatus.isActive) { CursorPos.X += Device->MouseXRel; CursorPos.Y += Device->MouseYRel; Device->MouseXRel = 0; Device->MouseYRel = 0; } else { CursorPos.X = Device->MouseX; CursorPos.Y = Device->MouseY; } } #else CursorPos.X = Device->MouseX; CursorPos.Y = Device->MouseY; if (CursorPos.X < 0) CursorPos.X = 0; if (CursorPos.X > (s32)Device->Width) CursorPos.X = Device->Width; if (CursorPos.Y < 0) CursorPos.Y = 0; if (CursorPos.Y > (s32)Device->Height) CursorPos.Y = Device->Height; #endif } void initCursors(); CIrrDeviceSDL *Device; core::position2d CursorPos; bool IsVisible; struct CursorDeleter { void operator()(SDL_Cursor *ptr) { if (ptr) SDL_FreeCursor(ptr); } }; std::vector> Cursors; gui::ECURSOR_ICON ActiveIcon = gui::ECURSOR_ICON::ECI_NORMAL; }; private: #ifdef _IRR_EMSCRIPTEN_PLATFORM_ static EM_BOOL MouseUpDownCallback(int eventType, const EmscriptenMouseEvent *event, void *userData); static EM_BOOL MouseEnterCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); static EM_BOOL MouseLeaveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); #endif // Check if a key is a known special character with no side effects on text boxes. static bool keyIsKnownSpecial(EKEY_CODE key); // Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0). static int findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key); // Check if a text box is in focus. Enable or disable SDL_TEXTINPUT events only if in focus. void resetReceiveTextInputEvents(); //! create the driver void createDriver(); bool createWindow(); void createKeyMap(); void logAttributes(); SDL_GLContext Context; SDL_Window *Window; int SDL_Flags; #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) core::array Joysticks; #endif s32 MouseX, MouseY; s32 MouseXRel, MouseYRel; u32 MouseButtonStates; u32 Width, Height; bool Resizable; core::rect lastElemPos; struct SKeyMap { SKeyMap() {} SKeyMap(s32 x11, s32 win32) : SDLKey(x11), Win32Key(win32) { } s32 SDLKey; s32 Win32Key; bool operator<(const SKeyMap &o) const { return SDLKey < o.SDLKey; } }; core::array KeyMap; SDL_SysWMinfo Info; s32 CurrentTouchCount; }; } // end namespace irr #endif // _IRR_COMPILE_WITH_SDL_DEVICE_