cursor.cpp

Go to the documentation of this file.
00001 /* $Id: cursor.cpp 23842 2008-02-16 08:47:16Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file cursor.cpp
00016 //! Support for different cursors-shapes.
00017 
00018 #include "global.hpp"
00019 
00020 #include "cursor.hpp"
00021 #include "game_config.hpp"
00022 #include "image.hpp"
00023 #include "preferences_display.hpp"
00024 #include "scoped_resource.hpp"
00025 #include "sdl_utils.hpp"
00026 #include "video.hpp"
00027 
00028 #include "SDL.h"
00029 
00030 #include <iostream>
00031 #include <vector>
00032 
00033 static bool use_colour_cursors()
00034 {
00035     return game_config::editor == false && preferences::use_colour_cursors();
00036 }
00037 
00038 static SDL_Cursor* create_cursor(surface surf)
00039 {
00040     const surface nsurf(make_neutral_surface(surf));
00041     if(nsurf == NULL) {
00042         return NULL;
00043     }
00044 
00045     // The width must be a multiple of 8 (SDL requirement)
00046 
00047 #ifdef __APPLE__
00048     size_t cursor_width = 16;
00049 #else
00050     size_t cursor_width = nsurf->w;
00051     if((cursor_width%8) != 0) {
00052         cursor_width += 8 - (cursor_width%8);
00053     }
00054 #endif
00055     std::vector<Uint8> data((cursor_width*nsurf->h)/8,0);
00056     std::vector<Uint8> mask(data.size(),0);
00057 
00058     // See http://sdldoc.csn.ul.ie/sdlcreatecursor.php for documentation
00059     // on the format that data has to be in to pass to SDL_CreateCursor
00060     surface_lock lock(nsurf);
00061     const Uint32* const pixels = reinterpret_cast<Uint32*>(lock.pixels());
00062     for(int y = 0; y != nsurf->h; ++y) {
00063         for(int x = 0; x != nsurf->w; ++x) {
00064             Uint8 r,g,b,a;
00065             Uint8 trans = 0;
00066             Uint8 black = 0;
00067 
00068             const size_t index = y*cursor_width + x;
00069 
00070             if (static_cast<size_t>(x) < cursor_width) {
00071                 SDL_GetRGBA(pixels[y*nsurf->w + x],nsurf->format,&r,&g,&b,&a);
00072 
00073                 const size_t shift = 7 - (index % 8);
00074 
00075                 trans = (a < 128 ? 0 : 1) << shift;
00076                 black = (trans == 0 || (r+g + b) / 3 > 128 ? 0 : 1) << shift;
00077 
00078                 data[index/8] |= black;
00079                 mask[index/8] |= trans;
00080             }
00081 
00082 
00083         }
00084     }
00085 
00086     return SDL_CreateCursor(&data[0],&mask[0],cursor_width,nsurf->h,0,0);
00087 }
00088 
00089 namespace {
00090 
00091 SDL_Cursor* cache[cursor::NUM_CURSORS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
00092 
00093 // This array must have members corresponding to cursor::CURSOR_TYPE enum members
00094 // Apple need 16x16 b&w cursors
00095 #ifdef __APPLE__
00096 const std::string bw_images[cursor::NUM_CURSORS] = { "normal.png", "wait-alt.png", "move.png", "attack.png", "select.png", "move_drag_alt.png" , "attack_drag_alt.png", "no_cursor.png"};
00097 #else
00098 const std::string bw_images[cursor::NUM_CURSORS] = { "normal.png", "wait.png", "move.png", "attack.png", "select.png", "move_drag.png", "attack_drag.png", "no_cursor.png"};
00099 #endif
00100 
00101 const std::string colour_images[cursor::NUM_CURSORS] = { "normal.png", "wait.png", "move.png", "attack.png", "select.png", "move_drag.png", "attack_drag.png", ""};
00102 
00103 // Position of the hotspot of the cursor, from the normal topleft
00104 const int shift_x[cursor::NUM_CURSORS] = {0, 0, 0, 0, 0, 2, 3, 0};
00105 const int shift_y[cursor::NUM_CURSORS] = {0, 0, 0, 0, 0, 20, 22, 0};
00106 
00107 cursor::CURSOR_TYPE current_cursor = cursor::NORMAL;
00108 
00109 int cursor_x = -1, cursor_y = -1;
00110 surface cursor_buf = NULL;
00111 bool have_focus = true;
00112 bool colour_ready = false;
00113 
00114 }
00115 
00116 static SDL_Cursor* get_cursor(cursor::CURSOR_TYPE type)
00117 {
00118     if(cache[type] == NULL) {
00119         static const std::string prefix = "cursors-bw/";
00120         const surface surf(image::get_image(prefix + bw_images[type]));
00121         cache[type] = create_cursor(surf);
00122     }
00123 
00124     return cache[type];
00125 }
00126 
00127 static void clear_cache()
00128 {
00129     for(size_t n = 0; n != cursor::NUM_CURSORS; ++n) {
00130         if(cache[n] != NULL) {
00131             SDL_FreeCursor(cache[n]);
00132             cache[n] = NULL;
00133         }
00134     }
00135 
00136     if(cursor_buf != NULL) {
00137         cursor_buf = NULL;
00138     }
00139 }
00140 
00141 namespace cursor
00142 {
00143 
00144 manager::manager()
00145 {
00146     SDL_ShowCursor(SDL_ENABLE);
00147     set();
00148 }
00149 
00150 manager::~manager()
00151 {
00152     clear_cache();
00153     SDL_ShowCursor(SDL_ENABLE);
00154 }
00155 
00156 void temporary_use_bw()
00157 {
00158     colour_ready = false;
00159     set();
00160 }
00161 
00162 void set(CURSOR_TYPE type)
00163 {
00164     // Change only if it's a valid cursor
00165     if (type != NUM_CURSORS) {
00166         current_cursor = type;
00167     } else if (current_cursor == NUM_CURSORS) {
00168         // Except if the current one is also invalid.
00169         // In this case, change to a valid one.
00170         current_cursor = NORMAL;
00171     }
00172 
00173     const CURSOR_TYPE new_cursor = use_colour_cursors() && colour_ready ? cursor::NO_CURSOR : current_cursor;
00174 
00175     SDL_Cursor * cursor_image = get_cursor(new_cursor);
00176 
00177     // Causes problem on Mac:
00178     //if (cursor_image != NULL && cursor_image != SDL_GetCursor())
00179         SDL_SetCursor(cursor_image);
00180 
00181 }
00182 
00183 void set_dragging(bool drag)
00184 {
00185     switch(current_cursor) {
00186         case MOVE:
00187             if (drag) cursor::set(MOVE_DRAG);
00188             break;
00189         case ATTACK:
00190             if (drag) cursor::set(ATTACK_DRAG);
00191             break;
00192         case MOVE_DRAG:
00193             if (!drag) cursor::set(MOVE);
00194             break;
00195         case ATTACK_DRAG:
00196             if (!drag) cursor::set(ATTACK);
00197             break;
00198         default:
00199             break;
00200     }
00201 }
00202 
00203 CURSOR_TYPE get()
00204 {
00205     return current_cursor;
00206 }
00207 
00208 void set_focus(bool focus)
00209 {
00210     have_focus = focus;
00211     if (focus==false) {
00212         colour_ready = false;
00213         set();
00214     }
00215 }
00216 
00217 setter::setter(CURSOR_TYPE type) : old_(current_cursor)
00218 {
00219     set(type);
00220 }
00221 
00222 setter::~setter()
00223 {
00224     set(old_);
00225 }
00226 
00227 void draw(surface screen)
00228 {
00229     if(use_colour_cursors() == false) {
00230         return;
00231     }
00232 
00233     if(current_cursor == NUM_CURSORS) {
00234         current_cursor = NORMAL;
00235     }
00236 
00237     if(have_focus == false) {
00238         cursor_buf = NULL;
00239         return;
00240     }
00241 
00242     if (!colour_ready) {
00243         // Display start to draw cursor
00244         // so it can now display colour cursor
00245         colour_ready = true;
00246         // Reset the cursor to be sure that we hide the b&w
00247         set();
00248     }
00249 
00250     //! @todo FIXME: don't parse the file path every time
00251     const surface surf(image::get_image("cursors/" + colour_images[current_cursor]));
00252     if(surf == NULL) {
00253         // Fall back to b&w cursors
00254         std::cerr << "could not load colour cursors. Falling back to hardware cursors\n";
00255         preferences::set_colour_cursors(false);
00256         return;
00257     }
00258 
00259     if(cursor_buf != NULL && (cursor_buf->w != surf->w || cursor_buf->h != surf->h)) {
00260         cursor_buf = NULL;
00261     }
00262 
00263     if(cursor_buf == NULL) {
00264         cursor_buf = create_compatible_surface(surf);
00265         if(cursor_buf == NULL) {
00266             std::cerr << "Could not allocate surface for mouse cursor\n";
00267             return;
00268         }
00269     }
00270 
00271     int new_cursor_x, new_cursor_y;
00272     SDL_GetMouseState(&new_cursor_x,&new_cursor_y);
00273     const bool must_update = new_cursor_x != cursor_x || new_cursor_y != cursor_y;
00274     cursor_x = new_cursor_x;
00275     cursor_y = new_cursor_y;
00276 
00277     // Save the screen area where the cursor is being drawn onto the back buffer
00278     SDL_Rect area = {cursor_x - shift_x[current_cursor], cursor_y - shift_y[current_cursor],surf->w,surf->h};
00279     SDL_BlitSurface(screen,&area,cursor_buf,NULL);
00280 
00281     // Blit the surface
00282     SDL_BlitSurface(surf,NULL,screen,&area);
00283 
00284     if(must_update) {
00285         update_rect(area);
00286     }
00287 }
00288 
00289 void undraw(surface screen)
00290 {
00291     if(use_colour_cursors() == false) {
00292         return;
00293     }
00294 
00295     if(cursor_buf == NULL) {
00296         return;
00297     }
00298 
00299     SDL_Rect area = {cursor_x - shift_x[current_cursor], cursor_y - shift_y[current_cursor],cursor_buf->w,cursor_buf->h};
00300     SDL_BlitSurface(cursor_buf,NULL,screen,&area);
00301     update_rect(area);
00302 }
00303 
00304 } // end namespace cursor
00305 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs