1
0
forked from 0ad/0ad

Moves cursor to VideoMode to draw it via SDL.

It removes the software implementation intentionally. Because it
duplicates SDL functionality. But it might be added in future on demand.

Tested By: bb, Langbart
Differential Revision: https://code.wildfiregames.com/D4278
This was SVN commit r25936.
This commit is contained in:
Vladislav Belov 2021-09-21 22:44:46 +00:00
parent b4f0464591
commit 0e599a3176
14 changed files with 204 additions and 532 deletions

View File

@ -105,8 +105,9 @@ showsky = true
; for a debugging of a system without GL_KHR_debug.
gl.checkerrorafterswap = false
; Disable hardware cursors
nohwcursor = false
; Different ways to draw a cursor, possible values are "sdl" and "system".
; The "system" one doesn't support a visual change of the cursor.
cursorbackend = "sdl"
; Specify the render path. This can be one of:
; default Automatically select one of the below, depending on system capabilities

View File

@ -25,6 +25,7 @@
#include "ps/Filesystem.h"
#include "ps/GameSetup/Config.h"
#include "ps/Profile.h"
#include "ps/VideoMode.h"
#include "ps/XML/Xeromyces.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptContext.h"
@ -146,7 +147,7 @@ void CGUIManager::SGUIPage::LoadPage(std::shared_ptr<ScriptContext> scriptContex
hotloadData = Script::WriteStructuredClone(rq, hotloadDataVal);
}
g_CursorName = g_DefaultCursor;
g_VideoMode.ResetCursor();
inputs.clear();
gui.reset(new CGUI(scriptContext));

View File

@ -23,6 +23,7 @@
#include "gui/GUIManager.h"
#include "gui/ObjectBases/IGUIObject.h"
#include "ps/GameSetup/Config.h"
#include "ps/VideoMode.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/StructuredClone.h"
@ -52,16 +53,14 @@ void PopGuiPage(const ScriptRequest& rq, JS::HandleValue args)
g_GUI->PopPage(Script::WriteStructuredClone(rq, args));
}
std::wstring SetCursor(const std::wstring& name)
void SetCursor(const std::wstring& name)
{
std::wstring old = g_CursorName;
g_CursorName = name;
return old;
g_VideoMode.SetCursor(name);
}
void ResetCursor()
{
g_CursorName = g_DefaultCursor;
g_VideoMode.ResetCursor();
}
bool TemplateExists(const std::string& templateName)

View File

@ -1,356 +0,0 @@
/* Copyright (C) 2020 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* mouse cursors (either via OpenGL texture or hardware)
*/
#include "precompiled.h"
#include "cursor.h"
#include <cstdio>
#include <cstring>
#include <sstream>
#include "lib/external_libraries/libsdl.h"
#include "lib/ogl.h"
#include "lib/res/h_mgr.h"
#include "ogl_tex.h"
class SDLCursor
{
SDL_Surface* surface;
SDL_Cursor* cursor;
public:
Status create(const PIVFS& vfs, const VfsPath& pathname, int hotspotx_, int hotspoty_, double scale)
{
std::shared_ptr<u8> file; size_t fileSize;
RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize));
Tex t;
RETURN_STATUS_IF_ERR(t.decode(file, fileSize));
// convert to required BGRA format.
const size_t flags = (t.m_Flags | TEX_BGR) & ~TEX_DXT;
RETURN_STATUS_IF_ERR(t.transform_to(flags));
void* bgra_img = t.get_data();
if(!bgra_img)
WARN_RETURN(ERR::FAIL);
surface = SDL_CreateRGBSurfaceFrom(bgra_img, (int)t.m_Width, (int)t.m_Height, 32, (int)t.m_Width*4, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
if(!surface)
return ERR::FAIL;
if(scale != 1.0)
{
SDL_Surface* scaled_surface = SDL_CreateRGBSurface(0, surface->w * scale, surface->h * scale, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
if(!scaled_surface)
return ERR::FAIL;
if(SDL_BlitScaled(surface, NULL, scaled_surface, NULL))
return ERR::FAIL;
SDL_FreeSurface(surface);
surface = scaled_surface;
}
cursor = SDL_CreateColorCursor(surface, hotspotx_, hotspoty_);
if(!cursor)
return ERR::FAIL;
return INFO::OK;
}
void set()
{
SDL_SetCursor(cursor);
}
void destroy()
{
SDL_FreeCursor(cursor);
SDL_FreeSurface(surface);
}
};
// no init is necessary because this is stored in struct Cursor, which
// is 0-initialized by h_mgr.
class GLCursor
{
Handle ht;
GLint w, h;
int hotspotx, hotspoty;
public:
Status create(const PIVFS& vfs, const VfsPath& pathname, int hotspotx_, int hotspoty_, double scale)
{
ht = ogl_tex_load(vfs, pathname);
RETURN_STATUS_IF_ERR(ht);
size_t width, height;
(void)ogl_tex_get_size(ht, &width, &height, 0);
w = (GLint)(width * scale);
h = (GLint)(height * scale);
hotspotx = hotspotx_; hotspoty = hotspoty_;
(void)ogl_tex_set_filter(ht, GL_NEAREST);
(void)ogl_tex_upload(ht);
return INFO::OK;
}
void destroy()
{
// note: we're stored in a resource => ht is initially 0 =>
// this is safe, no need for an is_valid flag
(void)ogl_tex_free(ht);
}
void draw(int x, int y) const
{
#if CONFIG2_GLES
UNUSED2(x); UNUSED2(y);
#warning TODO: implement cursors for GLES
#else
(void)ogl_tex_bind(ht);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2i(1, 0); glVertex2i( x-hotspotx+w, y+hotspoty );
glTexCoord2i(0, 0); glVertex2i( x-hotspotx, y+hotspoty );
glTexCoord2i(0, 1); glVertex2i( x-hotspotx, y+hotspoty-h );
glTexCoord2i(1, 1); glVertex2i( x-hotspotx+w, y+hotspoty-h );
glEnd();
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
#endif
}
Status validate() const
{
const GLint A = 128; // no cursor is expected to get this big
if(w > A || h > A || hotspotx > A || hotspoty > A)
WARN_RETURN(ERR::_1);
if(ht < 0)
WARN_RETURN(ERR::_2);
return INFO::OK;
}
};
enum CursorKind
{
CK_Default,
CK_SDL,
CK_OpenGL
};
struct Cursor
{
double scale;
// require kind == CK_OpenGL after reload
bool forceGL;
CursorKind kind;
// valid iff kind == CK_SDL
SDLCursor sdl_cursor;
// valid iff kind == CK_OpenGL
GLCursor gl_cursor;
};
H_TYPE_DEFINE(Cursor);
static void Cursor_init(Cursor* c, va_list args)
{
c->scale = va_arg(args, double);
c->forceGL = (va_arg(args, int) != 0);
}
static void Cursor_dtor(Cursor* c)
{
switch(c->kind)
{
case CK_Default:
break; // nothing to do
case CK_SDL:
c->sdl_cursor.destroy();
break;
case CK_OpenGL:
c->gl_cursor.destroy();
break;
default:
DEBUG_WARN_ERR(ERR::LOGIC);
break;
}
}
static Status Cursor_reload(Cursor* c, const PIVFS& vfs, const VfsPath& name, Handle)
{
const VfsPath pathname(VfsPath(L"art/textures/cursors") / name);
// read pixel offset of the cursor's hotspot [the bit of it that's
// drawn at (g_mouse_x,g_mouse_y)] from file.
int hotspotx = 0, hotspoty = 0;
{
const VfsPath pathnameHotspot = pathname.ChangeExtension(L".txt");
std::shared_ptr<u8> buf; size_t size;
RETURN_STATUS_IF_ERR(vfs->LoadFile(pathnameHotspot, buf, size));
std::wstringstream s(std::wstring((const wchar_t*)buf.get(), size));
s >> hotspotx >> hotspoty;
}
const VfsPath pathnameImage = pathname.ChangeExtension(L".png");
// try loading as SDL2 cursor
if(!c->forceGL && c->sdl_cursor.create(vfs, pathnameImage, hotspotx, hotspoty, c->scale) == INFO::OK)
c->kind = CK_SDL;
// fall back to GLCursor (system cursor code is disabled or failed)
else if(c->gl_cursor.create(vfs, pathnameImage, hotspotx, hotspoty, c->scale) == INFO::OK)
c->kind = CK_OpenGL;
// everything failed, leave cursor unchanged
else
c->kind = CK_Default;
return INFO::OK;
}
static Status Cursor_validate(const Cursor* c)
{
switch(c->kind)
{
case CK_Default:
break; // nothing to do
case CK_SDL:
break; // nothing to do
case CK_OpenGL:
RETURN_STATUS_IF_ERR(c->gl_cursor.validate());
break;
default:
WARN_RETURN(ERR::_2);
break;
}
return INFO::OK;
}
static Status Cursor_to_string(const Cursor* c, wchar_t* buf)
{
const wchar_t* type;
switch(c->kind)
{
case CK_Default:
type = L"default";
break;
case CK_SDL:
type = L"sdl";
break;
case CK_OpenGL:
type = L"gl";
break;
default:
DEBUG_WARN_ERR(ERR::LOGIC);
type = L"?";
break;
}
swprintf_s(buf, H_STRING_LEN, L"cursor (%ls)", type);
return INFO::OK;
}
// note: these standard resource interface functions are not exposed to the
// caller. all we need here is storage for the SDL_Cursor / GLCursor and
// a name -> data lookup mechanism, both provided by h_mgr.
// in other words, we continually create/free the cursor resource in
// cursor_draw and trust h_mgr's caching to absorb it.
static Handle cursor_load(const PIVFS& vfs, const VfsPath& name, double scale, bool forceGL)
{
return h_alloc(H_Cursor, vfs, name, 0, scale, (int)forceGL);
}
void cursor_shutdown()
{
h_mgr_free_type(H_Cursor);
}
static Status cursor_free(Handle& h)
{
return h_free(h, H_Cursor);
}
Status cursor_draw(const PIVFS& vfs, const wchar_t* name, int x, int y, double scale, bool forceGL)
{
// hide the cursor
if(!name)
{
SDL_ShowCursor(SDL_DISABLE);
return INFO::OK;
}
Handle hc = cursor_load(vfs, name, scale, forceGL);
// TODO: if forceGL changes at runtime after a cursor is first created,
// we might reuse a cached version of the cursor with the old forceGL flag
RETURN_STATUS_IF_ERR(hc); // silently ignore failures
H_DEREF(hc, Cursor, c);
switch(c->kind)
{
case CK_Default:
break;
case CK_SDL:
c->sdl_cursor.set();
SDL_ShowCursor(SDL_ENABLE);
break;
case CK_OpenGL:
c->gl_cursor.draw(x, y);
SDL_ShowCursor(SDL_DISABLE);
break;
default:
DEBUG_WARN_ERR(ERR::LOGIC);
break;
}
(void)cursor_free(hc);
return INFO::OK;
}

View File

@ -1,57 +0,0 @@
/* Copyright (C) 2017 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* mouse cursors (either via OpenGL texture or hardware)
*/
#ifndef INCLUDED_GRAPHICS_CURSOR
#define INCLUDED_GRAPHICS_CURSOR
#include "lib/file/vfs/vfs.h"
/**
* Draw the cursor on-screen.
*
* @param vfs
* @param name Base name of cursor or zero to hide the cursor.
* @param x,y Coordinates [pixels] (origin at lower left)
* (the origin is convenient for drawing via OpenGL, but requires the
* mouse Y coordinate to be subtracted from the client area height.
* Making the caller responsible for this avoids a dependency on
* the g_yres global variable.)
* @param scale Scale factor for drawing size the cursor.
* @param forceGL Require the OpenGL cursor implementation, not hardware cursor
*
* Uses a hardware mouse cursor where available, otherwise a
* portable OpenGL implementation.
**/
extern Status cursor_draw(const PIVFS& vfs, const wchar_t* name, int x, int y, double scale, bool forceGL);
/**
* Forcibly frees all cursor handles.
*
* Currently used just prior to SDL shutdown.
*/
void cursor_shutdown();
#endif // #ifndef INCLUDED_GRAPHICS_CURSOR

View File

@ -159,12 +159,6 @@ static InReaction MainInputHandler(const SDL_Event_* ev)
case SDL_WINDOWEVENT:
switch(ev->ev.window.event)
{
case SDL_WINDOWEVENT_ENTER:
RenderCursor(true);
break;
case SDL_WINDOWEVENT_LEAVE:
RenderCursor(false);
break;
case SDL_WINDOWEVENT_RESIZED:
g_ResizedW = ev->ev.window.data1;
g_ResizedH = ev->ev.window.data2;

View File

@ -26,11 +26,6 @@
#include "ps/GameSetup/CmdLineArgs.h"
// (these variables are documented in the header.)
const wchar_t g_DefaultCursor[] = L"default-arrow";
CStrW g_CursorName = g_DefaultCursor;
bool g_PauseOnFocusLoss = false;
int g_xres, g_yres;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2020 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -29,9 +29,6 @@ extern float g_GuiScale;
extern bool g_Quickstart;
extern bool g_DisableAudio;
extern CStrW g_CursorName;
extern const wchar_t g_DefaultCursor[];
class CmdLineArgs;
extern void CONFIG_Init(const CmdLineArgs& args);

View File

@ -25,7 +25,6 @@
#include "lib/external_libraries/libsdl.h"
#include "lib/file/common/file_stats.h"
#include "lib/res/h_mgr.h"
#include "lib/res/graphics/cursor.h"
#include "graphics/CinemaManager.h"
#include "graphics/Color.h"
@ -113,7 +112,6 @@ ERROR_TYPE(System, RequiredExtensionsMissing);
bool g_DoRenderGui = true;
bool g_DoRenderLogger = true;
bool g_DoRenderCursor = true;
thread_local std::shared_ptr<ScriptContext> g_ScriptContext;
@ -284,54 +282,6 @@ void Render()
g_ProfileViewer.RenderProfile();
ogl_WarnIfError();
// Draw the cursor (or set the Windows cursor, on Windows)
if (g_DoRenderCursor)
{
PROFILE3_GPU("cursor");
CStrW cursorName = g_CursorName;
if (cursorName.empty())
{
cursor_draw(g_VFS, NULL, g_mouse_x, g_yres-g_mouse_y, g_GuiScale, false);
}
else
{
bool forceGL = false;
CFG_GET_VAL("nohwcursor", forceGL);
#if CONFIG2_GLES
#warning TODO: implement cursors for GLES
#else
// set up transform for GL cursor
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
CMatrix3D transform;
transform.SetOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);
glLoadMatrixf(&transform._11);
#endif
#if OS_ANDROID
#warning TODO: cursors for Android
#else
if (cursor_draw(g_VFS, cursorName.c_str(), g_mouse_x, g_yres-g_mouse_y, g_GuiScale, forceGL) < 0)
LOGWARNING("Failed to draw cursor '%s'", utf8_from_wstring(cursorName));
#endif
#if CONFIG2_GLES
#warning TODO: implement cursors for GLES
#else
// restore transform
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
#endif
}
}
glEnable(GL_DEPTH_TEST);
g_Renderer.EndFrame();
@ -539,9 +489,6 @@ static void ShutdownPs()
SAFE_DELETE(g_GUI);
UnloadHotkeys();
// disable the special Windows cursor, or free textures for OGL cursors
cursor_draw(g_VFS, 0, g_mouse_x, g_yres-g_mouse_y, 1.0, false);
}
@ -661,9 +608,6 @@ void Shutdown(int flags)
g_Profiler2.ShutdownGPU();
// Free cursors before shutting down SDL, as they may depend on SDL.
cursor_shutdown();
TIMER_BEGIN(L"shutdown SDL");
ShutdownSDL();
TIMER_END(L"shutdown SDL");
@ -1087,11 +1031,6 @@ void RenderLogger(bool RenderingState)
g_DoRenderLogger = RenderingState;
}
void RenderCursor(bool RenderingState)
{
g_DoRenderCursor = RenderingState;
}
/**
* Temporarily loads a scenario map and retrieves the "ScriptSettings" JSON
* data from it.

View File

@ -74,12 +74,6 @@ enum ShutdownFlags
extern void RenderGui(bool RenderingState);
extern void RenderLogger(bool RenderingState);
/**
* enable/disable rendering of the cursor - this does not hide cursor, but reverts to OS style
*/
extern void RenderCursor(bool RenderingState);
class CmdLineArgs;
class Paths;
extern const std::vector<CStr>& GetMods(const CmdLineArgs& args, int flags);

View File

@ -19,34 +19,32 @@
#include "ps/Util.h"
#include "lib/posix/posix_utsname.h"
#include "lib/ogl.h"
#include "lib/timer.h"
#include "lib/bits.h" // round_up
#include "graphics/GameView.h"
#include "i18n/L10n.h"
#include "lib/allocators/shared_ptr.h"
#include "lib/sysdep/sysdep.h" // sys_OpenFile
#include "lib/sysdep/gfx.h"
#include "lib/sysdep/cpu.h"
#include "lib/sysdep/os_cpu.h"
#include "lib/bits.h" // round_up
#include "lib/ogl.h"
#include "lib/posix/posix_utsname.h"
#if ARCH_X86_X64
#include "lib/sysdep/arch/x86_x64/topology.h"
#endif
#include "lib/sysdep/gfx.h"
#include "lib/sysdep/cpu.h"
#include "lib/sysdep/os_cpu.h"
#include "lib/sysdep/smbios.h"
#include "lib/sysdep/sysdep.h" // sys_OpenFile
#include "lib/tex/tex.h"
#include "i18n/L10n.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/Game.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/Game.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/Pyrogenesis.h"
#include "ps/VideoMode.h"
#include "renderer/Renderer.h"
#include "maths/MathUtil.h"
#include "graphics/GameView.h"
#if CONFIG2_AUDIO
#include "soundmanager/SoundManager.h"
@ -55,8 +53,6 @@
#include <iomanip>
#include <sstream>
extern CStrW g_CursorName;
static std::string SplitExts(const char *exts)
{
std::string str = exts;
@ -376,10 +372,6 @@ void WriteBigScreenshot(const VfsPath& extension, int tiles, int tileWidth, int
glReadBuffer(GL_FRONT);
#endif
// Hide the cursor
CStrW oldCursor = g_CursorName;
g_CursorName = L"";
// Render each tile
CMatrix3D projection;
projection.SetIdentity();
@ -412,9 +404,6 @@ void WriteBigScreenshot(const VfsPath& extension, int tiles, int tileWidth, int
}
}
// Restore the old cursor
g_CursorName = oldCursor;
#if !CONFIG2_GLES
// Restore the buffer settings
glDrawBuffer(oldDrawBuffer);

View File

@ -45,6 +45,8 @@ int DEFAULT_WINDOW_H = 768;
int DEFAULT_FULLSCREEN_W = 1024;
int DEFAULT_FULLSCREEN_H = 768;
const wchar_t DEFAULT_CURSOR_NAME[] = L"default-arrow";
} // anonymous namespace
#if OS_WIN
@ -59,11 +61,162 @@ extern void wutil_EnableHiDPIOnWindows();
CVideoMode g_VideoMode;
class CVideoMode::CCursor
{
public:
enum class CursorBackend
{
SDL,
SYSTEM
};
CCursor();
~CCursor();
void SetCursor(const CStrW& name);
void ResetCursor();
private:
CursorBackend m_CursorBackend = CursorBackend::SYSTEM;
SDL_Surface* m_CursorSurface = nullptr;
SDL_Cursor* m_Cursor = nullptr;
CStrW m_CursorName;
};
CVideoMode::CCursor::CCursor()
{
std::string cursorBackend;
CFG_GET_VAL("cursorbackend", cursorBackend);
if (cursorBackend == "sdl")
m_CursorBackend = CursorBackend::SDL;
else
m_CursorBackend = CursorBackend::SYSTEM;
ResetCursor();
}
CVideoMode::CCursor::~CCursor()
{
if (m_Cursor)
SDL_FreeCursor(m_Cursor);
if (m_CursorSurface)
SDL_FreeSurface(m_CursorSurface);
}
void CVideoMode::CCursor::SetCursor(const CStrW& name)
{
if (m_CursorBackend == CursorBackend::SYSTEM || m_CursorName == name)
return;
m_CursorName = name;
if (m_Cursor)
SDL_FreeCursor(m_Cursor);
if (m_CursorSurface)
SDL_FreeSurface(m_CursorSurface);
if (name.empty())
{
SDL_ShowCursor(SDL_DISABLE);
return;
}
const VfsPath pathBaseName(VfsPath(L"art/textures/cursors") / name);
// Read pixel offset of the cursor's hotspot [the bit of it that's
// drawn at (g_mouse_x,g_mouse_y)] from file.
int hotspotX = 0, hotspotY = 0;
{
const VfsPath pathHotspotName = pathBaseName.ChangeExtension(L".txt");
std::shared_ptr<u8> buffer;
size_t size;
if (g_VFS->LoadFile(pathHotspotName, buffer, size) != INFO::OK)
{
LOGERROR("Can't load hotspot for cursor: %s", pathHotspotName.string8().c_str());
return;
}
std::wstringstream s(std::wstring(reinterpret_cast<const wchar_t*>(buffer.get()), size));
s >> hotspotX >> hotspotY;
}
const VfsPath pathImageName = pathBaseName.ChangeExtension(L".png");
std::shared_ptr<u8> file;
size_t fileSize;
if (g_VFS->LoadFile(pathImageName, file, fileSize) != INFO::OK)
{
LOGERROR("Can't load image for cursor: %s", pathImageName.string8().c_str());
return;
}
Tex t;
if (t.decode(file, fileSize) != INFO::OK)
{
LOGERROR("Can't decode image for cursor");
return;
}
// Convert to required BGRA format.
const size_t flags = (t.m_Flags | TEX_BGR) & ~TEX_DXT;
if (t.transform_to(flags) != INFO::OK)
{
LOGERROR("Can't transform image for cursor");
return;
}
void* imageBGRA = t.get_data();
if (!imageBGRA)
{
LOGERROR("Transformed image is empty for cursor");
return;
}
m_CursorSurface = SDL_CreateRGBSurfaceFrom(imageBGRA,
static_cast<int>(t.m_Width), static_cast<int>(t.m_Height), 32,
static_cast<int>(t.m_Width * 4),
0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
if (!m_CursorSurface)
{
LOGERROR("Can't create surface for cursor: %s", SDL_GetError());
return;
}
const float scale = g_GuiScale;
if (scale != 1.0)
{
SDL_Surface* scaledSurface = SDL_CreateRGBSurface(0,
m_CursorSurface->w * scale,
m_CursorSurface->h * scale, 32,
0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
if (!scaledSurface)
{
LOGERROR("Can't create scaled surface forcursor: %s", SDL_GetError());
return;
}
if (SDL_BlitScaled(m_CursorSurface, nullptr, scaledSurface, nullptr))
return;
SDL_FreeSurface(m_CursorSurface);
m_CursorSurface = scaledSurface;
}
m_Cursor = SDL_CreateColorCursor(m_CursorSurface, hotspotX, hotspotY);
if (!m_Cursor)
{
LOGERROR("Can't create cursor: %s", SDL_GetError());
return;
}
SDL_SetCursor(m_Cursor);
}
void CVideoMode::CCursor::ResetCursor()
{
SetCursor(DEFAULT_CURSOR_NAME);
}
CVideoMode::CVideoMode() :
m_WindowedW(DEFAULT_WINDOW_W), m_WindowedH(DEFAULT_WINDOW_H), m_WindowedX(0), m_WindowedY(0)
{
}
CVideoMode::~CVideoMode() = default;
void CVideoMode::ReadConfig()
{
bool windowed = !m_ConfigFullscreen;
@ -307,6 +460,8 @@ bool CVideoMode::InitSDL()
SetWindowIcon();
m_Cursor = std::make_unique<CCursor>();
return true;
}
@ -325,6 +480,8 @@ void CVideoMode::Shutdown()
{
ENSURE(m_IsInitialised);
m_Cursor.reset();
m_IsFullscreen = false;
m_IsInitialised = false;
if (m_Window)
@ -563,3 +720,15 @@ void CVideoMode::SetWindowIcon()
SDL_SetWindowIcon(m_Window, iconSurface);
SDL_FreeSurface(iconSurface);
}
void CVideoMode::SetCursor(const CStrW& name)
{
if (m_Cursor)
m_Cursor->SetCursor(name);
}
void CVideoMode::ResetCursor()
{
if (m_Cursor)
m_Cursor->ResetCursor();
}

View File

@ -18,12 +18,17 @@
#ifndef INCLUDED_VIDEOMODE
#define INCLUDED_VIDEOMODE
#include "ps/CStrForward.h"
#include <memory>
typedef struct SDL_Window SDL_Window;
class CVideoMode
{
public:
CVideoMode();
~CVideoMode();
/**
* Initialise the video mode, for use in an SDL-using application.
@ -88,6 +93,9 @@ public:
void SetWindowIcon();
void SetCursor(const CStrW& name);
void ResetCursor();
private:
void ReadConfig();
int GetBestBPP();
@ -135,6 +143,9 @@ private:
int m_CurrentW;
int m_CurrentH;
int m_CurrentBPP;
class CCursor;
std::unique_ptr<CCursor> m_Cursor;
};
extern CVideoMode g_VideoMode;

View File

@ -126,10 +126,6 @@ bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll)
RegisterHandlers();
// Disable the game's cursor rendering
extern CStrW g_CursorName;
g_CursorName = L"";
state.args = args;
state.running = true;
state.view = AtlasView::GetView_None();