Fix cursors not showing up on Windows.

(At least mostly; there might still be a few glitches.)
This commit is contained in:
2015-01-04 22:03:24 -05:00
parent c3e801ad7a
commit f44f713cac
8 changed files with 58 additions and 55 deletions

View File

@@ -220,6 +220,8 @@ void handle_actions_menu(int item_hit);
void handle_monster_info_menu(int item_hit);
void handle_menu_spell(eSpell spell_picked);
#include "cursors.h"
LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) {
if(message == WM_COMMAND) {
int cmd = LOWORD(wParam);
@@ -271,6 +273,11 @@ LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lPara
case IDM_HELP_HINTS: handle_help_menu(6); break;
case IDM_HELP_SPELLS: handle_help_menu(7); break;
}
} else if(message == WM_SETCURSOR) {
// Windows resets the cursor to an arrow whenever the mouse moves, unless we do this.
// Note: By handling this message, sf::Window::setMouseCursorVisible() will NOT work.
restore_cursor();
return true;
}
return CallWindowProc(reinterpret_cast<WNDPROC>(mainProc), handle, message, wParam, lParam);
}

View File

@@ -202,6 +202,7 @@ bool cControl::handleClick(location){
clicked = frame.contains(e.mouseButton.x, e.mouseButton.y);
depressed = false;
} else if(e.type == sf::Event::MouseMoved){
restore_cursor();
depressed = frame.contains(e.mouseMove.x, e.mouseMove.y);
}
}

View File

@@ -1158,16 +1158,18 @@ void cDialog::run(){
where = {currentEvent.mouseButton.x, currentEvent.mouseButton.y};
itemHit = process_click(where);
break;
default: // To silence warning of unhandled enum values
break;
case sf::Event::MouseMoved:
set_cursor(sword_curs);
bool inField = false;
for(auto& ctrl : controls) {
if(ctrl.second->getType() == CTRL_FIELD && ctrl.second->getBounds().contains(currentEvent.mouseMove.x, currentEvent.mouseMove.y)) {
set_cursor(text_curs);
inField = true;
break;
}
}
break;
default: // To silence warning of unhandled enum values
if(!inField) set_cursor(sword_curs);
break;
}
if(itemHit.empty()) continue;;

View File

@@ -15,6 +15,7 @@
#include "dlogutil.hpp"
#include "graphtool.h"
#include "winutil.h"
#include "cursors.h"
void cTextField::attachClickHandler(click_callback_t) throw(xHandlerNotSupported){
throw xHandlerNotSupported(false);
@@ -122,6 +123,7 @@ bool cTextField::handleClick(location clickLoc) {
if(e.type == sf::Event::MouseButtonReleased){
done = true;
} else if(e.type == sf::Event::MouseMoved){
restore_cursor();
location newLoc(e.mouseMove.x, e.mouseMove.y);
set_ip(newLoc, &cTextField::selectionPoint);
}

View File

@@ -102,6 +102,7 @@ bool cScrollbar::handleClick(location where) {
case PART_THUMB: break;
}
} else if(e.type == sf::Event::MouseMoved){
restore_cursor();
switch(pressedPart) {
case PART_UP:
depressed = e.mouseMove.y < frame.top + 16;

View File

@@ -78,6 +78,8 @@ void handle_file_menu(int item_hit);
void handle_edit_menus(int item_hit);
void handle_item_menu(int item_hit);
#include "cursors.h"
LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) {
if(message == WM_COMMAND) {
int cmd = LOWORD(wParam);
@@ -117,6 +119,11 @@ LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lPara
// Help menu
case IDM_HELP: handle_help_menu(1);
}
} else if(message == WM_SETCURSOR) {
// Windows resets the cursor to an arrow whenever the mouse moves, unless we do this.
// Note: By handling this message, sf::Window::setMouseCursorVisible() will NOT work.
restore_cursor();
return true;
}
return CallWindowProc(reinterpret_cast<WNDPROC>(mainProc), handle, message, wParam, lParam);
}

View File

@@ -149,6 +149,8 @@ void handle_item_menu(int item_hit);
void handle_monst_menu(int item_hit);
void handle_help_menu(int item_hit);
#include "cursors.h"
LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) {
if(message == WM_COMMAND) {
int cmd = LOWORD(wParam);
@@ -229,6 +231,11 @@ LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lPara
case IDM_HELP_TEST: handle_help_menu(2); break;
case IDM_HELP_DISTRIBUTE: handle_help_menu(3); break;
}
} else if(message == WM_SETCURSOR) {
// Windows resets the cursor to an arrow whenever the mouse moves, unless we do this.
// Note: By handling this message, sf::Window::setMouseCursorVisible() will NOT work.
restore_cursor();
return true;
}
return CallWindowProc(reinterpret_cast<WNDPROC>(mainProc), handle, message, wParam, lParam);
}

View File

@@ -8,64 +8,35 @@
extern cursor_type current_cursor;
extern sf::RenderWindow mainPtr;
static const COLORREF clrMagenta = RGB(255, 0, 255);
// This loads an image from a file and replaces transparency with magenta.
// This is probably undesirable in the general case, but since we only expect transparent GIFs, it should work out.
HBITMAP LoadPicture(std::string filename) {
sf::Image gif;
gif.loadFromFile(filename);
HDC dc = CreateCompatibleDC(NULL);
HBITMAP bmp = CreateCompatibleBitmap(dc, gif.getSize().x, gif.getSize().y);
SelectObject(dc, &bmp);
// Not exactly efficient, but it gets the job done.
for(int x = 0; x < gif.getSize().x; x++) {
for(int y = 0; y < gif.getSize().y; y++) {
sf::Color clr = gif.getPixel(x, y);
if(clr == sf::Color::Transparent)
SetPixel(dc, x, y, clrMagenta);
else SetPixel(dc, x, y, RGB(clr.r, clr.g, clr.b));
}
}
DeleteDC(dc);
return bmp;
}
// This function taken from <http://www.codeproject.com/Articles/5220/Creating-a-color-cursor-from-a-bitmap>
void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, HBITMAP& hAndMaskBitmap, HBITMAP& hXorMaskBitmap) {
// This function adapted from <http://www.codeproject.com/Articles/5220/Creating-a-color-cursor-from-a-bitmap>
static void GetMaskBitmaps(const sf::Image& srcImage, HBITMAP& hAndMaskBitmap, HBITMAP& hXorMaskBitmap) {
HDC hDC = GetDC(NULL);
HDC hMainDC = CreateCompatibleDC(hDC);
HDC hAndMaskDC = CreateCompatibleDC(hDC);
HDC hXorMaskDC = CreateCompatibleDC(hDC);
//Get the dimensions of the source bitmap
BITMAP bm;
GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
hAndMaskBitmap = CreateCompatibleBitmap(hDC, srcImage.getSize().x, srcImage.getSize().y);
hXorMaskBitmap = CreateCompatibleBitmap(hDC, srcImage.getSize().x, srcImage.getSize().y);
hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight);
//Select the bitmaps to DC
HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
// Select the bitmaps to DC
HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
//Scan each pixel of the souce bitmap and create the masks
COLORREF MainBitPixel;
for(int x = 0; x<bm.bmWidth; ++x) {
for(int y = 0; y<bm.bmHeight; ++y) {
MainBitPixel = GetPixel(hMainDC, x, y);
if(MainBitPixel == clrTransparent) {
// Scan each pixel of the source bitmap and create the masks
sf::Color mainBitPixel;
for(int x = 0; x < srcImage.getSize().x; ++x) {
for(int y = 0; y < srcImage.getSize().y; ++y) {
mainBitPixel = srcImage.getPixel(x, y);
if(mainBitPixel.a == 0) {
SetPixel(hAndMaskDC, x, y, RGB(255, 255, 255));
SetPixel(hXorMaskDC, x, y, RGB(0, 0, 0));
} else {
SetPixel(hAndMaskDC, x, y, RGB(0, 0, 0));
SetPixel(hXorMaskDC, x, y, MainBitPixel);
SetPixel(hXorMaskDC, x, y, RGB(mainBitPixel.r, mainBitPixel.g, mainBitPixel.b));
}
}
}
SelectObject(hMainDC, hOldMainBitmap);
SelectObject(hAndMaskDC, hOldAndMaskBitmap);
SelectObject(hXorMaskDC, hOldXorMaskBitmap);
@@ -77,12 +48,15 @@ void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, HBITMAP& hAn
}
Cursor::Cursor(fs::path imgPath, float hotSpotX, float hotSpotY) {
HBITMAP cursorImage = LoadPicture(imgPath.string()), cursorAnd, cursorXor;
if(cursorImage == NULL) {
sf::Image gif;
if(!gif.loadFromFile(imgPath.string())) {
std::string error = "Error loading cursor from " + imgPath.string();
throw std::exception(error.c_str());
}
GetMaskBitmaps(cursorImage, clrMagenta, cursorAnd, cursorXor);
// Calculate the AND and XOR masks
HBITMAP cursorAnd = CreateCompatibleBitmap(GetDC(NULL), gif.getSize().x, gif.getSize().y);
HBITMAP cursorXor = CreateCompatibleBitmap(GetDC(NULL), gif.getSize().x, gif.getSize().y);
GetMaskBitmaps(gif, cursorAnd, cursorXor);
ICONINFO iconinfo = {0};
iconinfo.fIcon = FALSE;
@@ -92,22 +66,24 @@ Cursor::Cursor(fs::path imgPath, float hotSpotX, float hotSpotY) {
iconinfo.hbmColor = cursorXor;
HCURSOR hCursor = CreateIconIndirect(&iconinfo);
ptr = &hCursor;
DeleteObject(cursorImage);
if(hCursor == NULL) {
std::string error = "Error creating cursor from " + imgPath.string();
error += " (error code " + std::to_string(GetLastError()) + ")";
throw error;
}
ptr = hCursor;
DeleteObject(cursorAnd);
DeleteObject(cursorXor);
}
Cursor::~Cursor() {
HCURSOR* curs = (HCURSOR*)ptr;
DestroyIcon(*curs);
HCURSOR curs = (HCURSOR)ptr;
DestroyIcon(curs);
}
void Cursor::apply() {
HCURSOR* curs = (HCURSOR*)ptr;
SetCursor(*curs);
// TODO: This ensures the cursor stays set when the mouse moves. Is it necessary, though?
SetClassLong(mainPtr.getSystemHandle(), GCL_HCURSOR, (LONG)(*curs));
HCURSOR curs = (HCURSOR)ptr;
SetCursor(curs);
}
void obscureCursor() {