Help with cursor

Haru Urara

Участник
Автор темы
52
19
Я учусь создавать меню для SA-MP. Как исправить курсор мыши? Он не двигается с середины экрана, когда я открываю меню. Я использую SDK SAMP от DK22Pac.

1769726783046.png

My Code:
C++:
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
#include <d3d9.h>
#include <stdio.h>
#include <time.h>
#pragma comment(lib, "d3d9.lib")

#include "imgui.h"
#include "imgui_impl_dx9.h"
#include "imgui_impl_win32.h"
#include "MinHook.h"

#ifdef _M_X64
#pragma comment(lib, "libMinHook.x64.lib")
#else
#pragma comment(lib, "libMinHook.x86.lib")
#endif



#define LOG_FILE "bode_menu.txt"
#define FRAME_TIME_BUDGET 16.0f  // ~60 FPS, em ms

// Estrutura para salvar estado do cursor
struct CursorState {
    bool bVisible;
    bool clipExists;
    RECT clipRect;
    HCURSOR hCursor;
};



static bool menu = false;
static bool inicializado = false;
static bool render_paused = false;
static int click_count = 0;
static HWND gameWindow = nullptr;

// Estado do cursor
static CursorState saved_cursor_state = {};
static bool cursor_state_saved = false;

// Ponteiros originais (D3D9)
typedef HRESULT(__stdcall* tEndScene)(IDirect3DDevice9*);
typedef HRESULT(__stdcall* tReset)(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*);
tEndScene oEndScene = nullptr;
tReset oReset = nullptr;
WNDPROC oWndProc = nullptr;

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);


void Log(const char* fmt, ...) {
    FILE* f = fopen(LOG_FILE, "a");
    if (f) {
        // Timestamp
        time_t now = time(nullptr);
        struct tm* timeinfo = localtime(&now);
        fprintf(f, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);

        va_list args;
        va_start(args, fmt);
        vfprintf(f, fmt, args);
        fprintf(f, "\n");
        va_end(args);
        fclose(f);
    }
}



void SaveCursorState() {
    if (cursor_state_saved) return;

    Log("[CURSOR] Salvando estado do cursor...");

    // Usa CURSORINFO para obter visibilidade e hCursor
    CURSORINFO ci;
    ci.cbSize = sizeof(ci);
    if (GetCursorInfo(&ci)) {
        saved_cursor_state.bVisible = (ci.flags & CURSOR_SHOWING) != 0;
        saved_cursor_state.hCursor = ci.hCursor;
    }
    else {
        // fallback
        saved_cursor_state.bVisible = true;
        saved_cursor_state.hCursor = LoadCursor(NULL, IDC_ARROW);
    }

    // Salva clip region, marcando se existe
    RECT rc;
    if (GetClipCursor(&rc)) {
        saved_cursor_state.clipExists = true;
        saved_cursor_state.clipRect = rc;
    }
    else {
        saved_cursor_state.clipExists = false;
    }

    cursor_state_saved = true;
    Log("[CURSOR] Estado salvo: Visible=%d, ClipExists=%d, Cursor=0x%p",
        saved_cursor_state.bVisible, saved_cursor_state.clipExists, saved_cursor_state.hCursor);
}

void RestoreCursorState() {
    if (!cursor_state_saved) return;

    Log("[CURSOR] Restaurando estado do cursor...");

    // Restaura clip region somente se existia; caso contrário remove confinamento
    if (saved_cursor_state.clipExists) {
        ClipCursor(&saved_cursor_state.clipRect);
    }
    else {
        ClipCursor(NULL);
    }

    // Restaura cursor gráfico
    SetCursor(saved_cursor_state.hCursor);

    // Restaura visibilidade usando ShowCursor; ajusta contador interno
    if (saved_cursor_state.bVisible) {
        while (ShowCursor(TRUE) < 0) {}
    }
    else {
        while (ShowCursor(FALSE) >= 0) {}
    }

    cursor_state_saved = false;
    Log("[CURSOR] Estado restaurado: Visible=%d", saved_cursor_state.bVisible);
}

void ShowCursorForMenu() {
    Log("[CURSOR] Mostrando cursor para menu...");

    // Remove confinamento
    ClipCursor(NULL);

    // Força cursor sistema com seta
    SetCursor(LoadCursor(NULL, IDC_ARROW));

    // Garante visibilidade
    while (ShowCursor(TRUE) < 0) {}

    // Pedir para ImGui não desenhar cursor (usar sistema)
    ImGui::GetIO().MouseDrawCursor = false;

    Log("[CURSOR] Cursor visível para menu");
}

// ============================================================================
// WNDPROC HOOK - INPUT HANDLING
// ============================================================================

LRESULT CALLBACK WndProcHook(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    // Se menu está fechado, passa tudo normalmente
    if (!menu) {
        return CallWindowProc(oWndProc, hWnd, uMsg, wParam, lParam);
    }

    // Menu está aberto - ImGui processa primeiro
    if (ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam))
        return TRUE;

    ImGuiIO& io = ImGui::GetIO();

    // Se ImGui quer capturar, bloqueia do jogo
    if (io.WantCaptureMouse) {
        if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
            return TRUE;
    }

    if (io.WantCaptureKeyboard) {
        if (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST)
            return TRUE;
    }

    return CallWindowProc(oWndProc, hWnd, uMsg, wParam, lParam);
}

// ============================================================================
// D3D9 HOOKS
// ============================================================================

HRESULT __stdcall HookedReset(IDirect3DDevice9* pDevice, D3DPRESENT_PARAMETERS* pPresentationParameters) {
    Log("[D3D] Reset detectado");

    if (inicializado) {
        ImGui_ImplDX9_InvalidateDeviceObjects();
    }

    HRESULT hr = oReset(pDevice, pPresentationParameters);

    if (SUCCEEDED(hr) && inicializado) {
        Log("[D3D] Recriando device objects...");
        ImGui_ImplDX9_CreateDeviceObjects();
    }

    return hr;
}

HRESULT __stdcall HookedEndScene(IDirect3DDevice9* pDevice) {
    // Skip frames logic to avoid residual draw immediately after fechar
    static int skip_render_frames = 0;
    if (skip_render_frames > 0) {
        --skip_render_frames;
        return oEndScene(pDevice);
    }

    
    if (!inicializado) {
        Log("[INIT] Inicializando menu ImGui...");

        gameWindow = FindWindowA(NULL, "GTA:SA:MP");
        if (!gameWindow) gameWindow = FindWindowA("Grand Theft Auto San Andreas", NULL);
        if (!gameWindow) {
            D3DDEVICE_CREATION_PARAMETERS cp;
            if (SUCCEEDED(pDevice->GetCreationParameters(&cp))) {
                gameWindow = cp.hFocusWindow;
            }
        }

        if (!gameWindow) {
            Log("[INIT] ERRO: Não conseguiu encontrar HWND do jogo!");
            return oEndScene(pDevice);
        }

        Log("[INIT] HWND detectado: 0x%p", gameWindow);

        // Inicializa ImGui
        ImGui::CreateContext();
        ImGui::StyleColorsDark();

        ImGuiIO& io = ImGui::GetIO();
        io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
        io.IniFilename = NULL;
        io.MouseDrawCursor = false;  // Não desenha cursor (usa do sistema)

        ImGui_ImplWin32_Init(gameWindow);
        ImGui_ImplDX9_Init(pDevice);

        // Hook do WndProc para capturar input
        oWndProc = (WNDPROC)SetWindowLongPtr(gameWindow, GWLP_WNDPROC, (LONG_PTR)WndProcHook);

        inicializado = true;
        menu = false;
        Log("[INIT] === ImGui PRONTO! ===");
    }

    // Atualização de tempo local para medir frame
    clock_t frame_start_time_local = clock();

    ImGuiIO& io = ImGui::GetIO();

    if (menu) {
        POINT cursorPos;
        if (GetCursorPos(&cursorPos)) {
            ScreenToClient(gameWindow, &cursorPos);

            // Atualiza posição
            io.MousePos = ImVec2((float)cursorPos.x, (float)cursorPos.y);
        }
        else {
            io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
        }

        // Atualiza botões do mouse
        io.MouseDown[0] = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
        io.MouseDown[1] = (GetAsyncKeyState(VK_RBUTTON) & 0x8000) != 0;
        io.MouseDown[2] = (GetAsyncKeyState(VK_MBUTTON) & 0x8000) != 0;
    }

    // ========================================
    // TOGGLE DO MENU (F2)
    // ========================================
    static bool f2_pressed = false;
    if (GetAsyncKeyState(VK_F2) & 0x8000) {
        if (!f2_pressed) {
            f2_pressed = true;

            if (!menu) {
                // Abrindo menu
                menu = true;
                SaveCursorState();
                ShowCursorForMenu();
                // garante que ImGui esteja pronto a receber input
                ImGui::GetIO().WantCaptureMouse = true;
                ImGui::GetIO().WantCaptureKeyboard = true;

                render_paused = false;
                Log("[MENU] === MENU ABERTO ===");
            }
            else {
                // Fechando menu
                menu = false;

                // Restaurar o estado do cursor SALVO (não "ocultar" arbitrariamente)
                RestoreCursorState();

                // Invalida objetos do ImGui para evitar possíveis draw-residue/artefatos
                ImGui_ImplDX9_InvalidateDeviceObjects();

                // Limpa flags de captura
                ImGui::GetIO().WantCaptureMouse = false;
                ImGui::GetIO().WantCaptureKeyboard = false;

                // pula alguns frames sem render de menu para garantir limpeza visual
                skip_render_frames = 2;

                Log("[MENU] === MENU FECHADO ===");
            }
        }
    }
    else {
        f2_pressed = false;
    }

    // ========================================
    // SE MENU ESTÁ FECHADO, RETORNA RÁPIDO
    // ========================================
    if (!menu) {
        return oEndScene(pDevice);
    }

    // ========================================
    // RENDERIZA MENU (ImGui)
    // ========================================

    // Inicia frame do ImGui
    ImGui_ImplDX9_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    // === JANELA DO MENU ===
    ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver);
    ImGui::SetNextWindowSize(ImVec2(600, 500), ImGuiCond_FirstUseEver);

    if (ImGui::Begin("Bode Menu - SA-MP", &menu, ImGuiWindowFlags_NoCollapse)) {

        ImGui::TextColored(ImVec4(0, 1, 0, 1), "Menu Stealth - SA-MP");
        ImGui::Separator();

        // Mostra informações
        ImGui::Text("FPS: %.1f", io.Framerate);
        ImGui::Text("Posicao do Mouse: X=%.0f Y=%.0f", io.MousePos.x, io.MousePos.y);
        ImGui::Text("Cliques registrados: %d", click_count);

        ImGui::Spacing();
        ImGui::Separator();

        // Botão de teste
        if (ImGui::Button("TESTE CLIQUE", ImVec2(200, 50))) {
            click_count++;
            Beep(1000, 100);
            Log("[MENU] Botao clicado! Total: %d", click_count);
        }

        ImGui::Spacing();

        // Exemplos de widgets
        static bool checkbox_test = false;
        ImGui::Checkbox("Opcao de Teste", &checkbox_test);

        static float slider_test = 0.5f;
        ImGui::SliderFloat("Slider Teste", &slider_test, 0.0f, 1.0f);

        ImGui::Spacing();
        ImGui::TextColored(ImVec4(1, 1, 0, 1), "Pressione F2 para fechar");

        ImGui::End();
    }

    // Render ImGui
    ImGui::EndFrame();
    ImGui::Render();
    ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());

    // ========================================
    // CONTROLE DE FRAME TIME (OTIMIZAÇÃO) - medição local
    // ========================================
    clock_t frame_end_time_local = clock();
    double frame_ms = (double)(frame_end_time_local - frame_start_time_local) * 1000.0 / CLOCKS_PER_SEC;

    if (frame_ms > FRAME_TIME_BUDGET * 1.5) {
        static int slow_frame_count = 0;
        if (++slow_frame_count % 30 == 0) {
            Log("[PERF] Frame lento: %.2f ms (budget: %.2f ms)", frame_ms, FRAME_TIME_BUDGET);
        }
    }

    // Debug: detectar se outro módulo mudou o cursor (opcional)
    if (cursor_state_saved) {
        HCURSOR hCurNow = GetCursor();
        if (hCurNow != saved_cursor_state.hCursor) {
            Log("[CURSOR] Detectado cursor atual diferente do salvo: now=0x%p saved=0x%p", hCurNow, saved_cursor_state.hCursor);
        }
    }

    return oEndScene(pDevice);
}

// ============================================================================
// THREAD DE INICIALIZAÇÃO DO PLUGIN
// ============================================================================

DWORD WINAPI ThreadInicio(LPVOID) {
    Log("=== BODE PLUGIN - INICIANDO ===");

    // Aguarda carregamento do SAMP e D3D9
    int wait_count = 0;
    while (!GetModuleHandleA("samp.dll") && wait_count++ < 200) Sleep(50);
    wait_count = 0;
    while (!GetModuleHandleA("d3d9.dll") && wait_count++ < 200) Sleep(50);

    Log("SAMP e D3D9 carregados!");

    Sleep(2000);  // Aguarda jogo inicialisar

    // Inicializa MinHook
    if (MH_Initialize() != MH_OK) {
        Log("ERRO: Falha ao inicializar MinHook!");
        return 0;
    }

    // Cria device D3D9 temporário para hooking
    HWND hWnd = GetDesktopWindow();
    IDirect3D9* pD3D = Direct3DCreate9(D3D_SDK_VERSION);

    if (!pD3D) {
        Log("ERRO: Falha ao criar IDirect3D9!");
        MH_Uninitialize();
        return 0;
    }

    D3DPRESENT_PARAMETERS d3dpp = {};
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    d3dpp.BackBufferWidth = 1;
    d3dpp.BackBufferHeight = 1;

    IDirect3DDevice9* pDevice = nullptr;
    HRESULT hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, hWnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_DRIVER_MANAGEMENT,
        &d3dpp, &pDevice);

    if (FAILED(hr)) {
        hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
            D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDevice);
    }

    if (FAILED(hr)) {
        Log("ERRO ao criar D3D9 device: 0x%08X", hr);
        pD3D->Release();
        MH_Uninitialize();
        return 0;
    }

    // Obtém v-table
    void** vTable = *(void***)pDevice;
    void* pEndScene = vTable[42];      // EndScene index
    void* pReset = vTable[16];         // Reset index

    // Cria hooks
    if (MH_CreateHook(pEndScene, &HookedEndScene, (void**)&oEndScene) != MH_OK) {
        Log("ERRO: Falha ao criar hook EndScene!");
        pDevice->Release();
        pD3D->Release();
        MH_Uninitialize();
        return 0;
    }

    if (MH_CreateHook(pReset, &HookedReset, (void**)&oReset) != MH_OK) {
        Log("ERRO: Falha ao criar hook Reset!");
        pDevice->Release();
        pD3D->Release();
        MH_Uninitialize();
        return 0;
    }

    // Habilita hooks
    if (MH_EnableHook(MH_ALL_HOOKS) != MH_OK) {
        Log("ERRO: Falha ao ativar hooks!");
        pDevice->Release();
        pD3D->Release();
        MH_Uninitialize();
        return 0;
    }

    Log("Hooks instalados com sucesso!");
    Log("EndScene: 0x%p -> 0x%p", pEndScene, &HookedEndScene);
    Log("Reset: 0x%p -> 0x%p", pReset, &HookedReset);

    // Libera recursos temporários
    pDevice->Release();
    pD3D->Release();

    Log("=== PLUGIN PRONTO! Pressione F2 para abrir o menu ===");

    return 0;
}

// ============================================================================
// DLL MAIN
// ============================================================================

BOOL APIENTRY DllMain(HMODULE hMod, DWORD reason, LPVOID) {
    if (reason == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hMod);
        Log("=== PLUGIN CARREGADO ===");
        CreateThread(nullptr, 0, ThreadInicio, nullptr, 0, nullptr);
    }
    else if (reason == DLL_PROCESS_DETACH) {
        Log("=== PLUGIN DESCARREGANDO ===");

        // Restaura WndProc se alterado
        if (oWndProc && gameWindow) {
            SetWindowLongPtr(gameWindow, GWLP_WNDPROC, (LONG_PTR)oWndProc);
            oWndProc = nullptr;
        }

        // Cleanup ImGui se inicializado
        if (inicializado) {
            ImGui_ImplDX9_Shutdown();
            ImGui_ImplWin32_Shutdown();
            ImGui::DestroyContext();
            inicializado = false;
        }

        MH_DisableHook(MH_ALL_HOOKS);
        MH_Uninitialize();
        Log("=== PLUGIN DESCARREGADO ===");
    }
    return TRUE;
}
 
Решение

Winstаl

Известный
1,007
425
 

Похожие темы

  1. Ответы
    1
    Просмотры
    1K
  2. Ответы
    33
    Просмотры
    17K