LCOV - code coverage report
Current view: top level - source/lib/res/graphics - cursor.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 132 0.0 %
Date: 2021-07-26 19:54:35 Functions: 0 13 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2020 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : /*
      24             :  * mouse cursors (either via OpenGL texture or hardware)
      25             :  */
      26             : 
      27             : #include "precompiled.h"
      28             : #include "cursor.h"
      29             : 
      30             : #include <cstdio>
      31             : #include <cstring>
      32             : #include <sstream>
      33             : 
      34             : #include "lib/external_libraries/libsdl.h"
      35             : #include "lib/ogl.h"
      36             : #include "lib/res/h_mgr.h"
      37             : #include "ogl_tex.h"
      38             : 
      39             : class SDLCursor
      40             : {
      41             :     SDL_Surface* surface;
      42             :     SDL_Cursor* cursor;
      43             : 
      44             : public:
      45           0 :     Status create(const PIVFS& vfs, const VfsPath& pathname, int hotspotx_, int hotspoty_, double scale)
      46             :     {
      47           0 :         std::shared_ptr<u8> file; size_t fileSize;
      48           0 :         RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize));
      49             : 
      50           0 :         Tex t;
      51           0 :         RETURN_STATUS_IF_ERR(t.decode(file, fileSize));
      52             : 
      53             :         // convert to required BGRA format.
      54           0 :         const size_t flags = (t.m_Flags | TEX_BGR) & ~TEX_DXT;
      55           0 :         RETURN_STATUS_IF_ERR(t.transform_to(flags));
      56           0 :         void* bgra_img = t.get_data();
      57           0 :         if(!bgra_img)
      58           0 :             WARN_RETURN(ERR::FAIL);
      59             : 
      60           0 :         surface = SDL_CreateRGBSurfaceFrom(bgra_img, (int)t.m_Width, (int)t.m_Height, 32, (int)t.m_Width*4, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
      61           0 :         if(!surface)
      62             :             return ERR::FAIL;
      63           0 :         if(scale != 1.0)
      64             :         {
      65           0 :             SDL_Surface* scaled_surface = SDL_CreateRGBSurface(0, surface->w * scale, surface->h * scale, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
      66           0 :             if(!scaled_surface)
      67             :                 return ERR::FAIL;
      68           0 :             if(SDL_BlitScaled(surface, NULL, scaled_surface, NULL))
      69             :                 return ERR::FAIL;
      70           0 :             SDL_FreeSurface(surface);
      71           0 :             surface = scaled_surface;
      72             :         }
      73           0 :         cursor = SDL_CreateColorCursor(surface, hotspotx_, hotspoty_);
      74           0 :         if(!cursor)
      75           0 :             return ERR::FAIL;
      76             : 
      77             :         return INFO::OK;
      78             :     }
      79             : 
      80           0 :     void set()
      81             :     {
      82           0 :         SDL_SetCursor(cursor);
      83           0 :     }
      84             : 
      85           0 :     void destroy()
      86             :     {
      87           0 :         SDL_FreeCursor(cursor);
      88           0 :         SDL_FreeSurface(surface);
      89           0 :     }
      90             : };
      91             : 
      92             : // no init is necessary because this is stored in struct Cursor, which
      93             : // is 0-initialized by h_mgr.
      94             : class GLCursor
      95             : {
      96             :     Handle ht;
      97             : 
      98             :     GLint w, h;
      99             :     int hotspotx, hotspoty;
     100             : 
     101             : public:
     102           0 :     Status create(const PIVFS& vfs, const VfsPath& pathname, int hotspotx_, int hotspoty_, double scale)
     103             :     {
     104           0 :         ht = ogl_tex_load(vfs, pathname);
     105           0 :         RETURN_STATUS_IF_ERR(ht);
     106             : 
     107           0 :         size_t width, height;
     108           0 :         (void)ogl_tex_get_size(ht, &width, &height, 0);
     109           0 :         w = (GLint)(width * scale);
     110           0 :         h = (GLint)(height * scale);
     111             : 
     112           0 :         hotspotx = hotspotx_; hotspoty = hotspoty_;
     113             : 
     114           0 :         (void)ogl_tex_set_filter(ht, GL_NEAREST);
     115           0 :         (void)ogl_tex_upload(ht);
     116             :         return INFO::OK;
     117             :     }
     118             : 
     119             :     void destroy()
     120             :     {
     121             :         // note: we're stored in a resource => ht is initially 0 =>
     122             :         // this is safe, no need for an is_valid flag
     123           0 :         (void)ogl_tex_free(ht);
     124             :     }
     125             : 
     126           0 :     void draw(int x, int y) const
     127             :     {
     128             : #if CONFIG2_GLES
     129             :         UNUSED2(x); UNUSED2(y);
     130             : #warning TODO: implement cursors for GLES
     131             : #else
     132           0 :         (void)ogl_tex_bind(ht);
     133           0 :         glEnable(GL_TEXTURE_2D);
     134           0 :         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
     135           0 :         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     136           0 :         glEnable(GL_BLEND);
     137           0 :         glDisable(GL_DEPTH_TEST);
     138           0 :         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
     139             : 
     140           0 :         glBegin(GL_QUADS);
     141           0 :         glTexCoord2i(1, 0); glVertex2i( x-hotspotx+w, y+hotspoty   );
     142           0 :         glTexCoord2i(0, 0); glVertex2i( x-hotspotx,   y+hotspoty   );
     143           0 :         glTexCoord2i(0, 1); glVertex2i( x-hotspotx,   y+hotspoty-h );
     144           0 :         glTexCoord2i(1, 1); glVertex2i( x-hotspotx+w, y+hotspoty-h );
     145           0 :         glEnd();
     146             : 
     147           0 :         glDisable(GL_BLEND);
     148           0 :         glEnable(GL_DEPTH_TEST);
     149             : #endif
     150           0 :     }
     151             : 
     152           0 :     Status validate() const
     153             :     {
     154           0 :         const GLint A = 128;    // no cursor is expected to get this big
     155           0 :         if(w > A || h > A || hotspotx > A || hotspoty > A)
     156           0 :             WARN_RETURN(ERR::_1);
     157           0 :         if(ht < 0)
     158           0 :             WARN_RETURN(ERR::_2);
     159             :         return INFO::OK;
     160             :     }
     161             : };
     162             : 
     163             : enum CursorKind
     164             : {
     165             :     CK_Default,
     166             :     CK_SDL,
     167             :     CK_OpenGL
     168             : };
     169             : 
     170             : struct Cursor
     171             : {
     172             :     double scale;
     173             : 
     174             :     // require kind == CK_OpenGL after reload
     175             :     bool forceGL;
     176             : 
     177             :     CursorKind kind;
     178             : 
     179             :     // valid iff kind == CK_SDL
     180             :     SDLCursor sdl_cursor;
     181             : 
     182             :     // valid iff kind == CK_OpenGL
     183             :     GLCursor gl_cursor;
     184             : };
     185             : 
     186             : H_TYPE_DEFINE(Cursor);
     187             : 
     188           0 : static void Cursor_init(Cursor* c, va_list args)
     189             : {
     190           0 :     c->scale = va_arg(args, double);
     191           0 :     c->forceGL = (va_arg(args, int) != 0);
     192           0 : }
     193             : 
     194           0 : static void Cursor_dtor(Cursor* c)
     195             : {
     196           0 :     switch(c->kind)
     197             :     {
     198             :     case CK_Default:
     199             :         break;  // nothing to do
     200             : 
     201           0 :     case CK_SDL:
     202           0 :         c->sdl_cursor.destroy();
     203             :         break;
     204             : 
     205           0 :     case CK_OpenGL:
     206           0 :         c->gl_cursor.destroy();
     207             :         break;
     208             : 
     209           0 :     default:
     210           0 :         DEBUG_WARN_ERR(ERR::LOGIC);
     211             :         break;
     212             :     }
     213           0 : }
     214             : 
     215           0 : static Status Cursor_reload(Cursor* c, const PIVFS& vfs, const VfsPath& name, Handle)
     216             : {
     217           0 :     const VfsPath pathname(VfsPath(L"art/textures/cursors") / name);
     218             : 
     219             :     // read pixel offset of the cursor's hotspot [the bit of it that's
     220             :     // drawn at (g_mouse_x,g_mouse_y)] from file.
     221           0 :     int hotspotx = 0, hotspoty = 0;
     222           0 :     {
     223           0 :         const VfsPath pathnameHotspot = pathname.ChangeExtension(L".txt");
     224           0 :         std::shared_ptr<u8> buf; size_t size;
     225           0 :         RETURN_STATUS_IF_ERR(vfs->LoadFile(pathnameHotspot, buf, size));
     226           0 :         std::wstringstream s(std::wstring((const wchar_t*)buf.get(), size));
     227           0 :         s >> hotspotx >> hotspoty;
     228             :     }
     229             : 
     230           0 :     const VfsPath pathnameImage = pathname.ChangeExtension(L".png");
     231             : 
     232             :     // try loading as SDL2 cursor
     233           0 :     if(!c->forceGL && c->sdl_cursor.create(vfs, pathnameImage, hotspotx, hotspoty, c->scale) == INFO::OK)
     234           0 :         c->kind = CK_SDL;
     235             :     // fall back to GLCursor (system cursor code is disabled or failed)
     236           0 :     else if(c->gl_cursor.create(vfs, pathnameImage, hotspotx, hotspoty, c->scale) == INFO::OK)
     237           0 :         c->kind = CK_OpenGL;
     238             :     // everything failed, leave cursor unchanged
     239             :     else
     240           0 :         c->kind = CK_Default;
     241             : 
     242           0 :     return INFO::OK;
     243             : }
     244             : 
     245           0 : static Status Cursor_validate(const Cursor* c)
     246             : {
     247           0 :     switch(c->kind)
     248             :     {
     249             :     case CK_Default:
     250             :         break;  // nothing to do
     251             : 
     252             :     case CK_SDL:
     253             :         break;  // nothing to do
     254             : 
     255           0 :     case CK_OpenGL:
     256           0 :         RETURN_STATUS_IF_ERR(c->gl_cursor.validate());
     257             :         break;
     258             : 
     259           0 :     default:
     260           0 :         WARN_RETURN(ERR::_2);
     261             :         break;
     262             :     }
     263             : 
     264             :     return INFO::OK;
     265             : }
     266             : 
     267           0 : static Status Cursor_to_string(const Cursor* c, wchar_t* buf)
     268             : {
     269           0 :     const wchar_t* type;
     270           0 :     switch(c->kind)
     271             :     {
     272             :     case CK_Default:
     273             :         type = L"default";
     274             :         break;
     275             : 
     276           0 :     case CK_SDL:
     277           0 :         type = L"sdl";
     278           0 :         break;
     279             : 
     280           0 :     case CK_OpenGL:
     281           0 :         type = L"gl";
     282           0 :         break;
     283             : 
     284           0 :     default:
     285           0 :         DEBUG_WARN_ERR(ERR::LOGIC);
     286             :         type = L"?";
     287             :         break;
     288             :     }
     289             : 
     290           0 :     swprintf_s(buf, H_STRING_LEN, L"cursor (%ls)", type);
     291           0 :     return INFO::OK;
     292             : }
     293             : 
     294             : 
     295             : // note: these standard resource interface functions are not exposed to the
     296             : // caller. all we need here is storage for the SDL_Cursor / GLCursor and
     297             : // a name -> data lookup mechanism, both provided by h_mgr.
     298             : // in other words, we continually create/free the cursor resource in
     299             : // cursor_draw and trust h_mgr's caching to absorb it.
     300             : 
     301             : static Handle cursor_load(const PIVFS& vfs, const VfsPath& name, double scale, bool forceGL)
     302             : {
     303           0 :     return h_alloc(H_Cursor, vfs, name, 0, scale, (int)forceGL);
     304             : }
     305             : 
     306           0 : void cursor_shutdown()
     307             : {
     308           0 :     h_mgr_free_type(H_Cursor);
     309           0 : }
     310             : 
     311             : static Status cursor_free(Handle& h)
     312             : {
     313           0 :     return h_free(h, H_Cursor);
     314             : }
     315             : 
     316             : 
     317           0 : Status cursor_draw(const PIVFS& vfs, const wchar_t* name, int x, int y, double scale, bool forceGL)
     318             : {
     319             :     // hide the cursor
     320           0 :     if(!name)
     321             :     {
     322           0 :         SDL_ShowCursor(SDL_DISABLE);
     323           0 :         return INFO::OK;
     324             :     }
     325             : 
     326           0 :     Handle hc = cursor_load(vfs, name, scale, forceGL);
     327             :     // TODO: if forceGL changes at runtime after a cursor is first created,
     328             :     // we might reuse a cached version of the cursor with the old forceGL flag
     329             : 
     330           0 :     RETURN_STATUS_IF_ERR(hc); // silently ignore failures
     331             : 
     332           0 :     H_DEREF(hc, Cursor, c);
     333             : 
     334           0 :     switch(c->kind)
     335             :     {
     336             :     case CK_Default:
     337             :         break;
     338             : 
     339           0 :     case CK_SDL:
     340           0 :         c->sdl_cursor.set();
     341           0 :         SDL_ShowCursor(SDL_ENABLE);
     342             :         break;
     343             : 
     344           0 :     case CK_OpenGL:
     345           0 :         c->gl_cursor.draw(x, y);
     346           0 :         SDL_ShowCursor(SDL_DISABLE);
     347             :         break;
     348             : 
     349           0 :     default:
     350           0 :         DEBUG_WARN_ERR(ERR::LOGIC);
     351             :         break;
     352             :     }
     353             : 
     354           0 :     (void)cursor_free(hc);
     355             :     return INFO::OK;
     356             : }

Generated by: LCOV version 1.13