From eda5e44d3b7f0a98b516ef1c6bf550f2174c74f2 Mon Sep 17 00:00:00 2001 From: jacob Date: Sat, 31 Jan 2026 18:36:17 -0600 Subject: [PATCH] raw mouse look. cursor hiding & clipping --- src/base/base_controller.h | 4 +- src/pp/pp_vis/pp_vis_core.c | 221 ++++++++++++++----------- src/window/window.h | 2 + src/window/window_win32/window_win32.c | 146 +++++++++++++--- src/window/window_win32/window_win32.h | 2 + 5 files changed, 255 insertions(+), 120 deletions(-) diff --git a/src/base/base_controller.h b/src/base/base_controller.h index e2b4bb1f..495e4240 100644 --- a/src/base/base_controller.h +++ b/src/base/base_controller.h @@ -142,10 +142,10 @@ Struct(ControllerEvent) u32 text_codepoint; // ControllerEventKind_CursorMove - Vec2I32 cursor_pos; + Vec2 cursor_pos; // ControllerEventKind_MouseMove - Vec2I32 mouse_delta; + Vec2 mouse_delta; }; Struct(ControllerEventsArray) diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 95bfe2f8..72a6e2e4 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -569,7 +569,7 @@ void V_TickForever(WaveLaneCtx *lane) frame->is_editing = prev_frame->is_editing; frame->ui_debug = prev_frame->ui_debug; frame->show_console = prev_frame->show_console; - frame->look = NormRot(prev_frame->look); + frame->look = prev_frame->look; frame->edit_mode = prev_frame->edit_mode; frame->equipped_tile = prev_frame->equipped_tile; frame->edit_camera_pos = prev_frame->edit_camera_pos; @@ -653,6 +653,7 @@ void V_TickForever(WaveLaneCtx *lane) UI_Frame *ui_frame = UI_BeginFrame(ui_frame_flags); WND_Frame window_frame = ui_frame->window_frame; WND_SetCursor(window_frame, WND_CursorKind_Default); + WND_PushCmd(window_frame, .kind = WND_CmdKind_SetLockedCursor, .v = 0); // Restore window { @@ -734,80 +735,150 @@ void V_TickForever(WaveLaneCtx *lane) b32 has_mouse_focus = UI_IsKeyNil(ui_frame->hot_box) || UI_MatchKey(ui_frame->hot_box, vis_box); b32 has_keyboard_focus = 1; - if (!window_frame.has_focus) + Vec2 mouse_delta = Zi; { - has_mouse_focus = 0; - has_keyboard_focus = 0; - } - - for (Button btn = 0; btn < countof(frame->held_buttons); ++btn) - { - if (btn == Button_M1 || btn == Button_M2 || btn == Button_M3) + if (!window_frame.has_focus) { - if (!has_mouse_focus) - { - frame->held_buttons[btn] = 0; - } + has_mouse_focus = 0; + has_keyboard_focus = 0; } - else - { - if (!has_keyboard_focus) - { - frame->held_buttons[btn] = 0; - } - } - } - for (u64 i = 0; i < window_frame.controller_events.count; ++i) - { - ControllerEvent cev = window_frame.controller_events.events[i]; - b32 down = cev.kind == ControllerEventKind_ButtonDown; - b32 up = cev.kind == ControllerEventKind_ButtonUp; - - b32 ignore = 0; - if (down) + //- Reset held buttons + for (Button btn = 0; btn < countof(frame->held_buttons); ++btn) { - if (cev.button == Button_M1 || cev.button == Button_M2 || cev.button == Button_M3) + if (btn == Button_M1 || btn == Button_M2 || btn == Button_M3) { if (!has_mouse_focus) { - ignore = 1; + frame->held_buttons[btn] = 0; } } else { if (!has_keyboard_focus) { - ignore = 1; + frame->held_buttons[btn] = 0; } } } - if (!ignore) + //- Convert events into cmds + for (u64 cev_idx = 0; cev_idx < window_frame.controller_events.count; ++cev_idx) { - V_Hotkey hotkey = Zi; - hotkey.button = cev.button; - hotkey.ctrl = frame->held_buttons[Button_Ctrl]; - hotkey.shift = frame->held_buttons[Button_Shift]; - hotkey.alt = frame->held_buttons[Button_Alt]; + ControllerEvent cev = window_frame.controller_events.events[cev_idx]; + b32 down = cev.kind == ControllerEventKind_ButtonDown; + b32 up = cev.kind == ControllerEventKind_ButtonUp; + + b32 ignore = 0; + if (down) { - u64 hotkey_hash = HashString(StringFromStruct(&hotkey)); - V_ShortcutBin *bin = &shortcut_bins[hotkey_hash % shortcut_bins_count]; - V_Shortcut *shortcut = bin->first; - for (; shortcut; shortcut = shortcut->next_in_bin) + if (cev.button == Button_M1 || cev.button == Button_M2 || cev.button == Button_M3) { - if (shortcut->hotkey_hash == hotkey_hash && MatchStruct(&shortcut->hotkey, &hotkey)) + if (!has_mouse_focus) { - break; + ignore = 1; } } - if (shortcut != 0 && down) + else { - V_PushVisCmd(shortcut->cmd_name); + if (!has_keyboard_focus) + { + ignore = 1; + } } } - frame->held_buttons[hotkey.button] = down; + + if (!ignore) + { + V_Hotkey hotkey = Zi; + hotkey.button = cev.button; + hotkey.ctrl = frame->held_buttons[Button_Ctrl]; + hotkey.shift = frame->held_buttons[Button_Shift]; + hotkey.alt = frame->held_buttons[Button_Alt]; + { + u64 hotkey_hash = HashString(StringFromStruct(&hotkey)); + V_ShortcutBin *bin = &shortcut_bins[hotkey_hash % shortcut_bins_count]; + V_Shortcut *shortcut = bin->first; + for (; shortcut; shortcut = shortcut->next_in_bin) + { + if (shortcut->hotkey_hash == hotkey_hash && MatchStruct(&shortcut->hotkey, &hotkey)) + { + break; + } + } + if (shortcut != 0 && down) + { + V_PushVisCmd(shortcut->cmd_name); + } + } + frame->held_buttons[hotkey.button] = down; + } } + + //- Compute mouse delta from events + for (u64 cev_idx = 0; cev_idx < window_frame.controller_events.count; ++cev_idx) + { + ControllerEvent cev = window_frame.controller_events.events[cev_idx]; + if (cev.kind == ControllerEventKind_MouseMove) + { + mouse_delta = AddVec2(mouse_delta, cev.mouse_delta); + } + } + } + + ////////////////////////////// + //- Compute movement & look + + if (!frame->is_editing && !frame->palette.is_showing) + { + WND_PushCmd(window_frame, .kind = WND_CmdKind_SetLockedCursor, .v = 1); + WND_SetCursor(window_frame, WND_CursorKind_Hidden); + } + + { + Vec2 move = Zi; + { + if (frame->held_buttons[Button_A]) move.x -= 1; + if (frame->held_buttons[Button_D]) move.x += 1; + if (frame->held_buttons[Button_W]) move.y -= 1; + if (frame->held_buttons[Button_S]) move.y += 1; + } + move = ClampVec2Len(move, 1); + f32 fire_held = frame->held_buttons[Button_M1]; + Vec2 look = Zi; + f32 fire_presses = fire_held && !prev_frame->held_buttons[Button_M1]; + { + // Vec2 center = P_WorldShapeFromEnt(local_guy).centroid; + // look = SubVec2(frame->world_cursor, center); + + // TODO: Adjustable sensitivity + f32 mouse_scale_factor = 0.005; + + look = frame->look; + look = AddVec2(look, MulVec2(mouse_delta, mouse_scale_factor)); + } + if (frame->is_editing) + { + if (!frame->is_panning) + { + f32 edit_move_speed = 20.0 * MaxF32(frame->edit_camera_zoom, min_zoom); + frame->edit_camera_pos = AddVec2(frame->edit_camera_pos, MulVec2(move, edit_move_speed * frame->dt)); + } + + // FIXME: Remove this + frame->move = prev_frame->move; + frame->look = prev_frame->look; + frame->fire_held = prev_frame->fire_held; + frame->fire_presses = prev_frame->fire_presses; + } + else + { + frame->move = move; + frame->look = look; + frame->fire_held = fire_held; + frame->fire_presses = fire_presses; + } + frame->look = frame->look; } ////////////////////////////// @@ -858,9 +929,9 @@ void V_TickForever(WaveLaneCtx *lane) P_Ent *guy = P_EntFromKey(local_world->last_frame, player->guy); Vec2 guy_center = P_WorldShapeFromEnt(guy).centroid; Vec2 screen_center = MulVec2(frame->screen_dims, 0.5); - Vec2 look = MulAffineBasisVec2(prev_frame->af.screen_to_world, SubVec2(ui_frame->cursor_pos, screen_center)); + // Vec2 look = MulAffineBasisVec2(prev_frame->af.screen_to_world, SubVec2(ui_frame->cursor_pos, screen_center)); target_camera_pos = guy_center; - target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(look, look_ratio)); + target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(frame->look, look_ratio)); target_camera_zoom = 1; } target_camera_pos.x = ClampF32(target_camera_pos.x, -world_pitch / 2, world_pitch / 2); @@ -1268,50 +1339,6 @@ void V_TickForever(WaveLaneCtx *lane) } } - ////////////////////////////// - //- Compute movement & look - - { - Vec2 move = Zi; - { - if (frame->held_buttons[Button_A]) move.x -= 1; - if (frame->held_buttons[Button_D]) move.x += 1; - if (frame->held_buttons[Button_W]) move.y -= 1; - if (frame->held_buttons[Button_S]) move.y += 1; - } - move = ClampVec2Len(move, 1); - f32 fire_held = frame->held_buttons[Button_M1]; - Vec2 look = Zi; - f32 fire_presses = fire_held && !prev_frame->held_buttons[Button_M1]; - { - Vec2 center = P_WorldShapeFromEnt(local_guy).centroid; - look = SubVec2(frame->world_cursor, center); - } - if (frame->is_editing) - { - if (!frame->is_panning) - { - f32 edit_move_speed = 20.0 * MaxF32(frame->edit_camera_zoom, min_zoom); - frame->edit_camera_pos = AddVec2(frame->edit_camera_pos, MulVec2(move, edit_move_speed * frame->dt)); - } - - // FIXME: Remove this - frame->move = prev_frame->move; - frame->look = prev_frame->look; - frame->fire_held = prev_frame->fire_held; - frame->fire_presses = prev_frame->fire_presses; - - } - else - { - frame->move = move; - frame->look = look; - frame->fire_held = fire_held; - frame->fire_presses = fire_presses; - } - frame->look = NormRot(frame->look); - } - ////////////////////////////// //- Determine local simulation bounds // @@ -1369,7 +1396,7 @@ void V_TickForever(WaveLaneCtx *lane) control.tick = control_tick; control.orig_tick = control_tick; control.move = frame->move; - control.look = NormRot(frame->look); + control.look = frame->look; control.fire_held = frame->fire_held; // FIXME: Don't propagate fire presses over multiple sim frames control.fire_presses = frame->fire_presses; @@ -1964,7 +1991,11 @@ void V_TickForever(WaveLaneCtx *lane) Vec2 line_end = AddVec2(line_start, fire_dir); P_DebugDrawLine(line_start, line_end, Color_Yellow); - Vec2 cross = fire_pos; + // Vec2 cross = fire_pos; + // Vec2 cross = MulAffineVec2(frame->af.screen_to_world, frame->look); + + Vec2 cross = AddVec2(ent->xf.t, frame->look); + P_DebugDrawPoint(cross, Color_Red); } diff --git a/src/window/window.h b/src/window/window.h index 94ff0541..ee29829e 100644 --- a/src/window/window.h +++ b/src/window/window.h @@ -12,6 +12,7 @@ Struct(WND_Handle) Enum(WND_CursorKind) { WND_CursorKind_Default, + WND_CursorKind_Hidden, WND_CursorKind_Text, WND_CursorKind_No, WND_CursorKind_Hand, @@ -35,6 +36,7 @@ Enum(WND_CmdKind) WND_CmdKind_SetFullscreen, WND_CmdKind_SetForcedTop, WND_CmdKind_SetCursor, + WND_CmdKind_SetLockedCursor, WND_CmdKind_Restore, }; diff --git a/src/window/window_win32/window_win32.c b/src/window/window_win32/window_win32.c index ae7314ca..e14823b6 100644 --- a/src/window/window_win32/window_win32.c +++ b/src/window/window_win32/window_win32.c @@ -226,17 +226,33 @@ LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l } break; //- Cursor kind - case WM_SETCURSOR: { if ((HWND)wparam == hwnd && LOWORD(lparam) == HTCLIENT) { HCURSOR desired_cursor = (HCURSOR)Atomic64Fetch(&window->desired_cursor); + b32 desired_cursor_hidden = !Atomic64Fetch(&window->desired_cursor_hidden); if (desired_cursor != window->active_cursor) { SetCursor(desired_cursor); window->active_cursor = desired_cursor; } + if (desired_cursor_hidden != window->active_cursor_hidden) + { + if (desired_cursor_hidden) + { + while (ShowCursor(1) < 0) + { + } + } + else + { + while (ShowCursor(0) >= 0) + { + } + } + window->active_cursor_hidden = desired_cursor_hidden; + } result = 1; } else @@ -392,7 +408,7 @@ LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l i32 y = GET_Y_LPARAM(lparam); ControllerEvent event = Zi; event.kind = ControllerEventKind_CursorMove; - event.cursor_pos = VEC2I32(x, y); + event.cursor_pos = VEC2(x, y); WND_W32_PushEvent(window, event); } break; @@ -402,25 +418,26 @@ LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM l TempArena scratch = BeginScratchNoConflict(); { // Read raw input buffer - UINT buff_size; + UINT buff_size = 0; GetRawInputData((HRAWINPUT)lparam, RID_INPUT, 0, &buff_size, sizeof(RAWINPUTHEADER)); u8 *buff = PushStructs(scratch.arena, u8, buff_size); - if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, buff, &buff_size, sizeof(RAWINPUTHEADER)) != buff_size) + if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, buff, &buff_size, sizeof(RAWINPUTHEADER)) == buff_size) + { + RAWINPUT raw = Zi; + CopyBytes(&raw, buff, sizeof(RAWINPUT)); + if (raw.header.dwType == RIM_TYPEMOUSE) + { + i32 x = raw.data.mouse.lLastX; + i32 y = raw.data.mouse.lLastY; + ControllerEvent event = Zi; + event.kind = ControllerEventKind_MouseMove; + event.mouse_delta = VEC2(x, y); + WND_W32_PushEvent(window, event); + } + } + else { LogErrorF("GetRawInputData did not return correct size"); - break; - } - RAWINPUT raw = Zi; - CopyBytes(&raw, buff, sizeof(RAWINPUT)); - - if (raw.header.dwType == RIM_TYPEMOUSE) - { - i32 x = raw.data.mouse.lLastX; - i32 y = raw.data.mouse.lLastY; - ControllerEvent event = Zi; - event.kind = ControllerEventKind_MouseMove; - event.mouse_delta = VEC2I32(x, y); - WND_W32_PushEvent(window, event); } } EndScratch(scratch); @@ -579,6 +596,8 @@ void WND_EndFrame(WND_Frame frame, i32 vsync) // Process cmds b32 was_restored = 0; + b32 desires_locked_cursor = 0; + b32 desires_hidden_cursor = 0; HCURSOR desired_cursor = (HCURSOR)Atomic64Fetch(&window->desired_cursor); for (WND_W32_CmdNode *n = window->first_cmd; n; n = n->next) { @@ -649,10 +668,23 @@ void WND_EndFrame(WND_Frame frame, i32 vsync) SetWindowPos(hwnd, window->is_forced_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } break; - //- Cursor + //- Set cursor case WND_CmdKind_SetCursor: { - desired_cursor = WND_W32.cursors[cmd.cursor]; + if (cmd.cursor == WND_CursorKind_Hidden) + { + desires_hidden_cursor = 1; + } + else + { + desired_cursor = WND_W32.cursors[cmd.cursor]; + } + } break; + + //- Set locked cursor + case WND_CmdKind_SetLockedCursor: + { + desires_locked_cursor = !!cmd.v; } break; //- Restore @@ -715,12 +747,80 @@ void WND_EndFrame(WND_Frame frame, i32 vsync) BringWindowToTop(hwnd); } - // Set cursor + // Set/Clip/Hide cursor { - HCURSOR old_desired_cursor = (HCURSOR)Atomic64FetchSet(&window->desired_cursor, (i64)desired_cursor); - if (old_desired_cursor != desired_cursor) + RECT virtual_screen_rect = Zi; { - PostMessage(window->hwnd, WM_SETCURSOR, (WPARAM)window->hwnd, (LPARAM)HTCLIENT); + virtual_screen_rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN); + virtual_screen_rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN); + virtual_screen_rect.right = virtual_screen_rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN); + virtual_screen_rect.bottom = virtual_screen_rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN); + } + RECT client_screen_rect = Zi; + { + GetClientRect(hwnd, (LPRECT)&client_screen_rect); + ClientToScreen(hwnd, (LPPOINT)&client_screen_rect.left); + ClientToScreen(hwnd, (LPPOINT)&client_screen_rect.right); + } + b32 is_cursor_in_client_rect = 0; + { + POINT pos = Zi; + GetCursorPos(&pos); + is_cursor_in_client_rect = ( + pos.x >= client_screen_rect.left && + pos.x < client_screen_rect.right && + pos.y >= client_screen_rect.top && + pos.y < client_screen_rect.bottom + ); + } + RECT clipped_screen_rect = Zi; + { + GetClipCursor(&clipped_screen_rect); + } + + b32 is_cursor_clipped_any = !MatchStruct(&clipped_screen_rect, &virtual_screen_rect); + b32 is_cursor_clipped_client = MatchStruct(&clipped_screen_rect, &client_screen_rect); + + b32 final_desires_locked_cursor = desires_locked_cursor && frame.has_focus && is_cursor_in_client_rect; + + // Clip cursor + { + if (final_desires_locked_cursor && !is_cursor_clipped_client) + { + RECT rect = Zi; + { + GetClientRect(hwnd, (LPRECT)&rect); + ClientToScreen(hwnd, (LPPOINT)&rect.left); + ClientToScreen(hwnd, (LPPOINT)&rect.right); + } + ClipCursor(&rect); + } + else if (!final_desires_locked_cursor && is_cursor_clipped_any) + { + ClipCursor(0); + } + } + + if (frame.has_focus) + { + // Set cursor + { + HCURSOR old_desired_cursor = (HCURSOR)Atomic64FetchSet(&window->desired_cursor, (i64)desired_cursor); + if (old_desired_cursor != desired_cursor) + { + PostMessage(window->hwnd, WM_SETCURSOR, (WPARAM)window->hwnd, (LPARAM)HTCLIENT); + } + } + + // Hide cursor + { + b32 final_desires_hidden_cursor = desires_hidden_cursor && is_cursor_in_client_rect; + b32 old_desired_cursor_hidden = !!Atomic64FetchSet(&window->desired_cursor_hidden, final_desires_hidden_cursor); + if (old_desired_cursor_hidden != final_desires_hidden_cursor) + { + PostMessage(window->hwnd, WM_SETCURSOR, (WPARAM)window->hwnd, (LPARAM)HTCLIENT); + } + } } } diff --git a/src/window/window_win32/window_win32.h b/src/window/window_win32/window_win32.h index d4b9396d..93716fce 100644 --- a/src/window/window_win32/window_win32.h +++ b/src/window/window_win32/window_win32.h @@ -11,6 +11,7 @@ Struct(WND_W32_Window) // Window proc state u16 previous_utf16_high_surrogate; HCURSOR active_cursor; + b32 active_cursor_hidden; // User state Arena *frame_arena; @@ -27,6 +28,7 @@ Struct(WND_W32_Window) // User -> Window proc Atomic64 desired_cursor; + Atomic64 desired_cursor_hidden; }; ////////////////////////////////////////////////////////////