diff --git a/source/lib/res/cursor.cpp b/source/lib/res/cursor.cpp index cd251f7962..12abd8a621 100755 --- a/source/lib/res/cursor.cpp +++ b/source/lib/res/cursor.cpp @@ -3,30 +3,52 @@ #include #include -#define USE_WINDOWS_CURSOR - -#if OS_WIN -#include "lib/sysdep/win/win_internal.h" -#endif +// On Windows, allow runtime choice between system cursors and OpenGL +// cursors (Windows = more responsive, OpenGL = more consistent with what +// the game sees) +#define USE_WINDOWS_CURSOR 1 +#include "lib/ogl.h" +#include "sysdep/sysdep.h" // sys_cursor_* #include "res.h" #include "ogl_tex.h" -#include "lib/ogl.h" -#include "sysdep/sysdep.h" +#include "cursor.h" -struct ogl_cursor { - Handle tex; - int hotspotx, hotspoty; +// no init is necessary because this is stored in struct Cursor, which +// is 0-initialized by h_mgr. +class GLCursor +{ + Handle ht; int w, h; + int hotspotx, hotspoty; + +public: + void create(Handle ht_, int w_, int h_, int hotspotx_, int hotspoty_) + { + ht = ht_; + w = w_; h = h_; + hotspotx = hotspotx_; hotspoty = hotspoty_; + + WARN_ERR(tex_upload(ht, GL_NEAREST)); + } + + void destroy() + { + WARN_ERR(tex_free(ht)); + } + void draw(int x, int y) { - tex_bind(tex); + WARN_ERR(tex_bind(ht)); glEnable(GL_TEXTURE_2D); 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); + // OpenGL's coordinate system is "upside-down"; correct for that. + y = g_yres - y; + glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i( x-hotspotx, y+hotspoty ); glTexCoord2i(1, 0); glVertex2i( x-hotspotx+w, y+hotspoty ); @@ -36,170 +58,114 @@ struct ogl_cursor { } }; -extern int g_mouse_x, g_mouse_y; -#if OS_WIN -// On Windows, allow runtime choice between Windows cursors and OpenGL -// cursors (Windows = more responsive, OpenGL = more consistent with what -// the game sees) struct Cursor { - union { - HICON wincursor; // Windows handle - ogl_cursor* cursor; // font texture - }; - char type; // 0 for OpenGL cursor, 1 for Windows cursor + void* sys_cursor; + + // valid iff sys_cursor == 0. + GLCursor gl_cursor; }; -#else // #if OS_WIN -struct Cursor -{ - ogl_cursor* cursor; // font texture -}; -#endif // #if OS_WIN / #else H_TYPE_DEFINE(Cursor); -static void Cursor_init(Cursor* c, va_list) +static void Cursor_init(Cursor* UNUSED(c), va_list UNUSED(args)) { -#if OS_WIN -# ifdef USE_WINDOWS_CURSOR - c->type = 1; -# else - c->type = 0; -# endif - c->wincursor = NULL; -#endif - - c->cursor = NULL; } static void Cursor_dtor(Cursor* c) { -#if OS_WIN - if (c->type == 1) - { - if (c->wincursor) - DestroyIcon(c->wincursor); - } + if(c->sys_cursor) + WARN_ERR(sys_cursor_free(c->sys_cursor)); else -#endif - { - if (c->cursor) - { - tex_free(c->cursor->tex); - delete c->cursor; - c->cursor = NULL; - } - } + c->gl_cursor.destroy(); } static int Cursor_reload(Cursor* c, const char* name, Handle) { char filename[VFS_MAX_PATH]; - int ret; - // Load the .txt file containing the pixel offset of - // the cursor's hotspot (the bit of it that's - // drawn at (g_mouse_x,g_mouse_y) ) + // Load the .txt file containing the pixel offset of the cursor's + // hotspot (the bit of it that's drawn at (g_mouse_x,g_mouse_y) ) snprintf(filename, ARRAY_SIZE(filename), "art/textures/cursors/%s.txt", name); - int hotspotx, hotspoty; + int hotspotx = 0, hotspoty = 0; { - void* p; - size_t size; + void* p; size_t size; Handle hm = vfs_load(filename, p, size); - RETURN_ERR(hm); + WARN_ERR(hm); + if(hm > 0) + { + std::stringstream s(std::string((const char*)p, size)); + s >> hotspotx >> hotspoty; - std::stringstream s(std::string((const char*)p, size)); - s >> hotspotx >> hotspoty; - - mem_free_h(hm); + WARN_ERR(mem_free_h(hm)); + } } snprintf(filename, ARRAY_SIZE(filename), "art/textures/cursors/%s.png", name); - Handle ht = tex_load(filename); - CHECK_ERR(ht); + RETURN_ERR(ht); -#if OS_WIN - if (c->type == 1) - { - int w, h, gl_fmt, bpp; - void* img; - if(tex_info(ht, &w, &h, &gl_fmt, &bpp, &img) < 0 || - (bpp != 32 || gl_fmt != GL_RGBA)) - { - debug_warn("Cursor_reload: invalid texture format"); - ret = ERR_TEX_FMT_INVALID; - goto fail; - } + int w = 0, h = 0; + int gl_fmt = 0, bpp = 0; + void* img = 0; + WARN_ERR(tex_info(ht, &w, &h, &gl_fmt, &bpp, &img)); - ret = cursor_create(w, h, img, hotspotx, hotspoty, (void**)&c->wincursor); - if(ret < 0) - { -fail: - tex_free(ht); - return ret; - } - } +#if USE_WINDOWS_CURSOR + // verify texture format (this isn't done in sys_cursor_create to + // avoid needing to pass gl_fmt and bpp; it assumes 32-bit RGBA). + if(bpp != 32 || gl_fmt != GL_RGBA) + debug_warn("Cursor_reload: invalid texture format"); else + WARN_ERR(sys_cursor_create(w, h, img, hotspotx, hotspoty, &c->sys_cursor)); #endif - { - int err = tex_upload(ht, GL_NEAREST); - CHECK_ERR(err); - c->cursor = new ogl_cursor; - - c->cursor->tex = ht; - c->cursor->hotspotx = hotspotx; - c->cursor->hotspoty = hotspoty; - // Get the width/height - tex_info(c->cursor->tex, &c->cursor->w, &c->cursor->h, NULL, NULL, NULL); - } + // if the system cursor code is disabled or failed, fall back to GLCursor. + if(!c->sys_cursor) + c->gl_cursor.create(ht, w, h, hotspotx, hotspoty); return 0; } -Handle cursor_load(const char* name) +// note: these standard resource interface functions are not exposed to the +// caller. all we need here is storage for the sys_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 char* name) { return h_alloc(H_Cursor, name, 0); } -int cursor_free(Handle& h) +static int cursor_free(Handle& h) { return h_free(h, H_Cursor); } -extern int g_yres; // from main.cpp. Required because GL's (0,0) is in the bottom-left -void cursor_draw(const char* name) +// draw the specified cursor at the given pixel coordinates +// (origin is top-left to match the windowing system). +// uses a hardware mouse cursor where available, otherwise a +// portable OpenGL implementation. +int cursor_draw(const char* name, int x, int y) { // Use 'null' to disable the cursor - if (!name) + if(!name) { -#if OS_WIN - SetCursor(LoadCursor(NULL, IDC_ARROW)); -#endif - return; + WARN_ERR(sys_cursor_set(0)); + return 0; } - Handle h = cursor_load(name); - if (h <= 0) - return; + Handle hc = cursor_load(name); + RETURN_ERR(hc); + H_DEREF(hc, Cursor, c); - Cursor* c = (Cursor*) h_user_data(h, H_Cursor); - if (!c) - return; - -#if OS_WIN - if (c->type == 1) - { - SetCursor(c->wincursor); - } + if(c->sys_cursor) + WARN_ERR(sys_cursor_set(c->sys_cursor)); else -#endif - { - c->cursor->draw(g_mouse_x, g_yres - g_mouse_y); - } + c->gl_cursor.draw(x, y); - cursor_free(h); + (void)cursor_free(hc); + return 0; } diff --git a/source/lib/res/cursor.h b/source/lib/res/cursor.h index 002521712a..6814dec6d2 100755 --- a/source/lib/res/cursor.h +++ b/source/lib/res/cursor.h @@ -1 +1,13 @@ -void cursor_draw(const char* name); +#ifndef CURSOR_H__ +#define CURSOR_H__ + +// draw the specified cursor at the given pixel coordinates +// (origin is top-left to match the windowing system). +// uses a hardware mouse cursor where available, otherwise a +// portable OpenGL implementation. +extern int cursor_draw(const char* name, int x, int y); + +// internal use only: +extern int g_yres; + +#endif // #ifndef CURSOR_H__ diff --git a/source/lib/sysdep/sysdep.h b/source/lib/sysdep/sysdep.h index f6a17ffb85..da5763abcb 100755 --- a/source/lib/sysdep/sysdep.h +++ b/source/lib/sysdep/sysdep.h @@ -108,20 +108,22 @@ extern int clipboard_free(wchar_t* copy); // mouse cursor // +// note: these do not warn on error; that is left to the caller. + // creates a cursor from the given 32 bpp RGBA texture. hotspot (hx,hy) is // the offset from its upper-left corner to the position where mouse clicks // are registered. // the cursor must be cursor_free-ed when no longer needed. -extern int cursor_create(int w, int h, void* img, int hx, int hy, +extern int sys_cursor_create(int w, int h, void* img, int hx, int hy, void** cursor); // replaces the current system cursor with the one indicated. need only be // called once per cursor; pass 0 to restore the default. -extern int cursor_set(void* cursor); +extern int sys_cursor_set(void* cursor); // destroys the indicated cursor and frees its resources. if it is // currently the system cursor, the default cursor is restored first. -extern int cursor_free(void* cursor); +extern int sys_cursor_free(void* cursor); extern int get_executable_name(char* n_path, size_t buf_size); diff --git a/source/lib/sysdep/unix/unix.cpp b/source/lib/sysdep/unix/unix.cpp index 820001aa59..c90930118c 100644 --- a/source/lib/sysdep/unix/unix.cpp +++ b/source/lib/sysdep/unix/unix.cpp @@ -79,3 +79,29 @@ ErrorReaction display_error_impl(const wchar_t* text, int flags) } } } + + +// mouse cursor stubs (required by lib/res/cursor.cpp) +// note: do not return ERR_NOT_IMPLEMENTED or similar because that +// would result in WARN_ERRs. +// +// TODO: implementing these would be nice because then the game can +// take advantage of hardware mouse cursors instead of the (jerky when +// loading) OpenGL cursor. + +int sys_cursor_create(int UNUSED(w), int UNUSED(h), void* UNUSED(img), + int UNUSED(hx), UNUSED(int hy), void** cursor) +{ + *cursor = 0; + return 0; +} + +int sys_cursor_set(void* cursor) +{ + return 0; +} + +int sys_cursor_free(void* cursor) +{ + return 0; +} diff --git a/source/lib/sysdep/win/win.cpp b/source/lib/sysdep/win/win.cpp index 159633f82b..5524c96ffb 100755 --- a/source/lib/sysdep/win/win.cpp +++ b/source/lib/sysdep/win/win.cpp @@ -543,7 +543,7 @@ static HCURSOR HCURSOR_from_ptr(void* p) // the offset from its upper-left corner to the position where mouse clicks // are registered. // the cursor must be cursor_free-ed when no longer needed. -int cursor_create(int w, int h, void* img, int hx, int hy, +int sys_cursor_create(int w, int h, void* img, int hx, int hy, void** cursor) { *cursor = 0; @@ -569,7 +569,7 @@ int cursor_create(int w, int h, void* img, int hx, int hy, // CreateBitmap; bpp/format must be checked against those of the DC. // this is the simplest way and we don't care about slight performance // differences because this is typically only called once. - HBITMAP hbmColor = CreateBitmap(w, h, 1, 32, img_bgra); + HBITMAP hbmColour = CreateBitmap(w, h, 1, 32, img_bgra); free(img_bgra); @@ -584,18 +584,15 @@ int cursor_create(int w, int h, void* img, int hx, int hy, ii.xHotspot = hx; ii.yHotspot = hy; ii.hbmMask = hbmMask; - ii.hbmColor = hbmColor; + ii.hbmColor = hbmColour; HICON hIcon = CreateIconIndirect(&ii); // CreateIconIndirect makes copies, so we no longer need these. DeleteObject(hbmMask); - DeleteObject(hbmColor); + DeleteObject(hbmColour); if(!hIcon) // not INVALID_HANDLE_VALUE - { - debug_warn("cursor CreateIconIndirect failed"); return -1; - } *cursor = ptr_from_HICON(hIcon); return 0; @@ -604,7 +601,7 @@ int cursor_create(int w, int h, void* img, int hx, int hy, // replaces the current system cursor with the one indicated. need only be // called once per cursor; pass 0 to restore the default. -int cursor_set(void* cursor) +int sys_cursor_set(void* cursor) { // restore default cursor. if(!cursor) @@ -619,7 +616,7 @@ int cursor_set(void* cursor) // destroys the indicated cursor and frees its resources. if it is // currently the system cursor, the default cursor is restored first. -int cursor_free(void* cursor) +int sys_cursor_free(void* cursor) { // bail now to prevent potential confusion below; there's nothing to do. if(!cursor) @@ -628,7 +625,7 @@ int cursor_free(void* cursor) // if the cursor being freed is active, restore the default arrow // (just for safety). if(ptr_from_HCURSOR(GetCursor()) == cursor) - WARN_ERR(cursor_set(0)); + WARN_ERR(sys_cursor_set(0)); BOOL ok = DestroyIcon(HICON_from_ptr(cursor)); return ok? 0 : -1;