diff --git a/include/IrrlichtDevice.h b/include/IrrlichtDevice.h index c6a19787..4fc5be22 100644 --- a/include/IrrlichtDevice.h +++ b/include/IrrlichtDevice.h @@ -132,6 +132,11 @@ namespace irr /** \param text: New text of the window caption. */ virtual void setWindowCaption(const wchar_t* text) = 0; + //! Sets the window icon. + /** \param img The icon texture. + \return False if no icon was set. */ + virtual bool setWindowIcon(const video::IImage *img) = 0; + //! Returns if the window is active. /** If the window is inactive, nothing needs to be drawn. So if you don't want to draw anything @@ -307,6 +312,10 @@ namespace irr used. */ virtual E_DEVICE_TYPE getType() const = 0; + //! Get the display density in dots per inch. + //! Returns 0.0f on failure. + virtual float getDisplayDensity() const = 0; + //! Check if a driver type is supported by the engine. /** Even if true is returned the driver may not be available for a configuration requested when creating the device. */ diff --git a/source/Irrlicht/CIrrDeviceLinux.cpp b/source/Irrlicht/CIrrDeviceLinux.cpp index 0efc8f4f..9d4033b5 100644 --- a/source/Irrlicht/CIrrDeviceLinux.cpp +++ b/source/Irrlicht/CIrrDeviceLinux.cpp @@ -24,6 +24,8 @@ #include "SIrrCreationParameters.h" #include "SExposedVideoData.h" #include "IGUISpriteBank.h" +#include "IImageLoader.h" +#include "IFileSystem.h" #include #include @@ -171,6 +173,8 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param) if (param.WindowMaximized) maximizeWindow(); + + setupTopLevelXorgWindow(); } @@ -282,6 +286,72 @@ bool CIrrDeviceLinux::switchToFullscreen() } +void CIrrDeviceLinux::setupTopLevelXorgWindow() +{ +#ifdef _IRR_COMPILE_WITH_X11_ + if (CreationParams.DriverType == video::EDT_NULL) + return; // no display and window + + os::Printer::log("Configuring X11-specific top level window properties", ELL_DEBUG); + + // Set application name and class hints. For now name and class are the same. + // Note: SDL uses the executable name here (i.e. "minetest"). + XClassHint *classhint = XAllocClassHint(); + classhint->res_name = const_cast("Minetest"); + classhint->res_class = const_cast("Minetest"); + + XSetClassHint(XDisplay, XWindow, classhint); + XFree(classhint); + + // FIXME: In the future WMNormalHints should be set ... e.g see the + // gtk/gdk code (gdk/x11/gdksurface-x11.c) for the setup_top_level + // method. But for now (as it would require some significant changes) + // leave the code as is. + + // The following is borrowed from the above gdk source for setting top + // level windows. The source indicates and the Xlib docs suggest that + // this will set the WM_CLIENT_MACHINE and WM_LOCAL_NAME. This will not + // set the WM_CLIENT_MACHINE to a Fully Qualified Domain Name (FQDN) which is + // required by the Extended Window Manager Hints (EWMH) spec when setting + // the _NET_WM_PID (see further down) but running Minetest in an env + // where the window manager is on another machine from Minetest (therefore + // making the PID useless) is not expected to be a problem. Further + // more, using gtk/gdk as the model it would seem that not using a FQDN is + // not an issue for modern Xorg window managers. + + os::Printer::log("Setting Xorg window manager Properties", ELL_DEBUG); + + XSetWMProperties (XDisplay, XWindow, NULL, NULL, NULL, 0, NULL, NULL, NULL); + + // Set the _NET_WM_PID window property according to the EWMH spec. _NET_WM_PID + // (in conjunction with WM_CLIENT_MACHINE) can be used by window managers to + // force a shutdown of an application if it doesn't respond to the destroy + // window message. + + os::Printer::log("Setting Xorg _NET_WM_PID extended window manager property", ELL_DEBUG); + + Atom NET_WM_PID = XInternAtom(XDisplay, "_NET_WM_PID", false); + + pid_t pid = getpid(); + + XChangeProperty(XDisplay, XWindow, NET_WM_PID, + XA_CARDINAL, 32, PropModeReplace, + reinterpret_cast(&pid),1); + + // Set the WM_CLIENT_LEADER window property here. Minetest has only one + // window and that window will always be the leader. + + os::Printer::log("Setting Xorg WM_CLIENT_LEADER property", ELL_DEBUG); + + Atom WM_CLIENT_LEADER = XInternAtom(XDisplay, "WM_CLIENT_LEADER", false); + + XChangeProperty (XDisplay, XWindow, WM_CLIENT_LEADER, + XA_WINDOW, 32, PropModeReplace, + reinterpret_cast(&XWindow), 1); +#endif +} + + #if defined(_IRR_COMPILE_WITH_X11_) void IrrPrintXGrabError(int grabResult, const c8 * grabCommand ) { @@ -1178,6 +1248,50 @@ void CIrrDeviceLinux::setWindowCaption(const wchar_t* text) } +//! Sets the window icon. +bool CIrrDeviceLinux::setWindowIcon(const video::IImage *img) +{ + if (CreationParams.DriverType == video::EDT_NULL) + return false; // no display and window + + u32 height = img->getDimension().Height; + u32 width = img->getDimension().Width; + + size_t icon_buffer_len = 2 + height * width; + long *icon_buffer = new long[icon_buffer_len]; + + icon_buffer[0] = width; + icon_buffer[1] = height; + + for (u32 x = 0; x < width; x++) { + for (u32 y = 0; y < height; y++) { + video::SColor col = img->getPixel(x, y); + long pixel_val = 0; + pixel_val |= (u8)col.getAlpha() << 24; + pixel_val |= (u8)col.getRed() << 16; + pixel_val |= (u8)col.getGreen() << 8; + pixel_val |= (u8)col.getBlue(); + icon_buffer[2 + x + y * width] = pixel_val; + } + } + + if (XDisplay == NULL) { + os::Printer::log("Could not find x11 display for setting its icon.", ELL_ERROR); + delete[] icon_buffer; + return false; + } + + Atom net_wm_icon = XInternAtom(XDisplay, "_NET_WM_ICON", False); + Atom cardinal = XInternAtom(XDisplay, "CARDINAL", False); + XChangeProperty(XDisplay, XWindow, net_wm_icon, cardinal, 32, PropModeReplace, + (const unsigned char *)icon_buffer, icon_buffer_len); + + delete[] icon_buffer; + + return true; +} + + //! notifies the device that it should close itself void CIrrDeviceLinux::closeDevice() { @@ -1866,6 +1980,28 @@ void CIrrDeviceLinux::clearSystemMessages() #endif //_IRR_COMPILE_WITH_X11_ } +//! Get the display density in dots per inch. +float CIrrDeviceLinux::getDisplayDensity() const +{ +#ifdef _IRR_COMPILE_WITH_X11_ + if (XDisplay != NULL) { + /* try x direct */ + int dh = DisplayHeight(XDisplay, 0); + int dw = DisplayWidth(XDisplay, 0); + int dh_mm = DisplayHeightMM(XDisplay, 0); + int dw_mm = DisplayWidthMM(XDisplay, 0); + + if (dh_mm != 0 && dw_mm != 0) { + float dpi_height = floor(dh / (dh_mm * 0.039370) + 0.5); + float dpi_width = floor(dw / (dw_mm * 0.039370) + 0.5); + return std::max(dpi_height, dpi_width); + } + } +#endif //_IRR_COMPILE_WITH_X11_ + + return 0.0f; +} + void CIrrDeviceLinux::initXAtoms() { #ifdef _IRR_COMPILE_WITH_X11_ diff --git a/source/Irrlicht/CIrrDeviceLinux.h b/source/Irrlicht/CIrrDeviceLinux.h index 544a7364..5ac6eae9 100644 --- a/source/Irrlicht/CIrrDeviceLinux.h +++ b/source/Irrlicht/CIrrDeviceLinux.h @@ -54,6 +54,9 @@ namespace irr //! 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; @@ -120,6 +123,9 @@ namespace irr return EIDT_X11; } + //! Get the display density in dots per inch. + float getDisplayDensity() const override; + #ifdef _IRR_COMPILE_WITH_X11_ // convert an Irrlicht texture to a X11 cursor Cursor TextureToCursor(irr::video::ITexture * tex, const core::rect& sourceRect, const core::position2d &hotspot); @@ -146,6 +152,8 @@ namespace irr bool switchToFullscreen(); + void setupTopLevelXorgWindow(); + #ifdef _IRR_COMPILE_WITH_X11_ bool createInputContext(); void destroyInputContext(); diff --git a/source/Irrlicht/CIrrDeviceStub.cpp b/source/Irrlicht/CIrrDeviceStub.cpp index 3fde44ac..647257c7 100644 --- a/source/Irrlicht/CIrrDeviceStub.cpp +++ b/source/Irrlicht/CIrrDeviceStub.cpp @@ -136,6 +136,13 @@ ITimer* CIrrDeviceStub::getTimer() } +//! Sets the window icon. +bool CIrrDeviceStub::setWindowIcon(const video::IImage *img) +{ + return false; +} + + //! Returns the version of the engine. const char* CIrrDeviceStub::getVersion() const { @@ -385,6 +392,12 @@ void CIrrDeviceStub::clearSystemMessages() { } +//! Get the display density in dots per inch. +float CIrrDeviceStub::getDisplayDensity() const +{ + return 0.0f; +} + //! Checks whether the input device should take input from the IME bool CIrrDeviceStub::acceptsIME() { diff --git a/source/Irrlicht/CIrrDeviceStub.h b/source/Irrlicht/CIrrDeviceStub.h index 10e5d72e..3ffa8f38 100644 --- a/source/Irrlicht/CIrrDeviceStub.h +++ b/source/Irrlicht/CIrrDeviceStub.h @@ -71,6 +71,9 @@ namespace irr //! Returns a pointer to the ITimer object. With it the current Time can be received. ITimer* getTimer() override; + //! Sets the window icon. + bool setWindowIcon(const video::IImage *img) override; + //! Returns the version of the engine. const char* getVersion() const override; @@ -151,6 +154,9 @@ namespace irr //! Remove all messages pending in the system message loop void clearSystemMessages() override; + //! Get the display density in dots per inch. + float getDisplayDensity() const override; + //! Resize the render window. void setWindowSize(const irr::core::dimension2d& size) override {} diff --git a/source/Irrlicht/CIrrDeviceWin32.cpp b/source/Irrlicht/CIrrDeviceWin32.cpp index 7b12232c..5eb0680a 100644 --- a/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/source/Irrlicht/CIrrDeviceWin32.cpp @@ -1112,6 +1112,26 @@ void CIrrDeviceWin32::setWindowCaption(const wchar_t* text) } +//! Sets the window icon. +bool CIrrDeviceWin32::setWindowIcon(const video::IImage *img) +{ + // Ignore the img, instead load the ICON from resource file + // (This is minetest-specific!) + const HICON hicon = LoadIcon(GetModuleHandle(NULL), + MAKEINTRESOURCE(130) // The ID of the ICON defined in + // winresource.rc + ); + + if (hicon) { + SendMessage(HWnd, WM_SETICON, ICON_BIG, reinterpret_cast(hicon)); + SendMessage(HWnd, WM_SETICON, ICON_SMALL, + reinterpret_cast(hicon)); + return true; + } + return false; +} + + //! notifies the device that it should close itself void CIrrDeviceWin32::closeDevice() { @@ -1373,6 +1393,17 @@ void CIrrDeviceWin32::clearSystemMessages() {} } + +//! Get the display density in dots per inch. +float CIrrDeviceWin32::getDisplayDensity() const +{ + HDC hdc = GetDC(HWnd); + float dpi = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(HWnd, hdc); + return dpi; +} + + // Convert an Irrlicht texture to a Windows cursor // Based on http://www.codeguru.com/cpp/w-p/win32/cursors/article.php/c4529/ HCURSOR CIrrDeviceWin32::TextureToCursor(HWND hwnd, irr::video::ITexture * tex, const core::rect& sourceRect, const core::position2d &hotspot) diff --git a/source/Irrlicht/CIrrDeviceWin32.h b/source/Irrlicht/CIrrDeviceWin32.h index 9ca44acc..6737e780 100644 --- a/source/Irrlicht/CIrrDeviceWin32.h +++ b/source/Irrlicht/CIrrDeviceWin32.h @@ -47,6 +47,9 @@ namespace irr //! 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; @@ -96,6 +99,9 @@ namespace irr return EIDT_WIN32; } + //! Get the display density in dots per inch. + float getDisplayDensity() const override; + //! Compares to the last call of this function to return double and triple clicks. //! \return Returns only 1,2 or 3. A 4th click will start with 1 again. u32 checkSuccessiveClicks(s32 mouseX, s32 mouseY, EMOUSE_INPUT_EVENT inputEvent ) override