diff --git a/library/Hooks-darwin.cpp b/library/Hooks-darwin.cpp index 22e7cc2e9..fc9985858 100644 --- a/library/Hooks-darwin.cpp +++ b/library/Hooks-darwin.cpp @@ -127,6 +127,12 @@ DFhackCExport int SDL_PushEvent(SDL::Event* event) return _SDL_PushEvent(event); } +static unsigned char* (*_SDL_GetKeyState)(int *numkeys) = 0; +DFhackCExport unsigned char* SDL_GetKeyState(int *numkeys) +{ + return _SDL_GetKeyState(numkeys); +} + struct WINDOW; DFhackCExport int DFH_wgetch(WINDOW *win) { @@ -291,6 +297,7 @@ DFhackCExport int DFH_SDL_Init(uint32_t flags) bind(SDL_Quit); bind(SDL_PollEvent); bind(SDL_PushEvent); + bind(SDL_GetKeyState); bind(SDL_UpperBlit); bind(SDL_CreateRGBSurface); diff --git a/library/include/Hooks.h b/library/include/Hooks.h index 97caecfa5..07939aa65 100644 --- a/library/include/Hooks.h +++ b/library/include/Hooks.h @@ -58,6 +58,7 @@ DFhackCExport int SDL_NumJoysticks(void); DFhackCExport void SDL_Quit(void); DFhackCExport int SDL_PollEvent(SDL::Event* event); DFhackCExport int SDL_PushEvent(SDL::Event* event); +DFhackCExport unsigned char* SDL_GetKeyState(int *numkeys); DFhackCExport int SDL_Init(uint32_t flags); DFhackCExport int wgetch(WINDOW * win); diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e8e1b6bc8..c413f21db 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -136,7 +136,11 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(manipulator manipulator.cpp) DFHACK_PLUGIN(misery misery.cpp) DFHACK_PLUGIN(mode mode.cpp) - DFHACK_PLUGIN(mousequery mousequery.cpp) + IF(UNIX AND NOT APPLE) + DFHACK_PLUGIN(mousequery mousequery.cpp LINK_LIBRARIES SDL) + ELSE() + DFHACK_PLUGIN(mousequery mousequery.cpp) + ENDIF() DFHACK_PLUGIN(orders orders.cpp LINK_LIBRARIES jsoncpp) DFHACK_PLUGIN(pathable pathable.cpp LINK_LIBRARIES lua) DFHACK_PLUGIN(petcapRemover petcapRemover.cpp) diff --git a/plugins/mousequery.cpp b/plugins/mousequery.cpp index 66fa0fee5..387a03e3c 100644 --- a/plugins/mousequery.cpp +++ b/plugins/mousequery.cpp @@ -21,6 +21,10 @@ #include "TileTypes.h" #include "DataFuncs.h" +// For SDL_GetKeyState +#include "Hooks.h" +#include "SDL_keyboard.h" + DFHACK_PLUGIN("mousequery"); REQUIRE_GLOBAL(enabler); REQUIRE_GLOBAL(gps); @@ -48,6 +52,9 @@ static bool mouse_moved = false; static uint32_t scroll_delay = 100; +static bool awaiting_lbut_up, awaiting_rbut_up; +static enum { None, Left, Right } drag_mode; + static df::coord get_mouse_pos(int32_t &mx, int32_t &my) { df::coord pos; @@ -100,7 +107,7 @@ static vector get_units_at(const df::coord pos, bool only_one) { df::unit *unit = world->units.active[i]; - if(unit->pos.x == pos.x && unit->pos.y == pos.y && unit->pos.z == pos.z && + if(unit->pos.x == pos.x && unit->pos.y == pos.y && unit->pos.z == pos.z && !(unit->flags1.whole & bad_flags.whole) && unit->profession != profession::THIEF && unit->profession != profession::MASTER_THIEF) { @@ -170,7 +177,7 @@ static df::interface_key get_default_query_mode(const df::coord pos) { // For containers use item view, for everything else, query view return (type == building_type::Box || type == building_type::Cabinet || - type == building_type::Weaponrack || type == building_type::Armorstand) + type == building_type::Weaponrack || type == building_type::Armorstand) ? df::interface_key::D_BUILDITEM : df::interface_key::D_BUILDJOB; } } @@ -279,7 +286,7 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest bool isInAreaSelectionMode() { - bool selectableMode = + bool selectableMode = isInDesignationMenu() || ui->main.mode == Stockpiles || ui->main.mode == Zones; @@ -293,156 +300,190 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest return false; } - bool handleMouse(const set *input) + bool handleLeft(df::coord &mpos, int32_t mx, int32_t my) { - int32_t mx, my; - auto mpos = get_mouse_pos(mx, my); - if (mpos.x == -30000) - return false; + static unsigned char *keystate = 0; + if (!keystate) + keystate = SDL_GetKeyState(NULL); - auto dims = Gui::getDwarfmodeViewDims(); - if (enabler->mouse_lbut) - { - bool cursor_still_here = (last_clicked_x == mpos.x && last_clicked_y == mpos.y && last_clicked_z == mpos.z); - last_clicked_x = mpos.x; - last_clicked_y = mpos.y; - last_clicked_z = mpos.z; + if (!(keystate[SDL::Key::K_LSHIFT] || keystate[SDL::Key::K_RSHIFT])) + mpos.z += Gui::getDepthAt(mx, my); - df::interface_key key = interface_key::NONE; - bool designationMode = false; - bool skipRefresh = false; + bool cursor_still_here = (last_clicked_x == mpos.x && last_clicked_y == mpos.y && last_clicked_z == mpos.z); + last_clicked_x = mpos.x; + last_clicked_y = mpos.y; + last_clicked_z = mpos.z; - if (isInTrackableMode()) - { - designationMode = true; - key = df::interface_key::SELECT; - } - else + df::interface_key key = interface_key::NONE; + bool designationMode = false; + bool skipRefresh = false; + + if (isInTrackableMode()) + { + designationMode = true; + key = df::interface_key::SELECT; + } + else + { + switch (ui->main.mode) { - switch (ui->main.mode) + case QueryBuilding: + if (cursor_still_here) + key = df::interface_key::D_BUILDITEM; + break; + + case BuildingItems: + if (cursor_still_here) + key = df::interface_key::D_VIEWUNIT; + break; + + case ViewUnits: + if (cursor_still_here) + key = df::interface_key::D_LOOK; + break; + + case LookAround: + if (cursor_still_here) + key = df::interface_key::D_BUILDJOB; + break; + + case Build: + if (ui_build_selector) { - case QueryBuilding: - if (cursor_still_here) - key = df::interface_key::D_BUILDITEM; - break; - - case BuildingItems: - if (cursor_still_here) - key = df::interface_key::D_VIEWUNIT; - break; - - case ViewUnits: - if (cursor_still_here) - key = df::interface_key::D_LOOK; - break; - - case LookAround: - if (cursor_still_here) - key = df::interface_key::D_BUILDJOB; - break; - - case Build: - if (ui_build_selector) + if (ui_build_selector->stage < 2) { - if (ui_build_selector->stage < 2) - { - designationMode = true; - key = df::interface_key::SELECT; - } - else - { - designationMode = true; - skipRefresh = true; - key = df::interface_key::SELECT_ALL; - } + designationMode = true; + key = df::interface_key::SELECT; } - break; + else + { + designationMode = true; + skipRefresh = true; + key = df::interface_key::SELECT_ALL; + } + } + break; - case Default: - break; + case Default: + break; - default: - return false; - } + default: + return false; } + } - enabler->mouse_lbut = 0; + enabler->mouse_lbut = 0; - // Can't check limits earlier as we must be sure we are in query or default mode - // (so we can clear the button down flag) - int right_bound = (dims.menu_x1 > 0) ? dims.menu_x1 - 2 : gps->dimx - 2; - if (mx < 1 || mx > right_bound || my < 1 || my > gps->dimy - 2) - return false; + // Can't check limits earlier as we must be sure we are in query or default mode + // (so we can clear the button down flag) + auto dims = Gui::getDwarfmodeViewDims(); + int right_bound = (dims.menu_x1 > 0) ? dims.menu_x1 - 2 : gps->dimx - 2; + if (mx < 1 || mx > right_bound || my < 1 || my > gps->dimy - 2) + return false; - if (ui->main.mode == df::ui_sidebar_mode::Zones || - ui->main.mode == df::ui_sidebar_mode::Stockpiles) + if (ui->main.mode == df::ui_sidebar_mode::Zones || + ui->main.mode == df::ui_sidebar_mode::Stockpiles) + { + int32_t x, y, z; + if (Gui::getDesignationCoords(x, y, z)) { - int32_t x, y, z; - if (Gui::getDesignationCoords(x, y, z)) - { - auto dX = abs(x - mpos.x); - if (dX > 30) - return false; + auto dX = abs(x - mpos.x); + if (dX > 30) + return false; - auto dY = abs(y - mpos.y); - if (dY > 30) - return false; - } + auto dY = abs(y - mpos.y); + if (dY > 30) + return false; } + } - if (!designationMode) - { - Gui::resetDwarfmodeView(); + if (!designationMode) + { + Gui::resetDwarfmodeView(); - if (key == interface_key::NONE) - key = get_default_query_mode(mpos); + if (key == interface_key::NONE) + key = get_default_query_mode(mpos); - sendKey(key); - } + sendKey(key); + } - if (!skipRefresh) - { - // Force UI refresh - moveCursor(mpos, true); - } + if (!skipRefresh) + { + // Force UI refresh + moveCursor(mpos, true); + } - if (designationMode) - sendKey(key); + if (designationMode) + sendKey(key); - return true; + return true; + } + + bool handleRight(df::coord &mpos, int32_t mx, int32_t my) + { + if (isInDesignationMenu() && !box_designation_enabled) + return false; + + // Escape out of query mode + enabler->mouse_rbut_down = 0; + enabler->mouse_rbut = 0; + + using namespace df::enums::ui_sidebar_mode; + if ((ui->main.mode == QueryBuilding || ui->main.mode == BuildingItems || + ui->main.mode == ViewUnits || ui->main.mode == LookAround) || + (isInTrackableMode() && tracking_enabled)) + { + sendKey(df::interface_key::LEAVESCREEN); } - else if (rbutton_enabled && enabler->mouse_rbut) + else { - if (isInDesignationMenu() && !box_designation_enabled) - return false; + auto dims = Gui::getDwarfmodeViewDims(); + int scroll_trigger_x = dims.menu_x1 / 3; + int scroll_trigger_y = gps->dimy / 3; + if (mx < scroll_trigger_x) + sendKey(interface_key::CURSOR_LEFT_FAST); + + if (mx > ((dims.menu_x1 > 0) ? dims.menu_x1 : gps->dimx) - scroll_trigger_x) + sendKey(interface_key::CURSOR_RIGHT_FAST); + + if (my < scroll_trigger_y) + sendKey(interface_key::CURSOR_UP_FAST); + + if (my > gps->dimy - scroll_trigger_y) + sendKey(interface_key::CURSOR_DOWN_FAST); + } - // Escape out of query mode - enabler->mouse_rbut_down = 0; - enabler->mouse_rbut = 0; + return false; + } - using namespace df::enums::ui_sidebar_mode; - if ((ui->main.mode == QueryBuilding || ui->main.mode == BuildingItems || - ui->main.mode == ViewUnits || ui->main.mode == LookAround) || - (isInTrackableMode() && tracking_enabled)) + bool handleMouse(const set *input) + { + int32_t mx, my; + auto mpos = get_mouse_pos(mx, my); + if (mpos.x == -30000) + return false; + + if (enabler->mouse_lbut) + { + if (drag_mode == Left) { - sendKey(df::interface_key::LEAVESCREEN); + awaiting_lbut_up = true; + enabler->mouse_lbut = false; + last_move_pos = mpos; } else + return handleLeft(mpos, mx, my); + } + else if (enabler->mouse_rbut) + { + if (drag_mode == Right) { - int scroll_trigger_x = dims.menu_x1 / 3; - int scroll_trigger_y = gps->dimy / 3; - if (mx < scroll_trigger_x) - sendKey(interface_key::CURSOR_LEFT_FAST); - - if (mx > ((dims.menu_x1 > 0) ? dims.menu_x1 : gps->dimx) - scroll_trigger_x) - sendKey(interface_key::CURSOR_RIGHT_FAST); - - if (my < scroll_trigger_y) - sendKey(interface_key::CURSOR_UP_FAST); - - if (my > gps->dimy - scroll_trigger_y) - sendKey(interface_key::CURSOR_DOWN_FAST); + awaiting_rbut_up = true; + enabler->mouse_rbut = false; + last_move_pos = mpos; } + else if (rbutton_enabled) + return handleRight(mpos, mx, my); } else if (input->count(interface_key::CUSTOM_ALT_M) && isInDesignationMenu()) { @@ -537,7 +578,7 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest return; static decltype(enabler->clock) last_t = 0; - + auto dims = Gui::getDwarfmodeViewDims(); auto right_margin = (dims.menu_x1 > 0) ? dims.menu_x1 : gps->dimx; @@ -548,16 +589,45 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest mpos_valid = false; // Check if in lever binding mode - if (Gui::getFocusString(Core::getTopViewscreen()) == + if (Gui::getFocusString(Core::getTopViewscreen()) == "dwarfmode/QueryBuilding/Some/Lever/AddJob") { return; } + if (awaiting_lbut_up && !enabler->mouse_lbut_down) + { + awaiting_lbut_up = false; + handleLeft(mpos, mx, my); + } + + if (awaiting_rbut_up && !enabler->mouse_rbut_down) + { + awaiting_rbut_up = false; + if (rbutton_enabled) + handleRight(mpos, mx, my); + } + if (mpos_valid) { if (mpos.x != last_move_pos.x || mpos.y != last_move_pos.y || mpos.z != last_move_pos.z) { + awaiting_lbut_up = false; + awaiting_rbut_up = false; + + if ((enabler->mouse_lbut_down && drag_mode == Left) || (enabler->mouse_rbut_down && drag_mode == Right)) + { + int newx = (*df::global::window_x) - (mpos.x - last_move_pos.x); + int newy = (*df::global::window_y) - (mpos.y - last_move_pos.y); + + newx = std::max(0, std::min(newx, world->map.x_count - dims.map_x2+1)); + newy = std::max(0, std::min(newy, world->map.y_count - dims.map_y2+1)); + + (*df::global::window_x) = newx; + (*df::global::window_y) = newy; + return; + } + mouse_moved = true; last_move_pos = mpos; } @@ -650,7 +720,7 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest if (Gui::getDesignationCoords(x, y, z)) { color = COLOR_WHITE; - if (ui->main.mode == df::ui_sidebar_mode::Zones || + if (ui->main.mode == df::ui_sidebar_mode::Zones || ui->main.mode == df::ui_sidebar_mode::Stockpiles) { auto dX = abs(x - mpos.x); @@ -677,6 +747,10 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest return; } + // Don't change levels + if (mpos.z != *df::global::window_z) + return; + last_t = enabler->clock; moveCursor(mpos, false); } @@ -699,6 +773,14 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest set_to_limit(look_list, 8); int disp_y = gps->dimy - look_list - 2; + if (mpos.z != *df::global::window_z) + { + int y = gps->dimy - 2; + char buf[6]; + sprintf(buf, "@%d", mpos.z - *df::global::window_z); + OutputString(COLOR_GREY, disp_x, y, buf, true, left_margin); + } + int c = 0; for (auto it = ulist.begin(); it != ulist.end() && c < 8; it++, c++) { @@ -783,6 +865,15 @@ static command_result mousequery_cmd(color_ostream &out, vector & param { live_view = (state == "enable"); } + else if (cmd == "drag") + { + if (state == "left") + drag_mode = Left; + else if (state == "right") + drag_mode = Right; + else if (state == "disable") + drag_mode = None; + } else if (cmd[0] == 'd') { auto l = atoi(state.c_str()); @@ -827,13 +918,15 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector \n" " Set delay when edge scrolling in tracking mode. Omit amount to display current setting.\n" ));