Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
cursor.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2012 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 <cstring>
31 #include <cstdio>
32 #include <sstream>
33 
34 #include "lib/ogl.h"
35 #include "lib/sysdep/cursor.h"
36 #include "ogl_tex.h"
37 #include "lib/res/h_mgr.h"
38 
39 // On Windows, allow runtime choice between system cursors and OpenGL
40 // cursors (Windows = more responsive, OpenGL = more consistent with what
41 // the game sees)
42 #if OS_WIN || OS_UNIX
43 # define ALLOW_SYS_CURSOR 1
44 #else
45 # define ALLOW_SYS_CURSOR 0
46 #endif
47 
48 
49 static Status load_sys_cursor(const PIVFS& vfs, const VfsPath& pathname, int hx, int hy, sys_cursor* cursor)
50 {
51 #if !ALLOW_SYS_CURSOR
52  UNUSED2(vfs);
53  UNUSED2(pathname);
54  UNUSED2(hx);
55  UNUSED2(hy);
56  UNUSED2(cursor);
57 
58  return ERR::FAIL;
59 #else
60  shared_ptr<u8> file; size_t fileSize;
61  RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize));
62 
63  ScopedTex t;
64  RETURN_STATUS_IF_ERR(tex_decode(file, fileSize, &t));
65 
66  // convert to required BGRA format.
67  const size_t flags = (t.flags | TEX_BGR) & ~TEX_DXT;
69  void* bgra_img = tex_get_data(&t);
70  if(!bgra_img)
72 
73  RETURN_STATUS_IF_ERR(sys_cursor_create((int)t.w, (int)t.h, bgra_img, hx, hy, cursor));
74  return INFO::OK;
75 #endif
76 }
77 
78 
79 // no init is necessary because this is stored in struct Cursor, which
80 // is 0-initialized by h_mgr.
81 class GLCursor
82 {
84 
85  GLint w, h;
87 
88 public:
89  Status create(const PIVFS& vfs, const VfsPath& pathname, int hotspotx_, int hotspoty_)
90  {
91  ht = ogl_tex_load(vfs, pathname);
93 
94  size_t width, height;
95  (void)ogl_tex_get_size(ht, &width, &height, 0);
96  w = (GLint)width;
97  h = (GLint)height;
98 
99  hotspotx = hotspotx_; hotspoty = hotspoty_;
100 
101  (void)ogl_tex_set_filter(ht, GL_NEAREST);
102  (void)ogl_tex_upload(ht);
103  return INFO::OK;
104  }
105 
106  void destroy()
107  {
108  // note: we're stored in a resource => ht is initially 0 =>
109  // this is safe, no need for an is_valid flag
110  (void)ogl_tex_free(ht);
111  }
112 
113  void draw(int x, int y) const
114  {
115 #if CONFIG2_GLES
116  UNUSED2(x); UNUSED2(y);
117 #warning TODO: implement cursors for GLES
118 #else
119  (void)ogl_tex_bind(ht);
120  glEnable(GL_TEXTURE_2D);
121  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
122  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
123  glEnable(GL_BLEND);
124  glDisable(GL_DEPTH_TEST);
125  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
126 
127  glBegin(GL_QUADS);
128  glTexCoord2i(1, 0); glVertex2i( x-hotspotx+w, y+hotspoty );
129  glTexCoord2i(0, 0); glVertex2i( x-hotspotx, y+hotspoty );
130  glTexCoord2i(0, 1); glVertex2i( x-hotspotx, y+hotspoty-h );
131  glTexCoord2i(1, 1); glVertex2i( x-hotspotx+w, y+hotspoty-h );
132  glEnd();
133 
134  glDisable(GL_BLEND);
135  glEnable(GL_DEPTH_TEST);
136 #endif
137  }
138 
139  Status validate() const
140  {
141  const GLint A = 128; // no cursor is expected to get this big
142  if(w > A || h > A || hotspotx > A || hotspoty > A)
144  if(ht < 0)
146  return INFO::OK;
147  }
148 };
149 
151 {
155 };
156 
157 struct Cursor
158 {
159  // require kind == CK_OpenGL after reload
160  bool forceGL;
161 
163 
164  // valid iff kind == CK_System
166 
167  // valid iff kind == CK_OpenGL
170 };
171 
173 
174 static void Cursor_init(Cursor* c, va_list args)
175 {
176  c->forceGL = (va_arg(args, int) != 0);
177 }
178 
179 static void Cursor_dtor(Cursor* c)
180 {
181  switch(c->kind)
182  {
183  case CK_Default:
184  break; // nothing to do
185 
186  case CK_System:
188  break;
189 
190  case CK_OpenGL:
191  c->gl_cursor.destroy();
193  break;
194 
195  default:
197  break;
198  }
199 }
200 
201 static Status Cursor_reload(Cursor* c, const PIVFS& vfs, const VfsPath& name, Handle)
202 {
203  const VfsPath pathname(VfsPath(L"art/textures/cursors") / name);
204 
205  // read pixel offset of the cursor's hotspot [the bit of it that's
206  // drawn at (g_mouse_x,g_mouse_y)] from file.
207  int hotspotx = 0, hotspoty = 0;
208  {
209  const VfsPath pathnameHotspot = pathname.ChangeExtension(L".txt");
210  shared_ptr<u8> buf; size_t size;
211  RETURN_STATUS_IF_ERR(vfs->LoadFile(pathnameHotspot, buf, size));
212  std::wstringstream s(std::wstring((const wchar_t*)buf.get(), size));
213  s >> hotspotx >> hotspoty;
214  }
215 
216  const VfsPath pathnameImage = pathname.ChangeExtension(L".png");
217 
218  // try loading as system cursor (2d, hardware accelerated)
219  if(!c->forceGL && load_sys_cursor(vfs, pathnameImage, hotspotx, hotspoty, &c->system_cursor) == INFO::OK)
220  c->kind = CK_System;
221  // fall back to GLCursor (system cursor code is disabled or failed)
222  else if(c->gl_cursor.create(vfs, pathnameImage, hotspotx, hotspoty) == INFO::OK)
223  {
224  c->kind = CK_OpenGL;
225  // (we need to hide the system cursor when using a OpenGL cursor)
227  }
228  // everything failed, leave cursor unchanged
229  else
230  c->kind = CK_Default;
231 
232  return INFO::OK;
233 }
234 
235 static Status Cursor_validate(const Cursor* c)
236 {
237  switch(c->kind)
238  {
239  case CK_Default:
240  break; // nothing to do
241 
242  case CK_System:
243  if(c->system_cursor == 0)
245  break;
246 
247  case CK_OpenGL:
249  break;
250 
251  default:
253  break;
254  }
255 
256  return INFO::OK;
257 }
258 
259 static Status Cursor_to_string(const Cursor* c, wchar_t* buf)
260 {
261  const wchar_t* type;
262  switch(c->kind)
263  {
264  case CK_Default:
265  type = L"default";
266  break;
267 
268  case CK_System:
269  type = L"sys";
270  break;
271 
272  case CK_OpenGL:
273  type = L"gl";
274  break;
275 
276  default:
278  type = L"?";
279  break;
280  }
281 
282  swprintf_s(buf, H_STRING_LEN, L"cursor (%ls)", type);
283  return INFO::OK;
284 }
285 
286 
287 // note: these standard resource interface functions are not exposed to the
288 // caller. all we need here is storage for the sys_cursor / GLCursor and
289 // a name -> data lookup mechanism, both provided by h_mgr.
290 // in other words, we continually create/free the cursor resource in
291 // cursor_draw and trust h_mgr's caching to absorb it.
292 
293 static Handle cursor_load(const PIVFS& vfs, const VfsPath& name, bool forceGL)
294 {
295  return h_alloc(H_Cursor, vfs, name, 0, (int)forceGL);
296 }
297 
299 {
300  h_mgr_free_type(H_Cursor);
301 }
302 
304 {
305  return h_free(h, H_Cursor);
306 }
307 
308 
309 Status cursor_draw(const PIVFS& vfs, const wchar_t* name, int x, int y, bool forceGL)
310 {
311  // hide the cursor
312  if(!name)
313  {
314  sys_cursor_set(0);
315  return INFO::OK;
316  }
317 
318  Handle hc = cursor_load(vfs, name, forceGL);
319  // TODO: if forceGL changes at runtime after a cursor is first created,
320  // we might reuse a cached version of the cursor with the old forceGL flag
321 
322  RETURN_STATUS_IF_ERR(hc); // silently ignore failures
323 
324  H_DEREF(hc, Cursor, c);
325 
326  switch(c->kind)
327  {
328  case CK_Default:
329  break;
330 
331  case CK_System:
332  sys_cursor_set(c->system_cursor);
333  break;
334 
335  case CK_OpenGL:
336  c->gl_cursor.draw(x, y);
337  // note: gl_empty_system_cursor can be 0 if sys_cursor_create_empty
338  // failed; in that case we don't want to sys_cursor_set because that
339  // would restore the default cursor (which is exactly what we're
340  // trying to avoid here)
341  if(c->gl_empty_system_cursor)
342  sys_cursor_set(c->gl_empty_system_cursor);
343  break;
344 
345  default:
347  break;
348  }
349 
350  (void)cursor_free(hc);
351  return INFO::OK;
352 }
static Status Cursor_validate(const Cursor *c)
Definition: cursor.cpp:235
void destroy()
Definition: cursor.cpp:106
Status create(const PIVFS &vfs, const VfsPath &pathname, int hotspotx_, int hotspoty_)
Definition: cursor.cpp:89
Status h_free(Handle &h, H_Type type)
Definition: h_mgr.cpp:583
int hotspoty
Definition: cursor.cpp:86
const Status LOGIC
Definition: status.h:409
CursorKind
Definition: cursor.cpp:150
Handle ht
Definition: cursor.cpp:83
const Status _1
Definition: status.h:441
Definition: tex.h:468
Path VfsPath
VFS path of the form &quot;(dir/)*file?&quot;.
Definition: vfs_path.h:40
sys_cursor system_cursor
Definition: cursor.cpp:165
void draw(int x, int y) const
Definition: cursor.cpp:113
const Status OK
Definition: status.h:386
Status tex_transform_to(Tex *t, size_t new_flags)
Change &lt;t&gt;&#39;s pixel format (2nd version) (note: this is equivalent to tex_transform(t, t-&gt;flags^new_flags).
Definition: tex.cpp:494
void * sys_cursor
Definition: cursor.h:30
Status validate() const
Definition: cursor.cpp:139
Status ogl_tex_free(Handle &ht)
Release this texture reference.
Definition: ogl_tex.cpp:586
Status sys_cursor_create(int w, int h, void *bgra_img, int hx, int hy, sys_cursor *cursor)
Create a cursor from the given color image.
Definition: android.cpp:72
shared_ptr< IVFS > PIVFS
Definition: vfs.h:226
flags &amp; TEX_DXT is a field indicating compression.
Definition: tex.h:149
indicates B and R pixel components are exchanged.
Definition: tex.h:163
int swprintf_s(wchar_t *buf, size_t max_chars, const wchar_t *fmt,...) WPRINTF_ARGS(3)
Handle h_alloc(H_Type type, const PIVFS &vfs, const VfsPath &pathname, size_t flags,...)
Definition: h_mgr.cpp:519
static Handle cursor_load(const PIVFS &vfs, const VfsPath &name, bool forceGL)
Definition: cursor.cpp:293
Status sys_cursor_create_empty(sys_cursor *cursor)
Create a transparent cursor (used to hide the system cursor).
Definition: android.cpp:85
#define H_DEREF(h, type, var)
Definition: h_mgr.h:341
Status ogl_tex_bind(Handle ht, size_t unit)
Bind texture to the specified unit in preparation for using it in rendering.
Definition: ogl_tex.cpp:1054
#define H_TYPE_DEFINE(type)
Definition: h_mgr.h:307
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning...
bool forceGL
Definition: cursor.cpp:160
Status cursor_draw(const PIVFS &vfs, const wchar_t *name, int x, int y, bool forceGL)
Draw the cursor on-screen.
Definition: cursor.cpp:309
static Status cursor_free(Handle &h)
Definition: cursor.cpp:303
Definition: path.h:75
static void Cursor_init(Cursor *c, va_list args)
Definition: cursor.cpp:174
GLint w
Definition: cursor.cpp:85
u8 * tex_get_data(const Tex *t)
rationale: since Tex is a struct, its fields are accessible to callers.
Definition: tex.cpp:629
void cursor_shutdown()
Forcibly frees all cursor handles.
Definition: cursor.cpp:298
i64 Status
Error handling system.
Definition: status.h:171
i64 Handle
`handle&#39; representing a reference to a resource (sound, texture, etc.)
Definition: handle.h:41
void h_mgr_free_type(const H_Type type)
Definition: h_mgr.cpp:790
Handle ogl_tex_load(const PIVFS &vfs, const VfsPath &pathname, size_t flags)
Load and return a handle to the texture.
Definition: ogl_tex.cpp:542
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Definition: debug.h:331
GLCursor gl_cursor
Definition: cursor.cpp:168
Path ChangeExtension(Path extension) const
Definition: path.h:185
Status ogl_tex_set_filter(Handle ht, GLint filter)
Override default filter (see ogl_tex_set_defaults) for this texture.
Definition: ogl_tex.cpp:638
GLint h
Definition: cursor.cpp:85
#define WARN_RETURN(status)
Definition: status.h:255
Status sys_cursor_set(sys_cursor cursor)
override the current system cursor.
Definition: android.cpp:93
const Status FAIL
Definition: status.h:406
const Status _2
Definition: status.h:442
const size_t H_STRING_LEN
Definition: h_mgr.h:371
static void Cursor_dtor(Cursor *c)
Definition: cursor.cpp:179
int hotspotx
Definition: cursor.cpp:86
Status sys_cursor_free(sys_cursor cursor)
destroy the indicated cursor and frees its resources.
Definition: android.cpp:105
static Status Cursor_to_string(const Cursor *c, wchar_t *buf)
Definition: cursor.cpp:259
static Status Cursor_reload(Cursor *c, const PIVFS &vfs, const VfsPath &name, Handle)
Definition: cursor.cpp:201
CursorKind kind
Definition: cursor.cpp:162
Status tex_decode(const shared_ptr< u8 > &data, size_t dataSize, Tex *t)
decode an in-memory texture file into texture object.
Definition: tex.cpp:717
static Status load_sys_cursor(const PIVFS &vfs, const VfsPath &pathname, int hx, int hy, sys_cursor *cursor)
Definition: cursor.cpp:49
Status ogl_tex_upload(const Handle ht, GLenum fmt_ovr, int q_flags_ovr, GLint int_fmt_ovr)
Upload texture to OpenGL.
Definition: ogl_tex.cpp:912
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276
Status ogl_tex_get_size(Handle ht, size_t *w, size_t *h, size_t *bpp)
Retrieve dimensions and bit depth of the texture.
Definition: ogl_tex.cpp:982
sys_cursor gl_empty_system_cursor
Definition: cursor.cpp:169