diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-27 18:02:10 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-27 18:02:10 +0100 |
| commit | 0e9f3422e37d0b2d1344efec5ceb07c2246bfcd7 (patch) | |
| tree | a8aa0b5a4718f0e7d407a31135823523a77744b0 /src | |
| parent | Speedgraph: lower the graph (diff) | |
| download | hl2h-0e9f3422e37d0b2d1344efec5ceb07c2246bfcd7.tar.gz hl2h-0e9f3422e37d0b2d1344efec5ceb07c2246bfcd7.zip | |
GUI: add F2 preview shell with dummy controls
First vertical slice for the custom ISurface GUI:
- window/sidebar/content shell with Movement, Combat, Visuals, Weapons and
System categories;
- cards with dummy Active/Bind, collapsible settings, toggles, sliders,
buttons, bind capture, scrolling and tooltips;
- native OS cursor by releasing SDL relative mode; reasserted while open;
- F2 toggles the preview; Insert still opens the old text tree;
- while the GUI is open, do_move freezes movement/buttons/impulses/view and
skips combat features; ESP and the speed graph keep drawing.
Dummy controls do not touch the real config yet. The text menu is removed
after feature parity in the next pass.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gui.cpp | 1199 | ||||
| -rw-r--r-- | src/hl2h.cpp | 19 | ||||
| -rw-r--r-- | src/hl2h.h | 5 | ||||
| -rw-r--r-- | src/menu.cpp | 19 |
4 files changed, 1239 insertions, 3 deletions
diff --git a/src/gui.cpp b/src/gui.cpp new file mode 100644 index 0000000..74dd537 --- /dev/null +++ b/src/gui.cpp | |||
| @@ -0,0 +1,1199 @@ | |||
| 1 | // gui.cpp — custom immediate-mode GUI shell (temporary F2 preview) | ||
| 2 | // | ||
| 3 | // This is the first vertical slice: the visual shell, categories, cards, | ||
| 4 | // dummy controls, cursor handling and input freeze. It does not read or write | ||
| 5 | // the real feature settings yet; the next pass replaces the dummy state and | ||
| 6 | // removes the text menu. | ||
| 7 | |||
| 8 | #include "hl2h.h" | ||
| 9 | |||
| 10 | #include <atomic> | ||
| 11 | #include <cmath> | ||
| 12 | #include <cstdio> | ||
| 13 | #include <cstdlib> | ||
| 14 | #include <cstring> | ||
| 15 | #include <cwchar> | ||
| 16 | #include <dlfcn.h> | ||
| 17 | #include <time.h> | ||
| 18 | |||
| 19 | // --------------------------------------------------------------------------- | ||
| 20 | // ISurface drawing (same binary slots as menu.cpp / esp.cpp) | ||
| 21 | // --------------------------------------------------------------------------- | ||
| 22 | |||
| 23 | enum { | ||
| 24 | GS_DrawSetColor = 10, | ||
| 25 | GS_DrawFilledRect = 12, | ||
| 26 | GS_DrawOutlinedRect = 14, | ||
| 27 | GS_DrawLine = 15, | ||
| 28 | GS_DrawSetTextFont = 17, | ||
| 29 | GS_DrawSetTextColor = 18, | ||
| 30 | GS_DrawSetTextPos = 20, | ||
| 31 | GS_DrawPrintText = 22, | ||
| 32 | GS_GetScreenSize = 38, | ||
| 33 | GS_CreateFont = 66, | ||
| 34 | GS_AddGlyphSet = 67 | ||
| 35 | }; | ||
| 36 | |||
| 37 | static unsigned long g_font_body = 0; | ||
| 38 | static unsigned long g_font_title = 0; | ||
| 39 | static int g_body_h = 15; | ||
| 40 | static int g_title_h = 20; | ||
| 41 | static float g_font_scale = 0.0f; | ||
| 42 | static int g_scr_w = 0, g_scr_h = 0; | ||
| 43 | |||
| 44 | static void s_color(int r, int g, int b, int a) { | ||
| 45 | using Fn = void (*)(void *, int, int, int, int); | ||
| 46 | vfunc<Fn>(g_surface, GS_DrawSetColor)(g_surface, r, g, b, a); | ||
| 47 | } | ||
| 48 | static void s_fill(int x0, int y0, int x1, int y1) { | ||
| 49 | using Fn = void (*)(void *, int, int, int, int); | ||
| 50 | vfunc<Fn>(g_surface, GS_DrawFilledRect)(g_surface, x0, y0, x1, y1); | ||
| 51 | } | ||
| 52 | static void s_outline(int x0, int y0, int x1, int y1) { | ||
| 53 | using Fn = void (*)(void *, int, int, int, int); | ||
| 54 | vfunc<Fn>(g_surface, GS_DrawOutlinedRect)(g_surface, x0, y0, x1, y1); | ||
| 55 | } | ||
| 56 | static void s_line(int x0, int y0, int x1, int y1) { | ||
| 57 | using Fn = void (*)(void *, int, int, int, int); | ||
| 58 | vfunc<Fn>(g_surface, GS_DrawLine)(g_surface, x0, y0, x1, y1); | ||
| 59 | } | ||
| 60 | static void s_text_font(unsigned long f) { | ||
| 61 | using Fn = void (*)(void *, unsigned long); | ||
| 62 | vfunc<Fn>(g_surface, GS_DrawSetTextFont)(g_surface, f); | ||
| 63 | } | ||
| 64 | static void s_text_color(int r, int g, int b, int a) { | ||
| 65 | using Fn = void (*)(void *, int, int, int, int); | ||
| 66 | vfunc<Fn>(g_surface, GS_DrawSetTextColor)(g_surface, r, g, b, a); | ||
| 67 | } | ||
| 68 | static void s_text_pos(int x, int y) { | ||
| 69 | using Fn = void (*)(void *, int, int); | ||
| 70 | vfunc<Fn>(g_surface, GS_DrawSetTextPos)(g_surface, x, y); | ||
| 71 | } | ||
| 72 | static void s_print(const wchar_t *text, int n) { | ||
| 73 | using Fn = void (*)(void *, const wchar_t *, int, int); | ||
| 74 | vfunc<Fn>(g_surface, GS_DrawPrintText)(g_surface, text, n, 0); | ||
| 75 | } | ||
| 76 | static unsigned long s_create_font() { | ||
| 77 | using Fn = unsigned long (*)(void *); | ||
| 78 | return vfunc<Fn>(g_surface, GS_CreateFont)(g_surface); | ||
| 79 | } | ||
| 80 | static bool s_set_font(unsigned long font, const char *name, int tall, int weight, | ||
| 81 | int flags, int lo, int hi) { | ||
| 82 | using Fn = bool (*)(void *, unsigned long, const char *, int, int, int, int, | ||
| 83 | int, int, int); | ||
| 84 | return vfunc<Fn>(g_surface, GS_AddGlyphSet)(g_surface, font, name, tall, | ||
| 85 | weight, 0, 0, flags, lo, hi); | ||
| 86 | } | ||
| 87 | static void s_screen(int &w, int &h) { | ||
| 88 | using Fn = void (*)(void *, int &, int &); | ||
| 89 | vfunc<Fn>(g_surface, GS_GetScreenSize)(g_surface, w, h); | ||
| 90 | } | ||
| 91 | |||
| 92 | // --------------------------------------------------------------------------- | ||
| 93 | // SDL polling | ||
| 94 | // --------------------------------------------------------------------------- | ||
| 95 | |||
| 96 | using GetKeysFn = const unsigned char *(*)(int *); | ||
| 97 | using GetMouseFn = unsigned int (*)(int *, int *); | ||
| 98 | using ShowCursorFn = int (*)(int); | ||
| 99 | using SetRelativeFn = int (*)(int); | ||
| 100 | using GetRelativeFn = int (*)(void); | ||
| 101 | using AddWatchFn = int (*)(int (*)(void *, void *), void *); | ||
| 102 | using GetTicksFn = unsigned int (*)(void); | ||
| 103 | |||
| 104 | static GetKeysFn sd_get_keys = nullptr; | ||
| 105 | static GetMouseFn sd_get_mouse = nullptr; | ||
| 106 | static ShowCursorFn sd_show_cursor = nullptr; | ||
| 107 | static SetRelativeFn sd_set_relative = nullptr; | ||
| 108 | static GetRelativeFn sd_get_relative = nullptr; | ||
| 109 | static AddWatchFn sd_add_watch = nullptr; | ||
| 110 | static GetTicksFn sd_get_ticks = nullptr; | ||
| 111 | static bool g_sdl_ready = false; | ||
| 112 | static bool g_sdl_init_tried = false; | ||
| 113 | |||
| 114 | static std::atomic<int> g_wheel{0}; | ||
| 115 | static int sdl_event_watch(void *, void *event) { | ||
| 116 | if (!event) | ||
| 117 | return 0; | ||
| 118 | const unsigned char *p = (const unsigned char *)event; | ||
| 119 | unsigned int type = *(const unsigned int *)p; | ||
| 120 | if (type == 0x403) { // SDL_MOUSEWHEEL | ||
| 121 | int y = *(const int *)(p + 20); | ||
| 122 | g_wheel.fetch_add(y); | ||
| 123 | } | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | static void gui_init_sdl() { | ||
| 128 | if (g_sdl_init_tried) | ||
| 129 | return; | ||
| 130 | g_sdl_init_tried = true; | ||
| 131 | sd_get_keys = (GetKeysFn)dlsym(RTLD_DEFAULT, "SDL_GetKeyboardState"); | ||
| 132 | sd_get_mouse = (GetMouseFn)dlsym(RTLD_DEFAULT, "SDL_GetMouseState"); | ||
| 133 | sd_show_cursor = (ShowCursorFn)dlsym(RTLD_DEFAULT, "SDL_ShowCursor"); | ||
| 134 | sd_set_relative = (SetRelativeFn)dlsym(RTLD_DEFAULT, "SDL_SetRelativeMouseMode"); | ||
| 135 | sd_get_relative = (GetRelativeFn)dlsym(RTLD_DEFAULT, "SDL_GetRelativeMouseMode"); | ||
| 136 | sd_add_watch = (AddWatchFn)dlsym(RTLD_DEFAULT, "SDL_AddEventWatch"); | ||
| 137 | sd_get_ticks = (GetTicksFn)dlsym(RTLD_DEFAULT, "SDL_GetTicks"); | ||
| 138 | g_sdl_ready = sd_get_keys && sd_get_mouse; | ||
| 139 | if (!g_sdl_ready) { | ||
| 140 | logf("gui: SDL polling unavailable"); | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | if (sd_add_watch) | ||
| 144 | sd_add_watch(&sdl_event_watch, nullptr); | ||
| 145 | } | ||
| 146 | |||
| 147 | // --------------------------------------------------------------------------- | ||
| 148 | // Layout data (dummy shell) | ||
| 149 | // --------------------------------------------------------------------------- | ||
| 150 | |||
| 151 | struct Rect { | ||
| 152 | int x, y, w, h; | ||
| 153 | bool has(int px, int py) const { | ||
| 154 | return px >= x && py >= y && px < x + w && py < y + h; | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | struct Col { | ||
| 158 | int r, g, b, a; | ||
| 159 | }; | ||
| 160 | static const Col C_BG{17, 19, 23, 248}; | ||
| 161 | static const Col C_PANEL{27, 30, 36, 248}; | ||
| 162 | static const Col C_SIDEBAR{21, 23, 28, 248}; | ||
| 163 | static const Col C_HEADER{36, 40, 48, 255}; | ||
| 164 | static const Col C_BORDER{70, 76, 88, 255}; | ||
| 165 | static const Col C_TEXT{229, 232, 238, 255}; | ||
| 166 | static const Col C_DIM{150, 156, 168, 255}; | ||
| 167 | static const Col C_ACCENT{255, 145, 64, 255}; | ||
| 168 | static const Col C_ACCENT_DIM{120, 70, 32, 255}; | ||
| 169 | static const Col C_GOOD{86, 210, 135, 255}; | ||
| 170 | |||
| 171 | enum CtrlKind { | ||
| 172 | CK_TOGGLE, | ||
| 173 | CK_SLIDER, | ||
| 174 | CK_BIND, | ||
| 175 | CK_SECTION, | ||
| 176 | CK_INFO, | ||
| 177 | CK_BUTTON | ||
| 178 | }; | ||
| 179 | |||
| 180 | struct Ctrl { | ||
| 181 | int kind; | ||
| 182 | const char *label; | ||
| 183 | const char *desc; | ||
| 184 | int *value; | ||
| 185 | int min, max; | ||
| 186 | }; | ||
| 187 | |||
| 188 | struct Card { | ||
| 189 | int cat; | ||
| 190 | const char *title; | ||
| 191 | const char *desc; | ||
| 192 | bool expanded; | ||
| 193 | bool active; | ||
| 194 | int bind; | ||
| 195 | int c0, n; | ||
| 196 | }; | ||
| 197 | |||
| 198 | static Ctrl g_ctrls[192]; | ||
| 199 | static int g_nctrl = 0; | ||
| 200 | static Card g_cards[40]; | ||
| 201 | static int g_ncards = 0; | ||
| 202 | static int g_vals[192]; | ||
| 203 | static bool g_demo_built = false; | ||
| 204 | |||
| 205 | static const char *g_cats[] = {"Movement", "Combat", "Visuals", "Weapons", | ||
| 206 | "System"}; | ||
| 207 | static constexpr int CAT_COUNT = 5; | ||
| 208 | |||
| 209 | static int add_card(int cat, const char *title, const char *desc, | ||
| 210 | bool expanded) { | ||
| 211 | Card &c = g_cards[g_ncards]; | ||
| 212 | c.cat = cat; | ||
| 213 | c.title = title; | ||
| 214 | c.desc = desc; | ||
| 215 | c.expanded = expanded; | ||
| 216 | c.active = true; | ||
| 217 | c.bind = 0; | ||
| 218 | c.c0 = g_nctrl; | ||
| 219 | c.n = 0; | ||
| 220 | return g_ncards++; | ||
| 221 | } | ||
| 222 | static void add_ctrl(int card, int kind, const char *label, const char *desc, | ||
| 223 | int min, int max, int init) { | ||
| 224 | int i = g_nctrl++; | ||
| 225 | g_vals[i] = init; | ||
| 226 | g_ctrls[i].kind = kind; | ||
| 227 | g_ctrls[i].label = label; | ||
| 228 | g_ctrls[i].desc = desc; | ||
| 229 | g_ctrls[i].value = &g_vals[i]; | ||
| 230 | g_ctrls[i].min = min; | ||
| 231 | g_ctrls[i].max = max; | ||
| 232 | g_cards[card].n++; | ||
| 233 | } | ||
| 234 | static void add_toggle(int card, const char *label, const char *desc, | ||
| 235 | int init) { | ||
| 236 | add_ctrl(card, CK_TOGGLE, label, desc, 0, 1, init); | ||
| 237 | } | ||
| 238 | static void add_slider(int card, const char *label, const char *desc, int min, | ||
| 239 | int max, int init) { | ||
| 240 | add_ctrl(card, CK_SLIDER, label, desc, min, max, init); | ||
| 241 | } | ||
| 242 | static void add_section(int card, const char *label) { | ||
| 243 | add_ctrl(card, CK_SECTION, label, nullptr, 0, 0, 0); | ||
| 244 | } | ||
| 245 | static void add_info(int card, const char *label) { | ||
| 246 | add_ctrl(card, CK_INFO, label, nullptr, 0, 0, 0); | ||
| 247 | } | ||
| 248 | static void add_button(int card, const char *label, const char *desc) { | ||
| 249 | add_ctrl(card, CK_BUTTON, label, desc, 0, 0, 0); | ||
| 250 | } | ||
| 251 | |||
| 252 | static void build_demo() { | ||
| 253 | if (g_demo_built) | ||
| 254 | return; | ||
| 255 | g_demo_built = true; | ||
| 256 | |||
| 257 | int bhop = add_card(0, "Bunnyhop", "Automatic jump on landing.", true); | ||
| 258 | add_toggle(bhop, "Uni-directional auto-strafe", | ||
| 259 | "Rotate airborne movement into the velocity.", 1); | ||
| 260 | |||
| 261 | int abh = add_card(0, "ABH", "Accelerate on the jump tick by facing away " | ||
| 262 | "from the velocity.", true); | ||
| 263 | (void)abh; | ||
| 264 | |||
| 265 | int jb = add_card(0, "Jumpbug", "Duck+jump when landing would hurt.", true); | ||
| 266 | add_toggle(jb, "Assist timing", | ||
| 267 | "Nudge the final command into the unduck window.", 0); | ||
| 268 | |||
| 269 | int aim = add_card(1, "Aimbot", "Smoothly snap to a hostile target.", true); | ||
| 270 | g_cards[aim].bind = 604; | ||
| 271 | add_toggle(aim, "Vischeck", "Require world line of sight.", 1); | ||
| 272 | add_slider(aim, "FOV", "Maximum angular distance from the crosshair.", 1, 180, | ||
| 273 | 60); | ||
| 274 | add_slider(aim, "Smoothness", "Maximum degrees per tick.", 1, 45, 5); | ||
| 275 | add_toggle(aim, "Show FOV circle", "Draw the acquisition cone on screen.", 1); | ||
| 276 | add_section(aim, "Bones"); | ||
| 277 | add_toggle(aim, "Head", "Aim at the head hitbox.", 1); | ||
| 278 | add_toggle(aim, "Torso", "Aim at the chest/stomach.", 0); | ||
| 279 | add_toggle(aim, "Left arm", "Include the left arm.", 0); | ||
| 280 | add_toggle(aim, "Right arm", "Include the right arm.", 0); | ||
| 281 | add_toggle(aim, "Left leg", "Include the left leg.", 0); | ||
| 282 | add_toggle(aim, "Right leg", "Include the right leg.", 0); | ||
| 283 | |||
| 284 | int sil = add_card(1, "Silent aim", "Fire at the target without turning " | ||
| 285 | "the rendered view.", true); | ||
| 286 | g_cards[sil].bind = 602; | ||
| 287 | add_slider(sil, "FOV", "Maximum angular distance from the crosshair.", 1, 180, | ||
| 288 | 60); | ||
| 289 | |||
| 290 | int tb = add_card(1, "Triggerbot", "Fire when the crosshair is on a hostile.", | ||
| 291 | true); | ||
| 292 | g_cards[tb].bind = 603; | ||
| 293 | |||
| 294 | int melee = add_card(1, "Melee killbot", "360-degree crowbar silent aim.", | ||
| 295 | true); | ||
| 296 | |||
| 297 | int esp = add_card(2, "ESP", "Draw hostile NPC information.", true); | ||
| 298 | add_toggle(esp, "Show neutral", "Include neutral wildlife.", 1); | ||
| 299 | add_toggle(esp, "Show healthbar", "Draw a health bar.", 1); | ||
| 300 | add_toggle(esp, "Show healthnum", "Draw the numeric health value.", 1); | ||
| 301 | add_toggle(esp, "Show name", "Draw the model name.", 1); | ||
| 302 | add_toggle(esp, "Show skeleton", "Draw the animated bone links.", 1); | ||
| 303 | add_toggle(esp, "Show box", "Draw a bounding box.", 1); | ||
| 304 | add_toggle(esp, "Show speed graph", "Draw the lower-center speed graph.", 1); | ||
| 305 | |||
| 306 | int spread = add_card(3, "Spread control", | ||
| 307 | "Choose a server shot seed that tightens the cone.", | ||
| 308 | true); | ||
| 309 | add_toggle(spread, "No spread", | ||
| 310 | "Override the FireBullets seed with the best offset.", 1); | ||
| 311 | |||
| 312 | int sys = add_card(4, "System", "Configuration and maintenance.", true); | ||
| 313 | add_info(sys, "Config: ~/.config/hl2hake/config"); | ||
| 314 | add_info(sys, "Build: GUI shell preview"); | ||
| 315 | add_button(sys, "Reload config", "Re-read the INI without restarting."); | ||
| 316 | add_button(sys, "Reset defaults", "Restore feature defaults; GUI layout kept."); | ||
| 317 | |||
| 318 | (void)g_cards[melee]; | ||
| 319 | } | ||
| 320 | |||
| 321 | // --------------------------------------------------------------------------- | ||
| 322 | // Window state / input | ||
| 323 | // --------------------------------------------------------------------------- | ||
| 324 | |||
| 325 | static bool g_open = false; | ||
| 326 | static bool g_was_open = false; | ||
| 327 | static bool g_dragging = false; | ||
| 328 | static int g_drag_dx = 0, g_drag_dy = 0; | ||
| 329 | static int g_win_x = 0, g_win_y = 0, g_win_w = 0, g_win_h = 0; | ||
| 330 | static float g_scale = 1.0f; | ||
| 331 | static bool g_positioned = false; | ||
| 332 | static int g_cat = 0; | ||
| 333 | static float g_scroll = 0.0f; | ||
| 334 | static float g_scroll_max = 0.0f; | ||
| 335 | static bool g_scroll_drag = false; | ||
| 336 | static int g_scroll_drag_dy = 0; | ||
| 337 | static float g_scroll_drag_start = 0.0f; | ||
| 338 | static int g_hover_slider = -1; | ||
| 339 | static int g_slider_drag = -1; | ||
| 340 | static int g_capture_card = -1; | ||
| 341 | static QAngle g_frozen{0, 0, 0}; | ||
| 342 | static int g_mouse_x = 0, g_mouse_y = 0; | ||
| 343 | static bool g_mouse_click = false; | ||
| 344 | static bool g_mouse_down = false; | ||
| 345 | static bool g_mouse_rclick = false; | ||
| 346 | static unsigned int g_prev_mask = 0; | ||
| 347 | static unsigned char g_prev_keys[512] = {}; | ||
| 348 | static bool g_prev_cursor = true; | ||
| 349 | static bool g_prev_relative = true; | ||
| 350 | static double g_hover_since = 0.0; | ||
| 351 | static const char *g_hover_desc = nullptr; | ||
| 352 | static const char *g_hover_prev = nullptr; | ||
| 353 | static int g_hover_x = 0, g_hover_y = 0; | ||
| 354 | static int g_hover_last_mx = -1, g_hover_last_my = -1; | ||
| 355 | |||
| 356 | static int S(float v) { return (int)(v * g_scale + 0.5f); } | ||
| 357 | static float now_sec() { | ||
| 358 | struct timespec ts; | ||
| 359 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
| 360 | return (float)ts.tv_sec + (float)ts.tv_nsec * 1e-9f; | ||
| 361 | } | ||
| 362 | |||
| 363 | static void ensure_fonts() { | ||
| 364 | if (g_font_body && fabsf(g_font_scale - g_scale) < 0.001f) | ||
| 365 | return; | ||
| 366 | g_font_scale = g_scale; | ||
| 367 | g_body_h = (int)(15.0f * g_scale + 0.5f); | ||
| 368 | g_title_h = (int)(20.0f * g_scale + 0.5f); | ||
| 369 | if (g_body_h < 11) g_body_h = 11; | ||
| 370 | if (g_title_h < 15) g_title_h = 15; | ||
| 371 | static const char *families[] = {"Liberation Sans", "DejaVu Sans", | ||
| 372 | "Noto Sans", "Tahoma", "Verdana", "Arial", | ||
| 373 | nullptr}; | ||
| 374 | if (!g_font_body) { | ||
| 375 | for (int i = 0; families[i] && !g_font_body; i++) { | ||
| 376 | unsigned long f = s_create_font(); | ||
| 377 | if (!f) | ||
| 378 | break; | ||
| 379 | if (s_set_font(f, families[i], g_body_h, 400, 0x400, 0x20, 0xFF)) | ||
| 380 | g_font_body = f; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | if (!g_font_title) { | ||
| 384 | for (int i = 0; families[i] && !g_font_title; i++) { | ||
| 385 | unsigned long f = s_create_font(); | ||
| 386 | if (!f) | ||
| 387 | break; | ||
| 388 | if (s_set_font(f, families[i], g_title_h, 400, 0x400, 0x20, 0xFF)) | ||
| 389 | g_font_title = f; | ||
| 390 | } | ||
| 391 | } | ||
| 392 | if (!g_font_title) | ||
| 393 | g_font_title = g_font_body; | ||
| 394 | if (!g_font_body) | ||
| 395 | logf("gui: no usable body font"); | ||
| 396 | } | ||
| 397 | |||
| 398 | static int text_w(const char *s, int tall) { | ||
| 399 | if (!s) return 0; | ||
| 400 | return (int)(strlen(s) * tall * 0.52f + 0.5f); | ||
| 401 | } | ||
| 402 | |||
| 403 | static void draw_text_raw(int x, int y, const char *s, unsigned long font, | ||
| 404 | int tall) { | ||
| 405 | (void)tall; | ||
| 406 | if (!s || !font) | ||
| 407 | return; | ||
| 408 | wchar_t w[256]; | ||
| 409 | size_t n = mbstowcs(w, s, 255); | ||
| 410 | if (n == (size_t)-1) | ||
| 411 | return; | ||
| 412 | w[n] = 0; | ||
| 413 | s_text_font(font); | ||
| 414 | s_text_pos(x, y); | ||
| 415 | s_print(w, (int)n); | ||
| 416 | } | ||
| 417 | |||
| 418 | static void text_in(const Rect &clip, int x, int y, const char *s, | ||
| 419 | unsigned long font, int tall, int max_w, const Col &c) { | ||
| 420 | if (!s || !font) | ||
| 421 | return; | ||
| 422 | if (y < clip.y || y + tall > clip.y + clip.h) | ||
| 423 | return; | ||
| 424 | if (x >= clip.x + clip.w) | ||
| 425 | return; | ||
| 426 | if (x + text_w(s, tall) > clip.x + clip.w) | ||
| 427 | x = clip.x + clip.w - text_w(s, tall); | ||
| 428 | if (max_w > 0 && text_w(s, tall) > max_w) { | ||
| 429 | char buf[256]; | ||
| 430 | snprintf(buf, sizeof(buf), "%s", s); | ||
| 431 | int n = (int)strlen(buf); | ||
| 432 | while (n > 3 && text_w(buf, tall) > max_w) | ||
| 433 | buf[--n] = 0; | ||
| 434 | if (n > 3 && n < (int)sizeof(buf) - 3) { | ||
| 435 | buf[n++] = '.'; | ||
| 436 | buf[n++] = '.'; | ||
| 437 | buf[n++] = '.'; | ||
| 438 | buf[n] = 0; | ||
| 439 | } | ||
| 440 | s_text_color(c.r, c.g, c.b, c.a); | ||
| 441 | draw_text_raw(x, y, buf, font, tall); | ||
| 442 | return; | ||
| 443 | } | ||
| 444 | s_text_color(c.r, c.g, c.b, c.a); | ||
| 445 | draw_text_raw(x, y, s, font, tall); | ||
| 446 | } | ||
| 447 | |||
| 448 | static Rect clip_rect(const Rect &r, const Rect &clip) { | ||
| 449 | Rect o = r; | ||
| 450 | int x0 = r.x > clip.x ? r.x : clip.x; | ||
| 451 | int y0 = r.y > clip.y ? r.y : clip.y; | ||
| 452 | int x1 = (r.x + r.w) < (clip.x + clip.w) ? (r.x + r.w) : (clip.x + clip.w); | ||
| 453 | int y1 = (r.y + r.h) < (clip.y + clip.h) ? (r.y + r.h) : (clip.y + clip.h); | ||
| 454 | o.x = x0; | ||
| 455 | o.y = y0; | ||
| 456 | o.w = x1 - x0; | ||
| 457 | o.h = y1 - y0; | ||
| 458 | return o; | ||
| 459 | } | ||
| 460 | static void fill(const Rect &r, const Col &c, const Rect *clip = nullptr) { | ||
| 461 | if (r.w <= 0 || r.h <= 0) | ||
| 462 | return; | ||
| 463 | Rect q = clip ? clip_rect(r, *clip) : r; | ||
| 464 | if (q.w <= 0 || q.h <= 0) | ||
| 465 | return; | ||
| 466 | s_color(c.r, c.g, c.b, c.a); | ||
| 467 | s_fill(q.x, q.y, q.x + q.w, q.y + q.h); | ||
| 468 | } | ||
| 469 | static void outline(const Rect &r, const Col &c, const Rect *clip = nullptr) { | ||
| 470 | if (r.w <= 0 || r.h <= 0) | ||
| 471 | return; | ||
| 472 | Rect q = clip ? clip_rect(r, *clip) : r; | ||
| 473 | if (q.w <= 0 || q.h <= 0) | ||
| 474 | return; | ||
| 475 | s_color(c.r, c.g, c.b, c.a); | ||
| 476 | s_outline(q.x, q.y, q.x + q.w, q.y + q.h); | ||
| 477 | } | ||
| 478 | static void line(int x0, int y0, int x1, int y1, const Col &c) { | ||
| 479 | s_color(c.r, c.g, c.b, c.a); | ||
| 480 | s_line(x0, y0, x1, y1); | ||
| 481 | } | ||
| 482 | |||
| 483 | static const char *bind_label(int b) { | ||
| 484 | switch (b) { | ||
| 485 | case 0: return "None"; | ||
| 486 | case 600: return "MouseL"; | ||
| 487 | case 601: return "MouseR"; | ||
| 488 | case 602: return "MouseM"; | ||
| 489 | case 603: return "Mouse4"; | ||
| 490 | case 604: return "Mouse5"; | ||
| 491 | case 41: return "Esc"; | ||
| 492 | case 73: return "Insert"; | ||
| 493 | case 59: return "F2"; | ||
| 494 | default: return "Key"; | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | static void layout() { | ||
| 499 | int w = g_scr_w, h = g_scr_h; | ||
| 500 | if (g_surface) { | ||
| 501 | s_screen(w, h); | ||
| 502 | if (w > 0) g_scr_w = w; | ||
| 503 | if (h > 0) g_scr_h = h; | ||
| 504 | } | ||
| 505 | w = g_scr_w; | ||
| 506 | h = g_scr_h; | ||
| 507 | if (w <= 0 || h <= 0) | ||
| 508 | return; | ||
| 509 | float sc = fminf((float)w / 1920.0f, (float)h / 1080.0f); | ||
| 510 | if (sc < 0.75f) sc = 0.75f; | ||
| 511 | if (sc > 1.5f) sc = 1.5f; | ||
| 512 | g_scale = sc; | ||
| 513 | int nw = S(960.0f), nh = S(620.0f); | ||
| 514 | if (!g_positioned || nw != g_win_w || nh != g_win_h) { | ||
| 515 | if (!g_positioned) { | ||
| 516 | g_win_x = (w - nw) / 2; | ||
| 517 | g_win_y = (h - nh) / 2; | ||
| 518 | g_positioned = true; | ||
| 519 | } | ||
| 520 | g_win_w = nw; | ||
| 521 | g_win_h = nh; | ||
| 522 | } | ||
| 523 | if (g_win_x < 0) g_win_x = 0; | ||
| 524 | if (g_win_y < 0) g_win_y = 0; | ||
| 525 | if (g_win_x + g_win_w > w) g_win_x = w - g_win_w; | ||
| 526 | if (g_win_y + g_win_h > h) g_win_y = h - g_win_h; | ||
| 527 | } | ||
| 528 | |||
| 529 | static int row_h(const Ctrl &c) { | ||
| 530 | switch (c.kind) { | ||
| 531 | case CK_SECTION: return S(24.0f); | ||
| 532 | case CK_INFO: return S(20.0f); | ||
| 533 | case CK_BUTTON: return S(32.0f); | ||
| 534 | default: return S(27.0f); | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | static int body_h(const Card &c) { | ||
| 539 | if (!c.expanded) | ||
| 540 | return 0; | ||
| 541 | int h = S(6.0f); | ||
| 542 | for (int i = 0; i < c.n; i++) | ||
| 543 | h += row_h(g_ctrls[c.c0 + i]); | ||
| 544 | return h + S(6.0f); | ||
| 545 | } | ||
| 546 | |||
| 547 | static int card_h(const Card &c) { return S(36.0f) + body_h(c) + S(8.0f); } | ||
| 548 | |||
| 549 | static Rect card_rect(int index, int y) { | ||
| 550 | Card &c = g_cards[index]; | ||
| 551 | int x = g_win_x + S(190.0f) + S(10.0f); | ||
| 552 | int w = g_win_w - S(190.0f) - S(28.0f); | ||
| 553 | return Rect{x, y, w, card_h(c)}; | ||
| 554 | } | ||
| 555 | |||
| 556 | static bool card_in_category(int i) { return g_cards[i].cat == g_cat; } | ||
| 557 | |||
| 558 | static int content_height() { | ||
| 559 | int h = S(8.0f); | ||
| 560 | for (int i = 0; i < g_ncards; i++) | ||
| 561 | if (card_in_category(i)) | ||
| 562 | h += card_h(g_cards[i]); | ||
| 563 | return h; | ||
| 564 | } | ||
| 565 | |||
| 566 | // --------------------------------------------------------------------------- | ||
| 567 | // Input | ||
| 568 | // --------------------------------------------------------------------------- | ||
| 569 | |||
| 570 | static void close_gui() { | ||
| 571 | if (!g_open) | ||
| 572 | return; | ||
| 573 | g_open = false; | ||
| 574 | g_capture_card = -1; | ||
| 575 | g_dragging = false; | ||
| 576 | g_scroll_drag = false; | ||
| 577 | g_slider_drag = -1; | ||
| 578 | if (g_sdl_ready) { | ||
| 579 | if (sd_set_relative) | ||
| 580 | sd_set_relative(g_prev_relative ? 1 : 0); | ||
| 581 | if (sd_show_cursor) | ||
| 582 | sd_show_cursor(g_prev_cursor ? 1 : 0); | ||
| 583 | } | ||
| 584 | logf("gui: closed"); | ||
| 585 | } | ||
| 586 | |||
| 587 | static void open_gui() { | ||
| 588 | if (g_open) | ||
| 589 | return; | ||
| 590 | int w = 0, h = 0; | ||
| 591 | if (g_surface) { | ||
| 592 | s_screen(w, h); | ||
| 593 | if (w > 0) g_scr_w = w; | ||
| 594 | if (h > 0) g_scr_h = h; | ||
| 595 | } | ||
| 596 | if (!g_iface.engine) { | ||
| 597 | g_frozen = QAngle{0, 0, 0}; | ||
| 598 | } else { | ||
| 599 | using GetViewFn = void (*)(void *, QAngle *); | ||
| 600 | QAngle v{0, 0, 0}; | ||
| 601 | vfunc<GetViewFn>(g_iface.engine, 19)(g_iface.engine, &v); | ||
| 602 | g_frozen = v; | ||
| 603 | } | ||
| 604 | if (g_sdl_ready) { | ||
| 605 | if (sd_get_relative) | ||
| 606 | g_prev_relative = sd_get_relative() != 0; | ||
| 607 | if (sd_show_cursor) | ||
| 608 | g_prev_cursor = sd_show_cursor(-1) != 0; | ||
| 609 | if (sd_set_relative) | ||
| 610 | sd_set_relative(0); | ||
| 611 | if (sd_show_cursor) | ||
| 612 | sd_show_cursor(1); | ||
| 613 | } | ||
| 614 | g_open = true; | ||
| 615 | g_capture_card = -1; | ||
| 616 | g_wheel.exchange(0); | ||
| 617 | g_scroll = 0.0f; | ||
| 618 | g_cat = 0; | ||
| 619 | menu_force_close(); | ||
| 620 | logf("gui: opened"); | ||
| 621 | } | ||
| 622 | |||
| 623 | static void update_mouse() { | ||
| 624 | if (!g_sdl_ready) | ||
| 625 | return; | ||
| 626 | int mx = 0, my = 0; | ||
| 627 | unsigned int mask = sd_get_mouse(&mx, &my); | ||
| 628 | g_mouse_x = mx; | ||
| 629 | g_mouse_y = my; | ||
| 630 | g_mouse_down = (mask & 1u) != 0; | ||
| 631 | g_mouse_click = g_mouse_down && !(g_prev_mask & 1u); | ||
| 632 | g_mouse_rclick = ((mask & 2u) != 0) && !(g_prev_mask & 2u); | ||
| 633 | if (g_mouse_x < 0) g_mouse_x = 0; | ||
| 634 | if (g_mouse_y < 0) g_mouse_y = 0; | ||
| 635 | g_prev_mask = mask; | ||
| 636 | } | ||
| 637 | |||
| 638 | static void handle_capture(const unsigned char *keys) { | ||
| 639 | if (g_capture_card < 0) | ||
| 640 | return; | ||
| 641 | if (!g_open) { | ||
| 642 | g_capture_card = -1; | ||
| 643 | return; | ||
| 644 | } | ||
| 645 | if (keys[41]) { // Esc cancels | ||
| 646 | g_capture_card = -1; | ||
| 647 | return; | ||
| 648 | } | ||
| 649 | if (g_mouse_click) { | ||
| 650 | g_cards[g_capture_card].bind = 600; | ||
| 651 | g_capture_card = -1; | ||
| 652 | return; | ||
| 653 | } | ||
| 654 | if (g_mouse_rclick) { | ||
| 655 | g_cards[g_capture_card].bind = 601; | ||
| 656 | g_capture_card = -1; | ||
| 657 | return; | ||
| 658 | } | ||
| 659 | for (int k = 0; k < 512; k++) { | ||
| 660 | if (!keys[k] || g_prev_keys[k]) | ||
| 661 | continue; | ||
| 662 | if (k == 73 || k == 59) // Insert reserved; F2 closes the preview | ||
| 663 | continue; | ||
| 664 | g_cards[g_capture_card].bind = k; | ||
| 665 | g_capture_card = -1; | ||
| 666 | return; | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | static void handle_mouse_click() { | ||
| 671 | if (!g_mouse_click) | ||
| 672 | return; | ||
| 673 | int mx = g_mouse_x, my = g_mouse_y; | ||
| 674 | |||
| 675 | // title bar: drag or close | ||
| 676 | Rect title{g_win_x, g_win_y, g_win_w, S(38.0f)}; | ||
| 677 | Rect close{g_win_x + g_win_w - S(38.0f), g_win_y, S(38.0f), S(38.0f)}; | ||
| 678 | if (title.has(mx, my)) { | ||
| 679 | if (close.has(mx, my)) { | ||
| 680 | close_gui(); | ||
| 681 | return; | ||
| 682 | } | ||
| 683 | g_dragging = true; | ||
| 684 | g_drag_dx = mx - g_win_x; | ||
| 685 | g_drag_dy = my - g_win_y; | ||
| 686 | return; | ||
| 687 | } | ||
| 688 | |||
| 689 | // sidebar | ||
| 690 | int side_x = g_win_x; | ||
| 691 | int side_y = g_win_y + S(38.0f); | ||
| 692 | int side_w = S(190.0f); | ||
| 693 | int cat_h = S(34.0f); | ||
| 694 | for (int i = 0; i < CAT_COUNT; i++) { | ||
| 695 | Rect r{side_x, side_y + i * cat_h, side_w, cat_h}; | ||
| 696 | if (r.has(mx, my)) { | ||
| 697 | g_cat = i; | ||
| 698 | g_scroll = 0.0f; | ||
| 699 | return; | ||
| 700 | } | ||
| 701 | } | ||
| 702 | |||
| 703 | // content | ||
| 704 | Rect content{g_win_x + S(190.0f), g_win_y + S(38.0f), | ||
| 705 | g_win_w - S(190.0f), g_win_h - S(38.0f) - S(30.0f)}; | ||
| 706 | if (!content.has(mx, my)) | ||
| 707 | return; | ||
| 708 | |||
| 709 | // scrollbar | ||
| 710 | int sb_x = content.x + content.w - S(8.0f); | ||
| 711 | if (mx >= sb_x) { | ||
| 712 | if (g_scroll_max > 0.5f) { | ||
| 713 | int thumb_h = (int)((float)content.h * (float)content.h / | ||
| 714 | (float)(content.h + (int)g_scroll_max)); | ||
| 715 | if (thumb_h < S(24.0f)) thumb_h = S(24.0f); | ||
| 716 | int travel = content.h - thumb_h; | ||
| 717 | int thumb_y = content.y + (int)((g_scroll_max > 0.0f) | ||
| 718 | ? (g_scroll / g_scroll_max) * travel | ||
| 719 | : 0); | ||
| 720 | if (my >= thumb_y && my < thumb_y + thumb_h) { | ||
| 721 | g_scroll_drag = true; | ||
| 722 | g_scroll_drag_dy = my - thumb_y; | ||
| 723 | g_scroll_drag_start = g_scroll; | ||
| 724 | } else { | ||
| 725 | float t = (float)(my - content.y) / (float)content.h; | ||
| 726 | g_scroll = t * g_scroll_max; | ||
| 727 | } | ||
| 728 | } | ||
| 729 | return; | ||
| 730 | } | ||
| 731 | |||
| 732 | int y = content.y - (int)g_scroll + S(8.0f); | ||
| 733 | for (int i = 0; i < g_ncards; i++) { | ||
| 734 | if (!card_in_category(i)) | ||
| 735 | continue; | ||
| 736 | Card &c = g_cards[i]; | ||
| 737 | Rect cr = card_rect(i, y); | ||
| 738 | int ch = card_h(c); | ||
| 739 | if (my >= cr.y && my < cr.y + ch) { | ||
| 740 | // header | ||
| 741 | if (my < cr.y + S(36.0f)) { | ||
| 742 | if (c.cat != 4) { | ||
| 743 | Rect active{cr.x + cr.w - S(150.0f), cr.y + S(9.0f), S(18.0f), | ||
| 744 | S(18.0f)}; | ||
| 745 | Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f), | ||
| 746 | S(22.0f)}; | ||
| 747 | if (active.has(mx, my)) { | ||
| 748 | c.active = !c.active; | ||
| 749 | return; | ||
| 750 | } | ||
| 751 | if (bind.has(mx, my)) { | ||
| 752 | if (mx >= bind.x + bind.w - S(18.0f)) { | ||
| 753 | c.bind = 0; | ||
| 754 | return; | ||
| 755 | } | ||
| 756 | g_capture_card = i; | ||
| 757 | return; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | c.expanded = !c.expanded; | ||
| 761 | return; | ||
| 762 | } | ||
| 763 | // body | ||
| 764 | int by = cr.y + S(36.0f) + S(6.0f); | ||
| 765 | for (int j = 0; j < c.n; j++) { | ||
| 766 | Ctrl &ct = g_ctrls[c.c0 + j]; | ||
| 767 | int rh = row_h(ct); | ||
| 768 | if (my < by || my >= by + rh) { | ||
| 769 | by += rh; | ||
| 770 | continue; | ||
| 771 | } | ||
| 772 | if (ct.kind == CK_TOGGLE) { | ||
| 773 | *ct.value = !*ct.value; | ||
| 774 | return; | ||
| 775 | } | ||
| 776 | if (ct.kind == CK_SLIDER) { | ||
| 777 | Rect track{cr.x + S(16.0f) + S(230.0f), by + S(9.0f), | ||
| 778 | cr.w - S(16.0f) - S(230.0f) - S(64.0f), S(8.0f)}; | ||
| 779 | if (mx >= track.x && mx < track.x + track.w) { | ||
| 780 | float t = (float)(mx - track.x) / (float)track.w; | ||
| 781 | int v = ct.min + (int)(t * (ct.max - ct.min) + 0.5f); | ||
| 782 | if (v < ct.min) v = ct.min; | ||
| 783 | if (v > ct.max) v = ct.max; | ||
| 784 | *ct.value = v; | ||
| 785 | g_slider_drag = c.c0 + j; | ||
| 786 | } | ||
| 787 | return; | ||
| 788 | } | ||
| 789 | if (ct.kind == CK_BUTTON) { | ||
| 790 | Rect b{cr.x + cr.w - S(130.0f), by + S(3.0f), S(114.0f), | ||
| 791 | S(26.0f)}; | ||
| 792 | if (b.has(mx, my)) { | ||
| 793 | logf("gui: dummy action %s", ct.label ? ct.label : "?"); | ||
| 794 | return; | ||
| 795 | } | ||
| 796 | } | ||
| 797 | return; | ||
| 798 | } | ||
| 799 | } | ||
| 800 | y += ch; | ||
| 801 | } | ||
| 802 | } | ||
| 803 | |||
| 804 | static void handle_hover() { | ||
| 805 | g_hover_desc = nullptr; | ||
| 806 | g_hover_slider = -1; | ||
| 807 | int mx = g_mouse_x, my = g_mouse_y; | ||
| 808 | Rect content{g_win_x + S(190.0f), g_win_y + S(38.0f), | ||
| 809 | g_win_w - S(190.0f), g_win_h - S(38.0f) - S(30.0f)}; | ||
| 810 | if (!content.has(mx, my)) | ||
| 811 | return; | ||
| 812 | int y = content.y - (int)g_scroll + S(8.0f); | ||
| 813 | for (int i = 0; i < g_ncards; i++) { | ||
| 814 | if (!card_in_category(i)) | ||
| 815 | continue; | ||
| 816 | Card &c = g_cards[i]; | ||
| 817 | Rect cr = card_rect(i, y); | ||
| 818 | int ch = card_h(c); | ||
| 819 | if (my >= cr.y && my < cr.y + ch) { | ||
| 820 | if (my < cr.y + S(36.0f)) { | ||
| 821 | g_hover_desc = c.desc; | ||
| 822 | return; | ||
| 823 | } | ||
| 824 | int by = cr.y + S(36.0f) + S(6.0f); | ||
| 825 | for (int j = 0; j < c.n; j++) { | ||
| 826 | Ctrl &ct = g_ctrls[c.c0 + j]; | ||
| 827 | int rh = row_h(ct); | ||
| 828 | if (my >= by && my < by + rh) { | ||
| 829 | g_hover_desc = ct.desc; | ||
| 830 | if (ct.kind == CK_SLIDER) | ||
| 831 | g_hover_slider = c.c0 + j; | ||
| 832 | return; | ||
| 833 | } | ||
| 834 | by += rh; | ||
| 835 | } | ||
| 836 | return; | ||
| 837 | } | ||
| 838 | y += ch; | ||
| 839 | } | ||
| 840 | } | ||
| 841 | |||
| 842 | void gui_handle_input() { | ||
| 843 | gui_init_sdl(); | ||
| 844 | if (!g_surface) | ||
| 845 | return; | ||
| 846 | build_demo(); | ||
| 847 | layout(); | ||
| 848 | if (g_scr_w <= 0 || g_scr_h <= 0) | ||
| 849 | return; | ||
| 850 | ensure_fonts(); | ||
| 851 | if (!g_sdl_ready) | ||
| 852 | return; | ||
| 853 | |||
| 854 | int nk = 0; | ||
| 855 | const unsigned char *keys = sd_get_keys(&nk); | ||
| 856 | if (!keys) | ||
| 857 | return; | ||
| 858 | if (nk > 512) nk = 512; | ||
| 859 | update_mouse(); | ||
| 860 | |||
| 861 | bool f2 = (nk > 59) && keys[59]; | ||
| 862 | bool f2_edge = f2 && !g_prev_keys[59]; | ||
| 863 | bool esc = (nk > 41) && keys[41]; | ||
| 864 | bool esc_edge = esc && !g_prev_keys[41]; | ||
| 865 | bool left = (nk > 80) && keys[80]; | ||
| 866 | bool right = (nk > 79) && keys[79]; | ||
| 867 | bool left_edge = left && !g_prev_keys[80]; | ||
| 868 | bool right_edge = right && !g_prev_keys[79]; | ||
| 869 | |||
| 870 | if (f2_edge) { | ||
| 871 | if (g_open) | ||
| 872 | close_gui(); | ||
| 873 | else | ||
| 874 | open_gui(); | ||
| 875 | } | ||
| 876 | if (!g_open) { | ||
| 877 | memcpy(g_prev_keys, keys, nk); | ||
| 878 | g_was_open = false; | ||
| 879 | return; | ||
| 880 | } | ||
| 881 | |||
| 882 | if (esc_edge) { | ||
| 883 | if (g_capture_card >= 0) | ||
| 884 | g_capture_card = -1; | ||
| 885 | else | ||
| 886 | close_gui(); | ||
| 887 | } | ||
| 888 | |||
| 889 | // Keep the native cursor free even if the game re-grabs it. | ||
| 890 | if (sd_set_relative) | ||
| 891 | sd_set_relative(0); | ||
| 892 | if (sd_show_cursor) | ||
| 893 | sd_show_cursor(1); | ||
| 894 | |||
| 895 | handle_capture(keys); | ||
| 896 | |||
| 897 | if (g_dragging) { | ||
| 898 | if (!g_mouse_down) { | ||
| 899 | g_dragging = false; | ||
| 900 | } else { | ||
| 901 | g_win_x = g_mouse_x - g_drag_dx; | ||
| 902 | g_win_y = g_mouse_y - g_drag_dy; | ||
| 903 | if (g_win_x < 0) g_win_x = 0; | ||
| 904 | if (g_win_y < 0) g_win_y = 0; | ||
| 905 | if (g_win_x + g_win_w > g_scr_w) g_win_x = g_scr_w - g_win_w; | ||
| 906 | if (g_win_y + g_win_h > g_scr_h) g_win_y = g_scr_h - g_win_h; | ||
| 907 | } | ||
| 908 | } else { | ||
| 909 | handle_mouse_click(); | ||
| 910 | } | ||
| 911 | |||
| 912 | if (g_slider_drag >= 0) { | ||
| 913 | if (!g_mouse_down) { | ||
| 914 | g_slider_drag = -1; | ||
| 915 | } else { | ||
| 916 | Ctrl &ct = g_ctrls[g_slider_drag]; | ||
| 917 | Rect content{g_win_x + S(190.0f), g_win_y + S(38.0f), | ||
| 918 | g_win_w - S(190.0f), g_win_h - S(38.0f) - S(30.0f)}; | ||
| 919 | int x0 = g_win_x + S(190.0f) + S(16.0f) + S(230.0f); | ||
| 920 | int x1 = g_win_x + g_win_w - S(28.0f) - S(64.0f); | ||
| 921 | (void)content; | ||
| 922 | float t = (float)(g_mouse_x - x0) / (float)(x1 - x0); | ||
| 923 | int v = ct.min + (int)(t * (ct.max - ct.min) + 0.5f); | ||
| 924 | if (v < ct.min) v = ct.min; | ||
| 925 | if (v > ct.max) v = ct.max; | ||
| 926 | *ct.value = v; | ||
| 927 | } | ||
| 928 | } | ||
| 929 | |||
| 930 | if (g_scroll_drag) { | ||
| 931 | if (!g_mouse_down) { | ||
| 932 | g_scroll_drag = false; | ||
| 933 | } else { | ||
| 934 | Rect content{g_win_x + S(190.0f), g_win_y + S(38.0f), | ||
| 935 | g_win_w - S(190.0f), g_win_h - S(38.0f) - S(30.0f)}; | ||
| 936 | int thumb_h = (int)((float)content.h * (float)content.h / | ||
| 937 | (float)(content.h + (int)g_scroll_max)); | ||
| 938 | if (thumb_h < S(24.0f)) thumb_h = S(24.0f); | ||
| 939 | int travel = content.h - thumb_h; | ||
| 940 | if (travel > 0) { | ||
| 941 | float t = (float)(g_mouse_y - content.y - g_scroll_drag_dy) / | ||
| 942 | (float)travel; | ||
| 943 | g_scroll = t * g_scroll_max; | ||
| 944 | } | ||
| 945 | } | ||
| 946 | } | ||
| 947 | |||
| 948 | int wheel = g_wheel.exchange(0); | ||
| 949 | if (wheel != 0) | ||
| 950 | g_scroll -= wheel * S(44.0f); | ||
| 951 | |||
| 952 | if (left_edge || right_edge) { | ||
| 953 | if (g_hover_slider >= 0) { | ||
| 954 | Ctrl &ct = g_ctrls[g_hover_slider]; | ||
| 955 | *ct.value += right_edge ? 1 : -1; | ||
| 956 | if (*ct.value < ct.min) *ct.value = ct.min; | ||
| 957 | if (*ct.value > ct.max) *ct.value = ct.max; | ||
| 958 | } | ||
| 959 | } | ||
| 960 | |||
| 961 | int total = content_height(); | ||
| 962 | Rect content{g_win_x + S(190.0f), g_win_y + S(38.0f), | ||
| 963 | g_win_w - S(190.0f), g_win_h - S(38.0f) - S(30.0f)}; | ||
| 964 | g_scroll_max = (float)(total + S(8.0f) - content.h); | ||
| 965 | if (g_scroll_max < 0.0f) g_scroll_max = 0.0f; | ||
| 966 | if (g_scroll < 0.0f) g_scroll = 0.0f; | ||
| 967 | if (g_scroll > g_scroll_max) g_scroll = g_scroll_max; | ||
| 968 | |||
| 969 | handle_hover(); | ||
| 970 | { | ||
| 971 | double now = now_sec(); | ||
| 972 | int dx = abs(g_mouse_x - g_hover_last_mx); | ||
| 973 | int dy = abs(g_mouse_y - g_hover_last_my); | ||
| 974 | if (g_hover_desc != g_hover_prev || dx > 2 || dy > 2) { | ||
| 975 | g_hover_prev = g_hover_desc; | ||
| 976 | g_hover_last_mx = g_mouse_x; | ||
| 977 | g_hover_last_my = g_mouse_y; | ||
| 978 | g_hover_since = now; | ||
| 979 | g_hover_x = g_mouse_x; | ||
| 980 | g_hover_y = g_mouse_y; | ||
| 981 | } | ||
| 982 | } | ||
| 983 | |||
| 984 | memcpy(g_prev_keys, keys, nk); | ||
| 985 | g_was_open = true; | ||
| 986 | } | ||
| 987 | |||
| 988 | // --------------------------------------------------------------------------- | ||
| 989 | // Draw | ||
| 990 | // --------------------------------------------------------------------------- | ||
| 991 | |||
| 992 | static void draw_checkbox(int x, int y, bool on) { | ||
| 993 | Rect box{x, y, S(15.0f), S(15.0f)}; | ||
| 994 | fill(box, on ? C_ACCENT : Col{30, 34, 42, 255}); | ||
| 995 | outline(box, C_BORDER); | ||
| 996 | if (on) { | ||
| 997 | line(x + S(3.0f), y + S(8.0f), x + S(7.0f), y + S(12.0f), C_BG); | ||
| 998 | line(x + S(7.0f), y + S(12.0f), x + S(13.0f), y + S(4.0f), C_BG); | ||
| 999 | } | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | static void draw_slider(const Rect &clip, const Rect &cr, int y, const Ctrl &ct) { | ||
| 1003 | int track_x = cr.x + S(16.0f) + S(230.0f); | ||
| 1004 | int track_w = cr.w - S(16.0f) - S(230.0f) - S(64.0f); | ||
| 1005 | if (track_w < S(30.0f)) track_w = S(30.0f); | ||
| 1006 | Rect track{track_x, y + S(9.0f), track_w, S(8.0f)}; | ||
| 1007 | fill(track, Col{38, 42, 50, 255}, &clip); | ||
| 1008 | float t = (ct.max > ct.min) | ||
| 1009 | ? (float)(*ct.value - ct.min) / (float)(ct.max - ct.min) | ||
| 1010 | : 0.0f; | ||
| 1011 | int fw = (int)(track_w * t + 0.5f); | ||
| 1012 | fill(Rect{track.x, track.y, fw, track.h}, C_ACCENT_DIM, &clip); | ||
| 1013 | int kx = track.x + fw - S(4.0f); | ||
| 1014 | fill(Rect{kx, track.y - S(4.0f), S(8.0f), S(16.0f)}, C_ACCENT, &clip); | ||
| 1015 | char val[32]; | ||
| 1016 | snprintf(val, sizeof(val), "%d", *ct.value); | ||
| 1017 | text_in(clip, cr.x + cr.w - S(52.0f), y + S(4.0f), val, g_font_body, g_body_h, | ||
| 1018 | S(44.0f), C_TEXT); | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | static void draw_card(const Rect &clip, int index, int y) { | ||
| 1022 | Card &c = g_cards[index]; | ||
| 1023 | Rect cr = card_rect(index, y); | ||
| 1024 | Rect bg = clip_rect(cr, clip); | ||
| 1025 | if (bg.w <= 0 || bg.h <= 0) | ||
| 1026 | return; | ||
| 1027 | fill(cr, C_PANEL, &clip); | ||
| 1028 | outline(cr, Col{52, 57, 67, 255}, &clip); | ||
| 1029 | Rect head{cr.x, cr.y, cr.w, S(36.0f)}; | ||
| 1030 | fill(head, C_HEADER, &clip); | ||
| 1031 | // expand arrow | ||
| 1032 | int ax = cr.x + S(10.0f), ay = cr.y + S(13.0f); | ||
| 1033 | if (c.expanded) { | ||
| 1034 | line(ax, ay, ax + S(4.0f), ay + S(5.0f), C_DIM); | ||
| 1035 | line(ax + S(4.0f), ay + S(5.0f), ax + S(8.0f), ay, C_DIM); | ||
| 1036 | } else { | ||
| 1037 | line(ax, ay, ax + S(5.0f), ay + S(4.0f), C_DIM); | ||
| 1038 | line(ax + S(5.0f), ay + S(4.0f), ax, ay + S(8.0f), C_DIM); | ||
| 1039 | } | ||
| 1040 | text_in(clip, cr.x + S(24.0f), cr.y + S(8.0f), c.title, g_font_title, | ||
| 1041 | g_title_h, cr.w - S(180.0f), C_TEXT); | ||
| 1042 | if (c.cat != 4) { | ||
| 1043 | draw_checkbox(cr.x + cr.w - S(150.0f), cr.y + S(10.0f), c.active); | ||
| 1044 | text_in(clip, cr.x + cr.w - S(128.0f), cr.y + S(9.0f), | ||
| 1045 | c.active ? "On" : "Off", g_font_body, g_body_h, S(30.0f), | ||
| 1046 | c.active ? C_GOOD : C_DIM); | ||
| 1047 | Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f), S(22.0f)}; | ||
| 1048 | fill(bind, Col{45, 50, 60, 255}, &clip); | ||
| 1049 | outline(bind, C_BORDER, &clip); | ||
| 1050 | const char *bl = (g_capture_card == index) ? "Press..." : bind_label(c.bind); | ||
| 1051 | text_in(clip, bind.x + S(7.0f), bind.y + S(3.0f), bl, g_font_body, g_body_h, | ||
| 1052 | bind.w - S(22.0f), C_TEXT); | ||
| 1053 | text_in(clip, bind.x + bind.w - S(16.0f), bind.y + S(3.0f), "x", | ||
| 1054 | g_font_body, g_body_h, S(10.0f), C_DIM); | ||
| 1055 | } | ||
| 1056 | if (!c.expanded) | ||
| 1057 | return; | ||
| 1058 | |||
| 1059 | int by = cr.y + S(36.0f) + S(6.0f); | ||
| 1060 | for (int j = 0; j < c.n; j++) { | ||
| 1061 | Ctrl &ct = g_ctrls[c.c0 + j]; | ||
| 1062 | int rh = row_h(ct); | ||
| 1063 | if (ct.kind == CK_SECTION) { | ||
| 1064 | text_in(clip, cr.x + S(16.0f), by + S(3.0f), ct.label, g_font_body, | ||
| 1065 | g_body_h, cr.w - S(24.0f), C_ACCENT); | ||
| 1066 | } else if (ct.kind == CK_INFO) { | ||
| 1067 | text_in(clip, cr.x + S(16.0f), by + S(1.0f), ct.label, g_font_body, | ||
| 1068 | g_body_h, cr.w - S(24.0f), C_DIM); | ||
| 1069 | } else if (ct.kind == CK_BUTTON) { | ||
| 1070 | Rect b{cr.x + cr.w - S(130.0f), by + S(3.0f), S(114.0f), S(26.0f)}; | ||
| 1071 | fill(b, Col{45, 50, 60, 255}, &clip); | ||
| 1072 | outline(b, C_BORDER, &clip); | ||
| 1073 | text_in(clip, b.x + S(10.0f), b.y + S(4.0f), ct.label, g_font_body, | ||
| 1074 | g_body_h, b.w - S(16.0f), C_TEXT); | ||
| 1075 | } else { | ||
| 1076 | text_in(clip, cr.x + S(16.0f), by + S(4.0f), ct.label, g_font_body, | ||
| 1077 | g_body_h, S(220.0f), C_TEXT); | ||
| 1078 | if (ct.kind == CK_TOGGLE) { | ||
| 1079 | draw_checkbox(cr.x + cr.w - S(31.0f), by + S(5.0f), *ct.value != 0); | ||
| 1080 | text_in(clip, cr.x + cr.w - S(58.0f), by + S(3.0f), | ||
| 1081 | *ct.value ? "On" : "Off", g_font_body, g_body_h, S(26.0f), | ||
| 1082 | *ct.value ? C_GOOD : C_DIM); | ||
| 1083 | } else if (ct.kind == CK_SLIDER) { | ||
| 1084 | draw_slider(clip, cr, by, ct); | ||
| 1085 | } | ||
| 1086 | } | ||
| 1087 | by += rh; | ||
| 1088 | } | ||
| 1089 | } | ||
| 1090 | |||
| 1091 | static void draw_tooltip() { | ||
| 1092 | if (!g_hover_desc || !g_hover_desc[0]) | ||
| 1093 | return; | ||
| 1094 | float d = now_sec() - (float)g_hover_since; | ||
| 1095 | if (d < 0.4f) | ||
| 1096 | return; | ||
| 1097 | int tw = text_w(g_hover_desc, g_body_h) + S(16.0f); | ||
| 1098 | int maxw = S(380.0f); | ||
| 1099 | if (tw > maxw) tw = maxw; | ||
| 1100 | int th = g_body_h + S(14.0f); | ||
| 1101 | int x = g_hover_x + S(14.0f); | ||
| 1102 | int y = g_hover_y + S(16.0f); | ||
| 1103 | if (x + tw > g_scr_w - S(4.0f)) x = g_hover_x - tw - S(10.0f); | ||
| 1104 | if (y + th > g_scr_h - S(4.0f)) y = g_hover_y - th - S(10.0f); | ||
| 1105 | Rect r{x, y, tw, th}; | ||
| 1106 | fill(r, Col{12, 14, 18, 248}); | ||
| 1107 | outline(r, C_ACCENT_DIM); | ||
| 1108 | Rect inner{x + S(7.0f), y + S(6.0f), tw - S(14.0f), g_body_h}; | ||
| 1109 | text_in(inner, inner.x, inner.y, g_hover_desc, g_font_body, g_body_h, | ||
| 1110 | inner.w, C_TEXT); | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | void gui_draw() { | ||
| 1114 | if (!g_open || !g_surface) | ||
| 1115 | return; | ||
| 1116 | layout(); | ||
| 1117 | ensure_fonts(); | ||
| 1118 | int w = g_scr_w, h = g_scr_h; | ||
| 1119 | if (w <= 0 || h <= 0) | ||
| 1120 | return; | ||
| 1121 | Rect win{g_win_x, g_win_y, g_win_w, g_win_h}; | ||
| 1122 | fill(Rect{win.x + S(6.0f), win.y + S(6.0f), win.w, win.h}, | ||
| 1123 | Col{0, 0, 0, 100}); | ||
| 1124 | fill(win, C_BG); | ||
| 1125 | outline(win, C_BORDER); | ||
| 1126 | Rect title{win.x, win.y, win.w, S(38.0f)}; | ||
| 1127 | fill(title, Col{31, 35, 42, 255}); | ||
| 1128 | fill(Rect{win.x, win.y, S(4.0f), win.h}, C_ACCENT); | ||
| 1129 | text_in(win, win.x + S(16.0f), win.y + S(7.0f), "HL2hake", g_font_title, | ||
| 1130 | g_title_h, win.w - S(80.0f), C_TEXT); | ||
| 1131 | text_in(win, win.x + S(110.0f), win.y + S(10.0f), "GUI shell preview", | ||
| 1132 | g_font_body, g_body_h, win.w - S(180.0f), C_DIM); | ||
| 1133 | Rect close{win.x + win.w - S(38.0f), win.y, S(38.0f), S(38.0f)}; | ||
| 1134 | fill(close, Col{45, 48, 56, 255}); | ||
| 1135 | line(close.x + S(13.0f), close.y + S(12.0f), close.x + S(25.0f), | ||
| 1136 | close.y + S(25.0f), C_DIM); | ||
| 1137 | line(close.x + S(25.0f), close.y + S(12.0f), close.x + S(13.0f), | ||
| 1138 | close.y + S(25.0f), C_DIM); | ||
| 1139 | |||
| 1140 | Rect side{win.x, win.y + S(38.0f), S(190.0f), | ||
| 1141 | win.h - S(38.0f) - S(30.0f)}; | ||
| 1142 | fill(side, C_SIDEBAR); | ||
| 1143 | int cat_h = S(34.0f); | ||
| 1144 | for (int i = 0; i < CAT_COUNT; i++) { | ||
| 1145 | Rect r{side.x, side.y + i * cat_h, side.w, cat_h}; | ||
| 1146 | if (i == g_cat) { | ||
| 1147 | fill(r, Col{42, 45, 54, 255}); | ||
| 1148 | fill(Rect{r.x, r.y, S(3.0f), r.h}, C_ACCENT); | ||
| 1149 | } | ||
| 1150 | text_in(side, r.x + S(18.0f), r.y + S(8.0f), g_cats[i], g_font_body, | ||
| 1151 | g_body_h, r.w - S(24.0f), i == g_cat ? C_TEXT : C_DIM); | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | Rect content{win.x + S(190.0f), win.y + S(38.0f), | ||
| 1155 | win.w - S(190.0f), win.h - S(38.0f) - S(30.0f)}; | ||
| 1156 | fill(content, Col{20, 22, 27, 255}); | ||
| 1157 | Rect clip{content.x + S(4.0f), content.y + S(4.0f), content.w - S(20.0f), | ||
| 1158 | content.h - S(8.0f)}; | ||
| 1159 | int y = content.y - (int)g_scroll + S(8.0f); | ||
| 1160 | for (int i = 0; i < g_ncards; i++) { | ||
| 1161 | if (!card_in_category(i)) | ||
| 1162 | continue; | ||
| 1163 | Card &c = g_cards[i]; | ||
| 1164 | int ch = card_h(c); | ||
| 1165 | if (y + ch >= clip.y && y <= clip.y + clip.h) | ||
| 1166 | draw_card(clip, i, y); | ||
| 1167 | y += ch; | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | // scrollbar | ||
| 1171 | if (g_scroll_max > 0.5f) { | ||
| 1172 | Rect track{content.x + content.w - S(8.0f), content.y, S(8.0f), | ||
| 1173 | content.h}; | ||
| 1174 | fill(track, Col{30, 33, 40, 255}); | ||
| 1175 | int thumb_h = (int)((float)content.h * (float)content.h / | ||
| 1176 | (float)(content.h + (int)g_scroll_max)); | ||
| 1177 | if (thumb_h < S(24.0f)) thumb_h = S(24.0f); | ||
| 1178 | int travel = content.h - thumb_h; | ||
| 1179 | int thumb_y = content.y + (int)((g_scroll_max > 0.0f) | ||
| 1180 | ? (g_scroll / g_scroll_max) * travel | ||
| 1181 | : 0); | ||
| 1182 | fill(Rect{track.x, thumb_y, track.w, thumb_h}, C_ACCENT_DIM); | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | Rect foot{win.x, win.y + win.h - S(30.0f), win.w, S(30.0f)}; | ||
| 1186 | fill(foot, Col{24, 27, 33, 255}); | ||
| 1187 | line(foot.x, foot.y, foot.x + foot.w, foot.y, C_BORDER); | ||
| 1188 | text_in(foot, foot.x + S(12.0f), foot.y + S(6.0f), | ||
| 1189 | "config: ~/.config/hl2hake/config", g_font_body, g_body_h, | ||
| 1190 | foot.w - S(220.0f), C_DIM); | ||
| 1191 | text_in(foot, foot.x + foot.w - S(200.0f), foot.y + S(6.0f), | ||
| 1192 | g_capture_card >= 0 ? "press a key / Esc" : "F2 close | Insert text", | ||
| 1193 | g_font_body, g_body_h, S(190.0f), C_DIM); | ||
| 1194 | |||
| 1195 | draw_tooltip(); | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | bool gui_is_open() { return g_open; } | ||
| 1199 | QAngle gui_frozen_view() { return g_frozen; } | ||
diff --git a/src/hl2h.cpp b/src/hl2h.cpp index c4f8aad..c0c27af 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp | |||
| @@ -540,6 +540,25 @@ static void do_move(int seq, float frametime) { | |||
| 540 | bool inwater = (flags & FL_INWATER) != 0; | 540 | bool inwater = (flags & FL_INWATER) != 0; |
| 541 | const float *vel = (const float *)(e + g_net.velocity); | 541 | const float *vel = (const float *)(e + g_net.velocity); |
| 542 | 542 | ||
| 543 | if (gui_is_open()) { | ||
| 544 | QAngle f = gui_frozen_view(); | ||
| 545 | g_cam_view = Vector{f.x, f.y, f.z}; | ||
| 546 | cmd->viewangles[0] = f.x; | ||
| 547 | cmd->viewangles[1] = f.y; | ||
| 548 | cmd->viewangles[2] = f.z; | ||
| 549 | cmd->forwardmove = 0.0f; | ||
| 550 | cmd->sidemove = 0.0f; | ||
| 551 | cmd->upmove = 0.0f; | ||
| 552 | cmd->buttons = 0; | ||
| 553 | cmd->impulse = 0; | ||
| 554 | cmd->weaponselect = 0; | ||
| 555 | cmd->weaponsubtype = 0; | ||
| 556 | cmd->mousedx = 0; | ||
| 557 | cmd->mousedy = 0; | ||
| 558 | note_modified(cmd, flags, vel); | ||
| 559 | return; | ||
| 560 | } | ||
| 561 | |||
| 543 | bool bhop_on = feature_active(FEAT_BHOP); | 562 | bool bhop_on = feature_active(FEAT_BHOP); |
| 544 | bool abh_on = feature_active(FEAT_ABH); | 563 | bool abh_on = feature_active(FEAT_ABH); |
| 545 | 564 | ||
| @@ -230,6 +230,11 @@ bool head_position(int idx, Vector &out); | |||
| 230 | int aim_points(int idx, unsigned group_mask, Vector *out, int max); | 230 | int aim_points(int idx, unsigned group_mask, Vector *out, int max); |
| 231 | 231 | ||
| 232 | void outer_draw(); | 232 | void outer_draw(); |
| 233 | void menu_force_close(); | ||
| 234 | void gui_handle_input(); | ||
| 235 | void gui_draw(); | ||
| 236 | bool gui_is_open(); | ||
| 237 | QAngle gui_frozen_view(); | ||
| 233 | // ISurface pointer (defined in menu.cpp, used for drawing tests) | 238 | // ISurface pointer (defined in menu.cpp, used for drawing tests) |
| 234 | struct ISurface; | 239 | struct ISurface; |
| 235 | extern ISurface *g_surface; | 240 | extern ISurface *g_surface; |
diff --git a/src/menu.cpp b/src/menu.cpp index 9dffc9e..56fe6fa 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -625,9 +625,15 @@ int silentaim_fov() { return g_silent_fov; } | |||
| 625 | 625 | ||
| 626 | bool aimbot_fov_circle() { return g_aim_fov_circle; } | 626 | bool aimbot_fov_circle() { return g_aim_fov_circle; } |
| 627 | 627 | ||
| 628 | void menu_force_close() { | ||
| 629 | g_menu_open = false; | ||
| 630 | g_bind_mode = false; | ||
| 631 | g_insert_latch = false; | ||
| 632 | g_menu_draw_logged = false; | ||
| 633 | } | ||
| 634 | |||
| 628 | static void menu_handle_input() { | 635 | static void menu_handle_input() { |
| 629 | poll_keyboard(); | 636 | poll_keyboard(); |
| 630 | |||
| 631 | // SDL state can be sampled several times per rendered frame (both ISurface | 637 | // SDL state can be sampled several times per rendered frame (both ISurface |
| 632 | // hooks call outer_draw). Use a physical-key latch instead of the one-poll | 638 | // hooks call outer_draw). Use a physical-key latch instead of the one-poll |
| 633 | // edge so one press cannot open and close the menu in the same frame. | 639 | // edge so one press cannot open and close the menu in the same frame. |
| @@ -838,11 +844,13 @@ void draw_text_col(int x, int y, const char *text, int r, int g, int b) { | |||
| 838 | // (see hkdDrawPrintText hook in hl2h.cpp): we wrap our draws in | 844 | // (see hkdDrawPrintText hook in hl2h.cpp): we wrap our draws in |
| 839 | // PushMakeCurrent/PopMakeCurrent so they are submitted with a clean clip. | 845 | // PushMakeCurrent/PopMakeCurrent so they are submitted with a clean clip. |
| 840 | void outer_draw() { | 846 | void outer_draw() { |
| 841 | menu_handle_input(); | 847 | gui_handle_input(); |
| 848 | if (!gui_is_open()) | ||
| 849 | menu_handle_input(); | ||
| 842 | // HL2H_DRAW_TEST=1 draws a probe rect without needing the menu open | 850 | // HL2H_DRAW_TEST=1 draws a probe rect without needing the menu open |
| 843 | static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; | 851 | static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; |
| 844 | bool esp_on = feature_active(FEAT_ESP); | 852 | bool esp_on = feature_active(FEAT_ESP); |
| 845 | if (!g_surface || (!g_menu_open && !draw_test && !esp_on)) | 853 | if (!g_surface || (!g_menu_open && !gui_is_open() && !draw_test && !esp_on)) |
| 846 | return; | 854 | return; |
| 847 | 855 | ||
| 848 | // Level/menu -> game transitions can invalidate the VGUI font handle. | 856 | // Level/menu -> game transitions can invalidate the VGUI font handle. |
| @@ -896,6 +904,11 @@ void outer_draw() { | |||
| 896 | surf_draw_set_color(255, 255, 255, 255); | 904 | surf_draw_set_color(255, 255, 255, 255); |
| 897 | draw_text(28, 36, "HL2H DRAW TEST"); | 905 | draw_text(28, 36, "HL2H DRAW TEST"); |
| 898 | } | 906 | } |
| 907 | if (gui_is_open()) { | ||
| 908 | gui_draw(); | ||
| 909 | surf_pop_make_current(panel); | ||
| 910 | return; | ||
| 911 | } | ||
| 899 | if (!g_menu_open) { | 912 | if (!g_menu_open) { |
| 900 | surf_pop_make_current(panel); | 913 | surf_pop_make_current(panel); |
| 901 | return; | 914 | return; |
