11#include " ColorText.h"
2- #include " PluginManager.h"
32#include " MemAccess.h"
3+ #include " PluginManager.h"
44
5- #include " df/world_generatorst.h"
65#include " modules/Gui.h"
76#include " modules/DFSDL.h"
87
1615#include " df/viewscreen_new_regionst.h"
1716#include " df/world.h"
1817#include " df/world_data.h"
18+ #include " df/world_generatorst.h"
1919
2020#include < cstdint>
2121#include < optional>
@@ -53,33 +53,37 @@ DFhackCExport command_result plugin_shutdown([[maybe_unused]] color_ostream &out
5353 return CR_OK;
5454}
5555
56- static std::atomic_bool request_queued = false ;
56+ static std::atomic_bool callback_queued = false ;
5757
5858struct scroll_state {
5959 int8_t xdiff;
6060 int8_t ydiff;
6161};
6262
63- static const scroll_state state_default (0 , 0 );
63+ static scroll_state state;
64+ static scroll_state queued;
6465
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;
66+ static void render_thread_cb () {
67+ queued = {0 };
7068 // Ignore the mouse if outside the window
7169 if (!enabler->mouse_focus ) {
72- request_queued .store (false );
70+ callback_queued .store (false );
7371 return ;
7472 }
7573
76- // Determine window border location in window coordinates
74+ // Calculate the render rect in window coordinates
7775 auto * renderer = virtual_cast<df::renderer_2d>(enabler->renderer );
78- int origin_x, origin_y = 0 ;
76+ int origin_x, origin_y;
7977 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-
78+ DFSDL::DFSDL_RenderLogicalToWindow (
79+ (SDL_Renderer*)renderer->sdl_renderer , (float )renderer->origin_x ,
80+ (float )renderer->origin_y , &origin_x, &origin_y);
81+ DFSDL::DFSDL_RenderLogicalToWindow (
82+ (SDL_Renderer*)renderer->sdl_renderer ,
83+ (float )renderer->cur_w - (float )renderer->origin_x ,
84+ (float )(renderer->cur_h - renderer->origin_y ), &end_x, &end_y);
85+
86+ // Get the mouse location in window coordinates
8387 int mx, my;
8488 DFSDL::DFSDL_GetMouseState (&mx, &my);
8589
@@ -94,26 +98,28 @@ static void render_thread_cb([[maybe_unused]] void* _) {
9498 queued.ydiff ++;
9599 }
96100
97- request_queued .store (false );
101+ callback_queued .store (false );
98102}
99103
100104static bool update_mouse_pos () {
101- if (request_queued .load ())
102- return false ; // No new inputs, and a request for more is already placed
105+ if (callback_queued .load ())
106+ return false ; // Queued callback not complete, check back later
103107
108+ // Queued callback complete, save the results and enqueue again
104109 state = queued;
105- queued = state_default ;
106- DFHack::runOnRenderThread (render_thread_cb, nullptr );
107- request_queued .store (true );
110+ queued = { 0 } ;
111+ DFHack::runOnRenderThread (render_thread_cb);
112+ callback_queued .store (true );
108113 return true ;
109114}
110115
111- // Scrolling behavior
116+ // Apply scroll whilst maintaining boundaries
112117template <typename T>
113118static void apply_scroll (T* out, T diff, T min, T max) {
114119 *out = std::min (std::max (*out + diff, min), max);
115120}
116121
122+ // Scroll main fortress/adventure world views
117123static void scroll_dwarfmode (int xdiff, int ydiff) {
118124 using df::global::window_x;
119125 using df::global::window_y;
@@ -137,7 +143,7 @@ static void scroll_dwarfmode(int xdiff, int ydiff) {
137143}
138144
139145template <typename T>
140- static void scroll_world (T* screen, int xdiff, int ydiff) {
146+ static void scroll_world_internal (T* screen, int xdiff, int ydiff) {
141147 if constexpr (std::is_same_v<T, df::viewscreen_choose_start_sitest>) {
142148 if (screen->zoomed_in ) {
143149 int max_x = (world->world_data ->world_width * 16 )-1 ;
@@ -169,6 +175,9 @@ using world_map = std::variant<df::viewscreen_choose_start_sitest*, df::viewscre
169175static std::optional<world_map> get_map () {
170176 df::viewscreen* screen = Gui::getCurViewscreen (true );
171177 screen = Gui::getDFViewscreen (true , screen); // Get the first non-dfhack viewscreen
178+ if (!screen)
179+ return {};
180+
172181 if (auto start_site = virtual_cast<df::viewscreen_choose_start_sitest>(screen))
173182 return start_site;
174183 if (auto world_map = virtual_cast<df::viewscreen_worldst>(screen))
@@ -183,9 +192,9 @@ static std::optional<world_map> get_map() {
183192
184193static void scroll_world (world_map screen, int xdiff, int ydiff) {
185194 const auto visitor = overloads {
186- [xdiff, ydiff](df::viewscreen_choose_start_sitest* s) {scroll_world (s, xdiff, ydiff);},
187- [xdiff, ydiff](df::viewscreen_worldst* s) {scroll_world (s, xdiff, ydiff);},
188- [xdiff, ydiff](df::viewscreen_new_regionst* s) {scroll_world (s, xdiff, ydiff);},
195+ [xdiff, ydiff](df::viewscreen_choose_start_sitest* s) {scroll_world_internal (s, xdiff, ydiff);},
196+ [xdiff, ydiff](df::viewscreen_worldst* s) {scroll_world_internal (s, xdiff, ydiff);},
197+ [xdiff, ydiff](df::viewscreen_new_regionst* s) {scroll_world_internal (s, xdiff, ydiff);},
189198 };
190199 std::visit (visitor, screen);
191200}
@@ -198,18 +207,18 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) {
198207 if (now < last_action + cooldown_ms)
199208 return CR_OK;
200209
201- // Update mouse_x/y to values from the render thread
210+ // Update mouse_x/y from values read in render thread callback
202211 if (!update_mouse_pos ())
203- return CR_OK;
212+ return CR_OK; // No new input to process
213+
214+ if (state.xdiff == 0 && state.ydiff == 0 )
215+ return CR_OK; // No work to do
204216
205217 // Ensure either a map viewscreen or the main viewport are visible
206218 auto worldmap = get_map ();
207219 if (!worldmap.has_value () && (!gps->main_viewport || !gps->main_viewport ->flag .bits .active ))
208220 return CR_OK;
209221
210- if (state.xdiff == 0 && state.ydiff == 0 )
211- return CR_OK; // No work to do
212-
213222 // Dispatch scrolling to active scrollables
214223 if (worldmap.has_value ())
215224 scroll_world (worldmap.value (), state.xdiff , state.ydiff );
0 commit comments