Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 04da99d

Browse files
committed
Setup dfhooks_sdl_loop and use it to read exact mouse position for
edgescroll
1 parent 60cd9b7 commit 04da99d

6 files changed

Lines changed: 120 additions & 41 deletions

File tree

library/Core.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2508,6 +2508,10 @@ bool Core::DFH_SDL_Event(SDL_Event* ev) {
25082508
return ret;
25092509
}
25102510

2511+
void Core::DFH_SDL_Loop() {
2512+
DFHack::runRenderThreadCallbacks();
2513+
}
2514+
25112515
bool Core::doSdlInputEvent(SDL_Event* ev)
25122516
{
25132517
// this should only ever be called from the render thread

library/Hooks.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ DFhackCExport void dfhooks_sdl_loop() {
6868
if (disabled)
6969
return;
7070
// TODO: wire this up to the new SDL-based console once it is merged
71+
DFHack::Core::getInstance().DFH_SDL_Loop();
7172
}
7273

7374
// called from the main thread for each utf-8 char read from the ncurses input

library/include/Core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ namespace DFHack
151151
friend void ::dfhooks_update();
152152
friend void ::dfhooks_prerender();
153153
friend bool ::dfhooks_sdl_event(SDL_Event* event);
154+
friend void ::dfhooks_sdl_loop();
154155
friend bool ::dfhooks_ncurses_key(int key);
155156
public:
156157
/// Get the single Core instance or make one.
@@ -238,6 +239,7 @@ namespace DFHack
238239
int Update (void);
239240
int Shutdown (void);
240241
bool DFH_SDL_Event(SDL_Event* event);
242+
void DFH_SDL_Loop();
241243
bool ncurses_wgetch(int in, int & out);
242244
bool DFH_ncurses_key(int key);
243245

library/include/modules/DFSDL.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
#include "Export.h"
44
#include "ColorText.h"
55

6+
#include <cstdint>
7+
#include <functional>
68
#include <vector>
79

810
struct SDL_Surface;
911
struct SDL_Rect;
12+
struct SDL_Renderer;
1013
struct SDL_PixelFormat;
1114
struct SDL_Window;
1215
union SDL_Event;
@@ -55,6 +58,10 @@ namespace DFHack::DFSDL
5558
DFHACK_EXPORT SDL_Surface* DFSDL_CreateRGBSurfaceWithFormat(uint32_t flags, int width, int height, int depth, uint32_t format);
5659
DFHACK_EXPORT int DFSDL_ShowSimpleMessageBox(uint32_t flags, const char* title, const char* message, SDL_Window* window);
5760

61+
DFHACK_EXPORT uint32_t DFSDL_GetMouseState(int* x, int* y);
62+
DFHACK_EXPORT void DFSDL_RenderWindowToLogical(SDL_Renderer* renderer, int windowX, int windowY, float* logicalX, float* logicalY);
63+
DFHACK_EXPORT void DFSDL_RenderLogicalToWindow(SDL_Renderer* renderer, float logicalX, float logicalY, int* windowX, int* windowY);
64+
5865
// submitted and returned text is UTF-8
5966
// see wrapper functions below for cp-437 variants
6067
DFHACK_EXPORT char* DFSDL_GetClipboardText();
@@ -76,4 +83,7 @@ namespace DFHack
7683
DFHACK_EXPORT bool getClipboardTextCp437Multiline(std::vector<std::string> * lines);
7784
DFHACK_EXPORT bool setClipboardTextCp437Multiline(std::string text);
7885

86+
// Queue a cb to be run on the render thread, with optional userdata
87+
DFHACK_EXPORT void runOnRenderThread(std::function<void(void*)> cb, void* userdata);
88+
DFHACK_EXPORT void runRenderThreadCallbacks();
7989
}

library/modules/DFSDL.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include <SDL_stdinc.h>
1010

11+
#include <vector>
12+
1113
#ifdef WIN32
1214
# include <regex>
1315
#endif
@@ -61,6 +63,9 @@ SDL_Surface* (*g_SDL_CreateRGBSurfaceWithFormat)(uint32_t flags, int width, int
6163
int (*g_SDL_ShowSimpleMessageBox)(uint32_t flags, const char *title, const char *message, SDL_Window *window) = nullptr;
6264
char* (*g_SDL_GetPrefPath)(const char* org, const char* app) = nullptr;
6365
char* (*g_SDL_GetBasePath)() = nullptr;
66+
uint32_t (*g_SDL_GetMouseState)(int* x, int* y) = nullptr;
67+
void (*g_SDL_RenderWindowToLogical)(SDL_Renderer* renderer, int windowX, int windowY, float* logicalX, float* logicalY);
68+
void (*g_SDL_RenderLogicalToWindow)(SDL_Renderer* renderer, float logicalX, float logicalY, int* windowX, int* windowY);
6469

6570
bool DFSDL::init(color_ostream &out) {
6671
for (auto &lib_str : SDL_LIBS) {
@@ -106,6 +111,9 @@ bool DFSDL::init(color_ostream &out) {
106111
bind(g_sdl_handle, SDL_ShowSimpleMessageBox);
107112
bind(g_sdl_handle, SDL_GetPrefPath);
108113
bind(g_sdl_handle, SDL_GetBasePath);
114+
bind(g_sdl_handle, SDL_GetMouseState);
115+
bind(g_sdl_handle, SDL_RenderWindowToLogical);
116+
bind(g_sdl_handle, SDL_RenderLogicalToWindow);
109117
#undef bind
110118

111119
DEBUG(dfsdl,out).print("sdl successfully loaded\n");
@@ -190,6 +198,18 @@ char* DFSDL::DFSDL_GetBasePath()
190198
return g_SDL_GetBasePath();
191199
}
192200

201+
uint32_t DFSDL::DFSDL_GetMouseState(int* x, int* y) {
202+
return g_SDL_GetMouseState(x, y);
203+
}
204+
205+
void DFSDL::DFSDL_RenderWindowToLogical(SDL_Renderer *renderer, int windowX, int windowY, float *logicalX, float *logicalY) {
206+
g_SDL_RenderWindowToLogical(renderer, windowX, windowY, logicalX, logicalY);
207+
}
208+
209+
void DFSDL::DFSDL_RenderLogicalToWindow(SDL_Renderer *renderer, float logicalX, float logicalY, int *windowX, int *windowY) {
210+
g_SDL_RenderLogicalToWindow(renderer, logicalX, logicalY, windowX, windowY);
211+
}
212+
193213
int DFSDL::DFSDL_ShowSimpleMessageBox(uint32_t flags, const char *title, const char *message, SDL_Window *window) {
194214
if (!g_SDL_ShowSimpleMessageBox)
195215
return -1;
@@ -266,3 +286,19 @@ DFHACK_EXPORT bool DFHack::setClipboardTextCp437Multiline(string text) {
266286
}
267287
return 0 == DFHack::DFSDL::DFSDL_SetClipboardText(str.str().c_str());
268288
}
289+
290+
static std::recursive_mutex render_cb_lock;
291+
static std::vector<std::pair<std::function<void(void*)>, void*>> render_cb_queue;
292+
293+
DFHACK_EXPORT void DFHack::runOnRenderThread(std::function<void (void *)> cb, void *userdata) {
294+
std::lock_guard<std::recursive_mutex> l(render_cb_lock);
295+
render_cb_queue.push_back({cb, userdata});
296+
}
297+
298+
DFHACK_EXPORT void DFHack::runRenderThreadCallbacks() {
299+
std::lock_guard<std::recursive_mutex> l(render_cb_lock);
300+
for (auto& cb : render_cb_queue) {
301+
std::get<0>(cb)(std::get<1>(cb));
302+
}
303+
render_cb_queue.clear();
304+
}

plugins/edgescroll.cpp

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "df/world_generatorst.h"
66
#include "modules/Gui.h"
7+
#include "modules/DFSDL.h"
78

89
#include "df/enabler.h"
910
#include "df/gamest.h"
@@ -52,6 +53,62 @@ DFhackCExport command_result plugin_shutdown([[maybe_unused]] color_ostream &out
5253
return CR_OK;
5354
}
5455

56+
static std::atomic_bool request_queued = false;
57+
58+
struct scroll_state {
59+
int8_t xdiff;
60+
int8_t ydiff;
61+
};
62+
63+
static const scroll_state state_default(0, 0);
64+
65+
static scroll_state state = state_default;
66+
static scroll_state queued = state_default;
67+
68+
static void render_thread_cb([[maybe_unused]] void* _) {
69+
queued = state_default;
70+
// Ignore the mouse if outside the window
71+
if (!enabler->mouse_focus) {
72+
request_queued.store(false);
73+
return;
74+
}
75+
76+
// Determine window border location in window coordinates
77+
auto* renderer = virtual_cast<df::renderer_2d>(enabler->renderer);
78+
int origin_x, origin_y = 0;
79+
int end_x, end_y;
80+
DFSDL::DFSDL_RenderLogicalToWindow((SDL_Renderer*)renderer->sdl_renderer, renderer->origin_x, renderer->origin_y, &origin_x, &origin_y);
81+
DFSDL::DFSDL_RenderLogicalToWindow((SDL_Renderer*)renderer->sdl_renderer, renderer->cur_w - renderer->origin_x, renderer->cur_h - renderer->origin_y, &end_x, &end_y);
82+
83+
int mx, my;
84+
DFSDL::DFSDL_GetMouseState(&mx, &my);
85+
86+
if (mx <= origin_x + border_range) {
87+
queued.xdiff--;
88+
} else if (mx >= end_x - border_range) {
89+
queued.xdiff++;
90+
}
91+
if (my <= origin_y + border_range) {
92+
queued.ydiff--;
93+
} else if (my >= end_y - border_range) {
94+
queued.ydiff++;
95+
}
96+
97+
request_queued.store(false);
98+
}
99+
100+
static bool update_mouse_pos() {
101+
if (request_queued.load())
102+
return false; // No new inputs, and a request for more is already placed
103+
104+
state = queued;
105+
queued = state_default;
106+
DFHack::runOnRenderThread(render_thread_cb, nullptr);
107+
request_queued.store(true);
108+
return true;
109+
}
110+
111+
// Scrolling behavior
55112
template<typename T>
56113
static void apply_scroll(T* out, T diff, T min, T max) {
57114
*out = std::min(std::max(*out + diff, min), max);
@@ -134,61 +191,30 @@ static void scroll_world(world_map screen, int xdiff, int ydiff) {
134191
}
135192

136193
DFhackCExport command_result plugin_onupdate(color_ostream &out) {
137-
138-
// Ensure either a map viewscreen or the main viewport are visible
139-
auto worldmap = get_map();
140-
if (!worldmap.has_value() && (!gps->main_viewport || !gps->main_viewport->flag.bits.active))
141-
return CR_OK;
142-
143-
// FIXME: Once dfhooks_sdl_loop is hooked up in Core, use SDL_GetMouseState
144-
// to determine the correct mouse position without forcing the screen to
145-
// render slightly un-centered.
146-
// origin_x/y are already zero if not fitting the interface to the grid
147-
148-
// Force the origin_x/y values to zero to workaround df marking any
149-
// mouse position within the margin register as invalid
150-
auto renderer = virtual_cast<df::renderer_2d>(enabler->renderer);
151-
if (renderer && (renderer->origin_x != 0 || renderer->origin_y != 0)) {
152-
renderer->origin_x = 0;
153-
renderer->origin_y = 0;
154-
}
155-
156-
auto dim_x = gps->screen_pixel_x;
157-
auto dim_y = gps->screen_pixel_y;
158-
auto x = gps->precise_mouse_x;
159-
auto y = gps->precise_mouse_y;
160-
if (x == -1 || y == -1)
161-
return CR_OK; // Invalid mouse position
162-
163194
// Apply a cooldown to any potential edgescrolls
164195
auto& core = Core::getInstance();
165196
static uint32_t last_action = 0;
166197
uint32_t now = core.p->getTickCount();
167198
if (now < last_action + cooldown_ms)
168199
return CR_OK;
169200

201+
// Update mouse_x/y to values from the render thread
202+
if (!update_mouse_pos())
203+
return CR_OK;
170204

171-
int xdiff = 0;
172-
int ydiff = 0;
173-
if (x <= border_range) {
174-
xdiff--;
175-
} else if (x >= dim_x - border_range) {
176-
xdiff++;
177-
}
178-
if (y <= border_range) {
179-
ydiff--;
180-
} else if (y >= dim_y - border_range) {
181-
ydiff++;
182-
}
205+
// Ensure either a map viewscreen or the main viewport are visible
206+
auto worldmap = get_map();
207+
if (!worldmap.has_value() && (!gps->main_viewport || !gps->main_viewport->flag.bits.active))
208+
return CR_OK;
183209

184-
if (xdiff == 0 && ydiff == 0)
210+
if (state.xdiff == 0 && state.ydiff == 0)
185211
return CR_OK; // No work to do
186212

187213
// Dispatch scrolling to active scrollables
188214
if (worldmap.has_value())
189-
scroll_world(worldmap.value(), xdiff, ydiff);
215+
scroll_world(worldmap.value(), state.xdiff, state.ydiff);
190216
else if (gps->main_viewport->flag.bits.active)
191-
scroll_dwarfmode(xdiff, ydiff);
217+
scroll_dwarfmode(state.xdiff, state.ydiff);
192218

193219
// Update cooldown
194220
last_action = now;

0 commit comments

Comments
 (0)