|
|
@@ -22,12 +22,14 @@ |
|
|
#include "../window_gui.h" |
|
|
#include "../window_func.h" |
|
|
#include "../framerate_type.h" |
|
|
#include "../table/sprites.h" |
|
|
#include "win32_v.h" |
|
|
#include <windows.h> |
|
|
#include <imm.h> |
|
|
#include <mutex> |
|
|
#include <condition_variable> |
|
|
#include <algorithm> |
|
|
#include <map> |
|
|
|
|
|
#include "../safeguards.h" |
|
|
|
|
|
@@ -74,6 +76,10 @@ static std::condition_variable_any *_draw_signal = nullptr; |
|
|
static volatile bool _draw_continue; |
|
|
/** Local copy of the palette for use in the drawing thread. */ |
|
|
static Palette _local_palette; |
|
|
/** Sprite mouse cursors converted to Win32 cursors */ |
|
|
static std::map<SpriteID, HCURSOR> _system_cursor_map; |
|
|
/** Use a system cursor instead of custom/sprite cursor? */ |
|
|
static bool _use_system_cursor = false; |
|
|
|
|
|
static void MakePalette() |
|
|
{ |
|
|
@@ -112,12 +118,239 @@ static void UpdatePalette(HDC dc, uint start, uint count) |
|
|
SetDIBColorTable(dc, start, count, rgb); |
|
|
} |
|
|
|
|
|
class DibSectionSpriteEncoder : public SpriteEncoder { |
|
|
private: |
|
|
int cursor_max_x; |
|
|
int cursor_max_y; |
|
|
BITMAPINFO bmi; |
|
|
|
|
|
public: |
|
|
DibSectionSpriteEncoder() |
|
|
{ |
|
|
this->cursor_max_x = GetSystemMetrics(SM_CXCURSOR); |
|
|
this->cursor_max_y = GetSystemMetrics(SM_CYCURSOR); |
|
|
|
|
|
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); |
|
|
bmi.bmiHeader.biWidth = this->cursor_max_x; |
|
|
bmi.bmiHeader.biHeight = -this->cursor_max_y; |
|
|
bmi.bmiHeader.biPlanes = 1; |
|
|
bmi.bmiHeader.biBitCount = 32; |
|
|
bmi.bmiHeader.biCompression = BI_RGB; |
|
|
bmi.bmiHeader.biSizeImage = 0; |
|
|
bmi.bmiHeader.biXPelsPerMeter = 3780; /* 96 dpi / 25.4 mm/inch * 1000 mm/m */ |
|
|
bmi.bmiHeader.biYPelsPerMeter = 3780; |
|
|
bmi.bmiHeader.biClrUsed = 0; |
|
|
bmi.bmiHeader.biClrImportant = 0; |
|
|
} |
|
|
|
|
|
bool Is32BppSupported() override |
|
|
{ |
|
|
return true; |
|
|
} |
|
|
|
|
|
Sprite *Encode(const SpriteLoader::Sprite *sprite_allzooms, AllocatorProc *allocator) override |
|
|
{ |
|
|
const SpriteLoader::Sprite &sprite = sprite_allzooms[ZOOM_LVL_OUT_4X]; |
|
|
|
|
|
if (sprite.width > this->cursor_max_x || sprite.height > this->cursor_max_y) return nullptr; |
|
|
if ((sprite.colours & (SCC_PAL | SCC_RGB)) == 0) return nullptr; |
|
|
|
|
|
const uint32 mOpaque = 0; |
|
|
const uint32 mTransparent = 0xFFFFFFFF; |
|
|
|
|
|
/* Bitmap for colour bits */ |
|
|
uint32 *cbits; |
|
|
HBITMAP cbmp = CreateDIBSection(GetDC(_wnd.main_wnd), &bmi, DIB_RGB_COLORS, (void **)&cbits, nullptr, 0); |
|
|
MemSetT<uint32>(cbits, 0, this->cursor_max_x * this->cursor_max_y); |
|
|
|
|
|
/* Bitmap for mask bits */ |
|
|
uint32 *mbits; |
|
|
HBITMAP mbmp = CreateDIBSection(GetDC(_wnd.main_wnd), &bmi, DIB_RGB_COLORS, (void **)&mbits, nullptr, 0); |
|
|
MemSetT<uint32>(mbits, mTransparent, this->cursor_max_x * this->cursor_max_y); |
|
|
|
|
|
/* Fill pixels */ |
|
|
if ((sprite.colours & (SCC_RGB | SCC_ALPHA)) == (SCC_RGB | SCC_ALPHA)) { |
|
|
for (uint y = 0; y < sprite.height; ++y) { |
|
|
for (uint x = 0; x < sprite.width; ++x) { |
|
|
SpriteLoader::CommonPixel &src = sprite.data[x + y * sprite.width]; |
|
|
uint32 &cdst = cbits[x + y * this->cursor_max_x]; |
|
|
uint32 &mdst = mbits[x + y * this->cursor_max_x]; |
|
|
cdst = src.b | (src.g << 8) | (src.r << 16); |
|
|
mdst = src.a > 127 ? mTransparent : mOpaque; |
|
|
} |
|
|
} |
|
|
} else if ((sprite.colours & (SCC_RGB | SCC_PAL)) == (SCC_RGB | SCC_PAL)) { |
|
|
for (uint y = 0; y < sprite.height; ++y) { |
|
|
for (uint x = 0; x < sprite.width; ++x) { |
|
|
SpriteLoader::CommonPixel &src = sprite.data[x + y * sprite.width]; |
|
|
uint32 &cdst = cbits[x + y * this->cursor_max_x]; |
|
|
uint32 &mdst = mbits[x + y * this->cursor_max_x]; |
|
|
cdst = src.b | (src.g << 8) | (src.r << 16); |
|
|
mdst = src.m == 0 ? mTransparent : mOpaque; |
|
|
} |
|
|
} |
|
|
} else if (sprite.colours & SCC_PAL) { |
|
|
for (uint y = 0; y < sprite.height; ++y) { |
|
|
for (uint x = 0; x < sprite.width; ++x) { |
|
|
SpriteLoader::CommonPixel &src = sprite.data[x + y * sprite.width]; |
|
|
uint32 &cdst = cbits[x + y * this->cursor_max_x]; |
|
|
uint32 &mdst = mbits[x + y * this->cursor_max_x]; |
|
|
Colour &c = _cur_palette.palette[src.m]; |
|
|
cdst = c.b | (c.g << 8) | (c.r << 16); |
|
|
mdst = src.m == 0 ? mTransparent : mOpaque; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/* Create cursor */ |
|
|
ICONINFO iconinfo = { |
|
|
false, |
|
|
(DWORD)min(0, -sprite.x_offs), (DWORD)min(0, -sprite.y_offs), |
|
|
mbmp, cbmp |
|
|
}; |
|
|
HCURSOR cursor = CreateIconIndirect(&iconinfo); |
|
|
|
|
|
/*Sprite *dst_sprite = (Sprite *)allocator(sizeof(Sprite) + sizeof(HCURSOR)); |
|
|
dst_sprite->width = bmi.bmiHeader.biWidth; |
|
|
dst_sprite->height = bmi.bmiHeader.biHeight; |
|
|
dst_sprite->x_offs = sprite.x_offs; |
|
|
dst_sprite->y_offs = sprite.y_offs; |
|
|
(HCURSOR)dst_sprite->data = cursor; */ |
|
|
|
|
|
return (Sprite *)(void *)cursor; |
|
|
} |
|
|
|
|
|
HCURSOR MakeCursor(CursorID cursor_sprite) |
|
|
{ |
|
|
return (HCURSOR)GetRawSprite(cursor_sprite, ST_NORMAL, SimpleSpriteAlloc, this); |
|
|
} |
|
|
}; |
|
|
|
|
|
static void RebuildSystemCursorMap() |
|
|
{ |
|
|
for (std::pair<SpriteID, HCURSOR> mapping : _system_cursor_map) { |
|
|
ICONINFO iconinfo; |
|
|
if (GetIconInfo(mapping.second, &iconinfo)) { |
|
|
DeleteObject(iconinfo.hbmColor); |
|
|
DeleteObject(iconinfo.hbmMask); |
|
|
} |
|
|
DestroyIcon(mapping.second); |
|
|
} |
|
|
_system_cursor_map.clear(); |
|
|
|
|
|
_use_system_cursor = true; |
|
|
if (!_use_system_cursor) return; |
|
|
|
|
|
static const CursorID all_cursors[] = { |
|
|
SPR_CURSOR_MOUSE, |
|
|
SPR_CURSOR_ZZZ, |
|
|
SPR_CURSOR_BUOY, |
|
|
SPR_CURSOR_QUERY, |
|
|
SPR_CURSOR_HQ, |
|
|
SPR_CURSOR_SHIP_DEPOT, |
|
|
SPR_CURSOR_SIGN, |
|
|
SPR_CURSOR_TREE, |
|
|
SPR_CURSOR_BUY_LAND, |
|
|
SPR_CURSOR_LEVEL_LAND, |
|
|
SPR_CURSOR_TOWN, |
|
|
SPR_CURSOR_INDUSTRY, |
|
|
SPR_CURSOR_ROCKY_AREA, |
|
|
SPR_CURSOR_DESERT, |
|
|
SPR_CURSOR_TRANSMITTER, |
|
|
SPR_CURSOR_AIRPORT, |
|
|
SPR_CURSOR_DOCK, |
|
|
SPR_CURSOR_CANAL, |
|
|
SPR_CURSOR_LOCK, |
|
|
SPR_CURSOR_RIVER, |
|
|
SPR_CURSOR_AQUEDUCT, |
|
|
SPR_CURSOR_BRIDGE, |
|
|
SPR_CURSOR_NS_TRACK, |
|
|
SPR_CURSOR_SWNE_TRACK, |
|
|
SPR_CURSOR_EW_TRACK, |
|
|
SPR_CURSOR_NWSE_TRACK, |
|
|
SPR_CURSOR_NS_MONO, |
|
|
SPR_CURSOR_SWNE_MONO, |
|
|
SPR_CURSOR_EW_MONO, |
|
|
SPR_CURSOR_NWSE_MONO, |
|
|
SPR_CURSOR_NS_MAGLEV, |
|
|
SPR_CURSOR_SWNE_MAGLEV, |
|
|
SPR_CURSOR_EW_MAGLEV, |
|
|
SPR_CURSOR_NWSE_MAGLEV, |
|
|
SPR_CURSOR_NS_ELRAIL, |
|
|
SPR_CURSOR_SWNE_ELRAIL, |
|
|
SPR_CURSOR_EW_ELRAIL, |
|
|
SPR_CURSOR_NWSE_ELRAIL, |
|
|
SPR_CURSOR_RAIL_STATION, |
|
|
SPR_CURSOR_TUNNEL_RAIL, |
|
|
SPR_CURSOR_TUNNEL_ELRAIL, |
|
|
SPR_CURSOR_TUNNEL_MONO, |
|
|
SPR_CURSOR_TUNNEL_MAGLEV, |
|
|
SPR_CURSOR_AUTORAIL, |
|
|
SPR_CURSOR_AUTOELRAIL, |
|
|
SPR_CURSOR_AUTOMONO, |
|
|
SPR_CURSOR_AUTOMAGLEV, |
|
|
SPR_CURSOR_WAYPOINT, |
|
|
SPR_CURSOR_RAIL_DEPOT, |
|
|
SPR_CURSOR_ELRAIL_DEPOT, |
|
|
SPR_CURSOR_MONO_DEPOT, |
|
|
SPR_CURSOR_MAGLEV_DEPOT, |
|
|
SPR_CURSOR_CONVERT_RAIL, |
|
|
SPR_CURSOR_CONVERT_ELRAIL, |
|
|
SPR_CURSOR_CONVERT_MONO, |
|
|
SPR_CURSOR_CONVERT_MAGLEV, |
|
|
SPR_CURSOR_ROAD_NESW, |
|
|
SPR_CURSOR_ROAD_NWSE, |
|
|
SPR_CURSOR_AUTOROAD, |
|
|
SPR_CURSOR_TRAMWAY_NESW, |
|
|
SPR_CURSOR_TRAMWAY_NWSE, |
|
|
SPR_CURSOR_AUTOTRAM, |
|
|
SPR_CURSOR_ROAD_DEPOT, |
|
|
SPR_CURSOR_BUS_STATION, |
|
|
SPR_CURSOR_TRUCK_STATION, |
|
|
SPR_CURSOR_ROAD_TUNNEL, |
|
|
SPR_CURSOR_CLONE_TRAIN, |
|
|
SPR_CURSOR_CLONE_ROADVEH, |
|
|
SPR_CURSOR_CLONE_SHIP, |
|
|
SPR_CURSOR_CLONE_AIRPLANE, |
|
|
SPR_CURSOR_DEMOLISH_FIRST, |
|
|
SPR_CURSOR_DEMOLISH_1, |
|
|
SPR_CURSOR_DEMOLISH_2, |
|
|
SPR_CURSOR_DEMOLISH_LAST, |
|
|
SPR_CURSOR_LOWERLAND_FIRST, |
|
|
SPR_CURSOR_LOWERLAND_1, |
|
|
SPR_CURSOR_LOWERLAND_LAST, |
|
|
SPR_CURSOR_RAISELAND_FIRST, |
|
|
SPR_CURSOR_RAISELAND_1, |
|
|
SPR_CURSOR_RAISELAND_LAST, |
|
|
SPR_CURSOR_PICKSTATION_FIRST, |
|
|
SPR_CURSOR_PICKSTATION_1, |
|
|
SPR_CURSOR_PICKSTATION_LAST, |
|
|
SPR_CURSOR_BUILDSIGNALS_FIRST, |
|
|
SPR_CURSOR_BUILDSIGNALS_LAST, |
|
|
}; |
|
|
|
|
|
std::unique_ptr<DibSectionSpriteEncoder> enc(new DibSectionSpriteEncoder); |
|
|
for (CursorID cursor_sprite : all_cursors) { |
|
|
_system_cursor_map[cursor_sprite] = enc->MakeCursor(cursor_sprite); |
|
|
} |
|
|
} |
|
|
|
|
|
bool VideoDriver_Win32::ClaimMousePointer() |
|
|
{ |
|
|
MyShowCursor(false, true); |
|
|
return true; |
|
|
} |
|
|
|
|
|
bool VideoDriver_Win32::UseSystemCursor() |
|
|
{ |
|
|
return _use_system_cursor && _system_cursor_map.contains(_cursor.sprite_seq[0].sprite); |
|
|
} |
|
|
|
|
|
void VideoDriver_Win32::ClearSystemSprites() |
|
|
{ |
|
|
RebuildSystemCursorMap(); |
|
|
} |
|
|
|
|
|
struct VkMapping { |
|
|
byte vk_from; |
|
|
byte vk_count; |
|
|
@@ -720,6 +953,15 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP |
|
|
if (!_left_button_down && !_right_button_down) MyShowCursor(true); |
|
|
return 0; |
|
|
|
|
|
case WM_SETCURSOR: { |
|
|
if (!_use_system_cursor) return 0; |
|
|
if (LOWORD(lParam) != HTCLIENT) return DefWindowProc(hwnd, msg, wParam, lParam); |
|
|
HCURSOR hc = _system_cursor_map[_cursor.sprite_seq[0].sprite]; |
|
|
if (hc == nullptr) return 0; |
|
|
SetCursor(hc); |
|
|
return 1; |
|
|
} |
|
|
|
|
|
case WM_MOUSEMOVE: { |
|
|
int x = (int16)LOWORD(lParam); |
|
|
int y = (int16)HIWORD(lParam); |
|
|
@@ -758,7 +1000,7 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP |
|
|
ClientToScreen(hwnd, &pt); |
|
|
SetCursorPos(pt.x, pt.y); |
|
|
} |
|
|
MyShowCursor(false); |
|
|
MyShowCursor(_use_system_cursor); |
|
|
HandleMouseEvents(); |
|
|
return 0; |
|
|
} |
|
|
@@ -1172,6 +1414,8 @@ void VideoDriver_Win32::MainLoop() |
|
|
std::thread draw_thread; |
|
|
std::unique_lock<std::recursive_mutex> draw_lock; |
|
|
|
|
|
RebuildSystemCursorMap(); |
|
|
|
|
|
if (_draw_threaded) { |
|
|
/* Initialise the mutex first, because that's the thing we *need* |
|
|
* directly in the newly created thread. */ |
|
|
|