cursor.cpp

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

Generated by doxygen 1.7.1 on Thu May 24 2012 01:02:33 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs