cursor.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2008 by the FIFE team                              *
00003  *   http://www.fifengine.de                                               *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 #if defined( WIN32 )
00024 #include <windows.h>
00025 #include <sdl.h>
00026 #endif
00027 
00028 #if defined( __unix__ )
00029 #include <X11/Xcursor/Xcursor.h>
00030 #endif
00031 
00032 // 3rd party library includes
00033 
00034 // FIFE includes
00035 // These includes are split up in two parts, separated by one empty line
00036 // First block: files included from the FIFE root src directory
00037 // Second block: files included from the same folder
00038 #include "util/structures/rect.h"
00039 #include "util/time/timemanager.h"
00040 #include "util/log/logger.h"
00041 #include "video/imagemanager.h"
00042 
00043 #include "animation.h"
00044 #include "image.h"
00045 #include "renderbackend.h"
00046 #include "cursor.h"
00047 
00048 #if defined( WIN32 )
00049 
00050 // From SDL_sysmouse.c
00051 struct WMcursor {
00052     HCURSOR curs;
00053 #ifndef _WIN32_WCE
00054     Uint8 *ands;
00055     Uint8 *xors;
00056 #endif
00057 };
00058 
00059 #endif
00060 
00061 #if defined( __unix__ )
00062 
00063 // Stops the compiler from confusing it with FIFE:Cursor
00064 typedef Cursor XCursor;
00065 
00066 // From SDL_x11mouse.c
00067 struct WMcursor {
00068     Cursor x_cursor;
00069 };
00070 
00071 #endif
00072 
00073 namespace FIFE {
00074     static Logger _log(LM_GUI); //@todo We should have a log module for cursor
00075 
00076     Cursor::Cursor(RenderBackend* renderbackend):
00077         m_cursor_id(NC_ARROW),
00078         m_drag_id(0),
00079         m_cursor_type(CURSOR_NATIVE),
00080         m_drag_type(CURSOR_NONE),
00081         m_native_cursor(NULL),
00082         m_renderbackend(renderbackend),
00083         m_animtime(0),
00084         m_drag_animtime(0),
00085         m_drag_offset_x(0),
00086         m_drag_offset_y(0),
00087         m_mx(0),
00088         m_my(0),
00089         m_timemanager(TimeManager::instance()),
00090         m_invalidated(false) {
00091         assert(m_timemanager);
00092         set(m_cursor_id);
00093     }
00094 
00095     void Cursor::set(uint32_t cursor_id) {
00096         m_cursor_type = CURSOR_NATIVE;
00097 
00098         if (!SDL_ShowCursor(1)) {
00099             SDL_PumpEvents();
00100         }
00101         setNativeCursor(cursor_id);
00102 
00103         m_cursor_image.reset();
00104         m_cursor_animation.reset();
00105     }
00106 
00107     void Cursor::set(ImagePtr image) {
00108         assert(image != 0);
00109 
00110         m_cursor_image = image;
00111         m_cursor_type = CURSOR_IMAGE;
00112 
00113         if (SDL_ShowCursor(0)) {
00114             SDL_PumpEvents();
00115         }
00116 
00117         m_cursor_id = NC_ARROW;
00118         m_cursor_animation.reset();
00119     }
00120 
00121     void Cursor::set(AnimationPtr anim) {
00122         assert(anim != 0);
00123 
00124         m_cursor_animation = anim;
00125         m_cursor_type = CURSOR_ANIMATION;
00126 
00127         if (SDL_ShowCursor(0)) {
00128             SDL_PumpEvents();
00129         }
00130         m_animtime = m_timemanager->getTime();
00131 
00132         m_cursor_id = NC_ARROW;
00133         m_cursor_image.reset();
00134     }
00135 
00136     void Cursor::setDrag(ImagePtr image, int32_t drag_offset_x, int32_t drag_offset_y) {
00137         assert(image != 0);
00138 
00139         m_cursor_drag_image = image;
00140         m_drag_type = CURSOR_IMAGE;
00141         m_drag_offset_x = drag_offset_x;
00142         m_drag_offset_y = drag_offset_y;
00143 
00144         m_cursor_drag_animation.reset();
00145     }
00146 
00147     void Cursor::setDrag(AnimationPtr anim, int32_t drag_offset_x, int32_t drag_offset_y) {
00148         assert(anim != 0);
00149 
00150         m_cursor_drag_animation = anim;
00151         m_drag_type = CURSOR_ANIMATION;
00152         m_drag_offset_x = drag_offset_x;
00153         m_drag_offset_y = drag_offset_y;
00154 
00155         m_drag_animtime = m_timemanager->getTime();
00156 
00157         m_cursor_drag_image.reset();
00158     }
00159 
00160     void Cursor::resetDrag() {
00161         m_drag_type = CURSOR_NONE;
00162 
00163         m_drag_animtime = 0;
00164         m_drag_offset_x = 0;
00165         m_drag_offset_y = 0;
00166 
00167         m_cursor_drag_animation.reset();
00168         m_cursor_drag_image.reset();
00169     }
00170 
00171     void Cursor::setPosition(uint32_t x, uint32_t y) {
00172         m_mx = x;
00173         m_my = y;
00174         SDL_WarpMouse(m_mx, m_my);
00175     }
00176 
00177     void Cursor::getPosition(int32_t* x, int32_t* y) {
00178         *x = m_mx;
00179         *y = m_my;
00180     }
00181 
00182     void Cursor::invalidate() {
00183         if (m_native_cursor != NULL) {
00184             SDL_free(m_native_cursor->wm_cursor);
00185             m_native_cursor->wm_cursor = NULL;
00186             SDL_FreeCursor(m_native_cursor);
00187             m_native_cursor = NULL;
00188 
00189             m_invalidated = true;
00190         }
00191     }
00192 
00193     void Cursor::draw() {
00194         if (m_invalidated) {
00195             if (m_cursor_type != CURSOR_ANIMATION || m_cursor_type == CURSOR_IMAGE ) {
00196                 set(m_cursor_id);
00197             }
00198 
00199             m_invalidated = false;
00200         }
00201 
00202         SDL_GetMouseState(&m_mx, &m_my);
00203         if ((m_cursor_type == CURSOR_NATIVE) && (m_drag_type == CURSOR_NONE)) {
00204             return;
00205         }
00206 
00207         // render possible drag image
00208         ImagePtr img;
00209         if (m_drag_type == CURSOR_IMAGE) {
00210             img = m_cursor_drag_image;
00211         }
00212         else if (m_drag_type == CURSOR_ANIMATION) {
00213             int32_t animtime = (m_timemanager->getTime() - m_drag_animtime) % m_cursor_drag_animation->getDuration();
00214             img = m_cursor_drag_animation->getFrameByTimestamp(animtime);
00215         }
00216 
00217         if (img != 0) {
00218             Rect area(m_mx + m_drag_offset_x + img->getXShift(), m_my + m_drag_offset_y + img->getYShift(), img->getWidth(), img->getHeight());
00219             m_renderbackend->pushClipArea(area, false);
00220             img->render(area);
00221             m_renderbackend->renderVertexArrays();
00222             m_renderbackend->popClipArea();
00223         }
00224 
00225         ImagePtr img2;
00226         // render possible cursor image
00227         if (m_cursor_type == CURSOR_IMAGE) {
00228             img2 = m_cursor_image;
00229         }
00230         else if (m_cursor_type == CURSOR_ANIMATION) {
00231             int32_t animtime = (m_timemanager->getTime() - m_animtime) % m_cursor_animation->getDuration();
00232             img2 = m_cursor_animation->getFrameByTimestamp(animtime);
00233         }
00234 
00235         if (img2 != 0) {
00236             Rect area(m_mx + img2->getXShift(), m_my + img2->getYShift(), img2->getWidth(), img2->getHeight());
00237             m_renderbackend->pushClipArea(area, false);
00238             img2->render(area);
00239             m_renderbackend->renderVertexArrays();
00240             m_renderbackend->popClipArea();
00241         }
00242     }
00243 
00244     uint32_t Cursor::getNativeId(uint32_t cursor_id) {
00245 #if defined( WIN32 )
00246         switch (cursor_id) {
00247             case NC_ARROW:
00248                 return 32512; // IDC_ARROW;
00249             case NC_IBEAM:
00250                 return 32513; // IDC_IBEAM;
00251             case NC_WAIT:
00252                 return 32514; // IDC_WAIT;
00253             case NC_CROSS:
00254                 return 32515; // IDC_CROSS;
00255             case NC_UPARROW:
00256                 return 32516; // IDC_UPARROW;
00257             case NC_RESIZESE:
00258                 return 32642; // IDC_SIZENWSE;
00259             case NC_RESIZESW:
00260                 return 32643; // IDC_SIZENESW;
00261             case NC_RESIZEE:
00262                 return 32644; // IDC_SIZEWE;
00263             case NC_RESIZES:
00264                 return 32645; // IDC_SIZENS;
00265             case NC_RESIZENW:
00266                 return 32642; // IDC_SIZENWSE;
00267             case NC_RESIZENE:
00268                 return 32643; // IDC_SIZENESW;
00269             case NC_RESIZEW:
00270                 return 32644; // IDC_SIZEWE;
00271             case NC_RESIZEN:
00272                 return 32645; // IDC_SIZENS;
00273             case NC_RESIZEALL:
00274                 return 32646; // IDC_SIZEALL;
00275             case NC_NO:
00276                 return 32648; // IDC_NO;
00277             case NC_HAND:
00278                 return 32649; // IDC_HAND;
00279             case NC_APPSTARTING:
00280                 return 32650; // IDC_APPSTARTING;
00281             case NC_HELP:
00282                 return 32651; // IDC_HELP;
00283             default:
00284                 break;
00285         }
00286 
00287 #elif defined( __unix__ )
00288         switch (cursor_id) {
00289             case NC_ARROW:
00290                 return 68;
00291             case NC_IBEAM:
00292                 return 152;
00293             case NC_WAIT:
00294                 return 150;
00295             case NC_CROSS:
00296                 return 130;
00297             case NC_UPARROW:
00298                 return 22;
00299             case NC_RESIZESE:
00300                 return 14;
00301             case NC_RESIZESW:
00302                 return 12;
00303             case NC_RESIZEE:
00304                 return 96;
00305             case NC_RESIZES:
00306                 return 16;
00307             case NC_RESIZENW:
00308                 return 134;
00309             case NC_RESIZENE:
00310                 return 136;
00311             case NC_RESIZEW:
00312                 return 70;
00313             case NC_RESIZEN:
00314                 return 138;
00315             case NC_RESIZEALL:
00316                 return 52;
00317             case NC_NO:
00318                 return 0;
00319             case NC_HAND:
00320                 return 60;
00321             case NC_APPSTARTING:
00322                 return 150;
00323             case NC_HELP:
00324                 return 92;
00325             default:
00326                 break;
00327         }
00328 #endif
00329         return cursor_id;
00330     }
00331 
00332     void Cursor::setNativeCursor(uint32_t cursor_id) {
00333 #if defined( WIN32 ) || defined(__unix__)
00334         // Check if a value in NativeCursors is requested
00335         cursor_id = getNativeId(cursor_id);
00336 
00337         // Load cursor
00338 #if defined( __unix__ )
00339         static Display* dsp = XOpenDisplay(NULL);
00340         XCursor xCursor = XcursorShapeLoadCursor(dsp, cursor_id);
00341         if (xCursor == 0) {
00342             if (m_native_cursor != NULL) {
00343                 SDL_FreeCursor(m_native_cursor);
00344                 m_native_cursor = NULL;
00345             }
00346             FL_WARN(_log, "Cursor: No cursor matching cursor_id was found.");
00347             return;
00348         }
00349 #elif defined( WIN32 )
00350         // Load native cursor
00351         HCURSOR hIcon = LoadCursor(NULL, MAKEINTRESOURCE(cursor_id));
00352         if (hIcon == static_cast<HCURSOR>(0)) {
00353             if (m_native_cursor != NULL) {
00354                 SDL_FreeCursor(m_native_cursor);
00355                 m_native_cursor = NULL;
00356             }
00357             FL_WARN(_log, "Cursor: No cursor matching cursor_id was found.");
00358             return;
00359         }
00360 #endif
00361 
00362         WMcursor *cursor;
00363         SDL_Cursor *curs2;
00364 
00365         // Allocate memory. Use SDL_FreeCursor to free cursor memory
00366         cursor = (WMcursor *)SDL_malloc(sizeof(*cursor));
00367         curs2 = (SDL_Cursor *)SDL_malloc(sizeof *curs2);
00368 
00369         //-- Set up some default values --
00370         curs2->wm_cursor = cursor;
00371         curs2->data = NULL;
00372         curs2->mask = NULL;
00373         curs2->save[0] = NULL;
00374         curs2->save[1] = NULL;
00375         curs2->area.x = 0;
00376         curs2->area.y = 0;
00377         curs2->area.w = 32;
00378         curs2->area.h = 32;
00379         curs2->hot_x = 0;
00380         curs2->hot_y = 0;
00381 
00382 #if defined(WIN32)
00383         cursor->curs = hIcon;
00384 #ifndef _WIN32_WCE
00385         cursor->ands = NULL;
00386         cursor->xors = NULL;
00387 #endif
00388 
00389         // Get hot spot
00390         ICONINFO iconinfo;
00391         if (GetIconInfo(hIcon, &iconinfo)) {
00392             curs2->hot_x = static_cast<Sint16>(iconinfo.xHotspot);
00393             curs2->hot_y = static_cast<Sint16>(iconinfo.yHotspot);
00394         }
00395 
00396 #elif defined(__unix__)
00397         cursor->x_cursor = xCursor;
00398         XSync(dsp, false);
00399 #endif
00400 
00401         m_native_cursor = curs2;
00402         SDL_SetCursor(curs2);
00403 
00404 #endif // WIN32 || __unix__
00405     }
00406 }