Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
axstin
GitHub Repository: axstin/rbxfpsunlocker
Path: blob/master/Source/ui.cpp
259 views
1
#include <Windows.h>
2
#include <shellapi.h>
3
4
#include <cstdio>
5
#include <atomic>
6
7
#include "ui.h"
8
#include "resource.h"
9
#include "settings.h"
10
#include "rfu.h"
11
12
#define RFU_TRAYICON (WM_APP + 1)
13
#define RFU_TRAYMENU_APC (WM_APP + 2)
14
#define RFU_TRAYMENU_CONSOLE (WM_APP + 3)
15
#define RFU_TRAYMENU_EXIT (WM_APP + 4)
16
#define RFU_TRAYMENU_ALTENTERFIX (WM_APP + 5)
17
#define RFU_TRAYMENU_LOADSET (WM_APP + 6)
18
#define RFU_TRAYMENU_GITHUB (WM_APP + 7)
19
#define RFU_TRAYMENU_STUDIO (WM_APP + 8)
20
#define RFU_TRAYMENU_CFU (WM_APP + 9)
21
#define RFU_TRAYMENU_ADV_NBE (WM_APP + 10)
22
#define RFU_TRAYMENU_ADV_SE (WM_APP + 11)
23
#define RFU_TRAYMENU_ADV_QS (WM_APP + 12)
24
#define RFU_TRAYMENU_ADV_RFOC (WM_APP + 13)
25
#define RFU_TRAYMENU_CLIENT (WM_APP + 14)
26
27
#define RFU_TRAYMENU_UM (WM_APP + 15)
28
#define RFU_TRAYMENU_UM_HYBRID (RFU_TRAYMENU_UM + static_cast<uint32_t>(Settings::UnlockMethodType::Hybrid))
29
#define RFU_TRAYMENU_UM_MEMWRITE (RFU_TRAYMENU_UM + static_cast<uint32_t>(Settings::UnlockMethodType::MemoryWrite_Deprecated))
30
#define RFU_TRAYMENU_UM_FLAGSFILE (RFU_TRAYMENU_UM + static_cast<uint32_t>(Settings::UnlockMethodType::FlagsFile))
31
#define RFU_TRAYMENU_UM_LAST (RFU_TRAYMENU_UM + static_cast<uint32_t>(Settings::UnlockMethodType::Count) - 1)
32
33
#define RFU_FCS_FIRST (WM_APP + 30)
34
#define RFU_FCS_NONE RFU_FCS_FIRST
35
36
HWND UI::Window = NULL;
37
int UI::AttachedProcessesCount = 0;
38
bool UI::IsConsoleOnly = false;
39
std::atomic_int MessageBoxCount = 0;
40
41
HANDLE WatchThread;
42
NOTIFYICONDATA NotifyIconData;
43
44
int UI::Message(std::string_view message, std::string_view title, unsigned int option)
45
{
46
MessageBoxCount++;
47
// null window so a taskbar icon is shown. however, now our dialog is non-modal
48
auto result = MessageBoxA(NULL, message.data(), title.data(), option | MB_SETFOREGROUND);
49
MessageBoxCount--;
50
return result;
51
}
52
53
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
54
{
55
switch (uMsg)
56
{
57
case RFU_TRAYICON:
58
{
59
if (lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDOWN)
60
{
61
if (MessageBoxCount != 0)
62
break;
63
64
POINT position;
65
GetCursorPos(&position);
66
67
HMENU popup = CreatePopupMenu();
68
69
char buffer[64];
70
sprintf_s(buffer, "Attached Processes: %d", UI::AttachedProcessesCount);
71
72
AppendMenu(popup, MF_STRING | MF_GRAYED, RFU_TRAYMENU_APC, buffer);
73
AppendMenu(popup, MF_SEPARATOR, 0, NULL);
74
75
AppendMenu(popup, MF_STRING | (Settings::UnlockClient ? MF_CHECKED : 0), RFU_TRAYMENU_CLIENT, "Unlock Roblox Player");
76
AppendMenu(popup, MF_STRING | (Settings::UnlockStudio ? MF_CHECKED : 0), RFU_TRAYMENU_STUDIO, "Unlock Roblox Studio");
77
78
HMENU submenu = CreatePopupMenu();
79
AppendMenu(submenu, MF_STRING, RFU_FCS_NONE, "None");
80
for (size_t i = 0; i < Settings::FPSCapValues.size(); i++)
81
{
82
double value = Settings::FPSCapValues[i];
83
84
char name[16] = { 0 };
85
if (static_cast<int>(value) == value)
86
snprintf(name, sizeof(name), "%d", static_cast<int>(value));
87
else
88
snprintf(name, sizeof(name), "%.2f", value);
89
90
AppendMenu(submenu, MF_STRING, RFU_FCS_NONE + i + 1, name);
91
}
92
CheckMenuRadioItem(submenu, RFU_FCS_FIRST, RFU_FCS_FIRST + Settings::FPSCapValues.size(), RFU_FCS_FIRST + Settings::FPSCapSelection, MF_BYCOMMAND);
93
AppendMenu(popup, MF_POPUP, (UINT_PTR)submenu, "FPS Cap");
94
95
HMENU unlock_method = CreatePopupMenu();
96
AppendMenu(unlock_method, MF_STRING, RFU_TRAYMENU_UM_HYBRID, "Hybrid");
97
//AppendMenu(unlock_method, MF_STRING, RFU_TRAYMENU_UM_MEMWRITE, "Memory Write");
98
AppendMenu(unlock_method, MF_STRING, RFU_TRAYMENU_UM_FLAGSFILE, "Flags File");
99
CheckMenuRadioItem(unlock_method, RFU_TRAYMENU_UM, RFU_TRAYMENU_UM_LAST, RFU_TRAYMENU_UM + static_cast<uint32_t>(Settings::UnlockMethod), MF_BYCOMMAND);
100
AppendMenu(popup, MF_POPUP, (UINT_PTR)unlock_method, "Unlock Method");
101
102
AppendMenu(popup, MF_STRING | (Settings::AltEnterFix ? MF_CHECKED : 0), RFU_TRAYMENU_ALTENTERFIX, "Alt-Enter Fix");
103
104
AppendMenu(popup, MF_STRING | (Settings::CheckForUpdates ? MF_CHECKED : 0), RFU_TRAYMENU_CFU, "Check for Updates");
105
106
HMENU advanced = CreatePopupMenu();
107
AppendMenu(advanced, MF_STRING | (Settings::SilentErrors ? MF_CHECKED : 0), RFU_TRAYMENU_ADV_SE, "Silent Errors");
108
AppendMenu(advanced, MF_STRING | (Settings::SilentErrors ? MF_GRAYED : 0) | (Settings::NonBlockingErrors ? MF_CHECKED : 0), RFU_TRAYMENU_ADV_NBE, "Use Console Errors");
109
AppendMenu(advanced, MF_STRING | (Settings::QuickStart ? MF_CHECKED : 0), RFU_TRAYMENU_ADV_QS, "Quick Start");
110
AppendMenu(advanced, MF_STRING | (Settings::RevertFlagsOnClose ? MF_CHECKED : 0), RFU_TRAYMENU_ADV_RFOC, "Revert Flags on Close");
111
AppendMenu(popup, MF_POPUP, (UINT_PTR)advanced, "Advanced");
112
113
AppendMenu(popup, MF_SEPARATOR, 0, NULL);
114
AppendMenu(popup, MF_STRING, RFU_TRAYMENU_LOADSET, "Load Settings");
115
AppendMenu(popup, MF_STRING, RFU_TRAYMENU_CONSOLE, "Toggle Console");
116
AppendMenu(popup, MF_STRING, RFU_TRAYMENU_GITHUB, "Visit GitHub");
117
AppendMenu(popup, MF_STRING, RFU_TRAYMENU_EXIT, "Exit");
118
119
SetForegroundWindow(hwnd); // to allow "clicking away"
120
BOOL result = TrackPopupMenu(popup, TPM_RETURNCMD | TPM_TOPALIGN | TPM_LEFTALIGN, position.x, position.y, 0, hwnd, NULL);
121
122
if (result != 0 && MessageBoxCount == 0)
123
{
124
uint32_t event_flags{};
125
126
switch (result)
127
{
128
case RFU_TRAYMENU_EXIT:
129
RFU::OnEvent(RFU::Event::APP_EXIT);
130
Shell_NotifyIcon(NIM_DELETE, &NotifyIconData);
131
TerminateThread(WatchThread, 0);
132
FreeConsole();
133
PostQuitMessage(0);
134
break;
135
136
case RFU_TRAYMENU_CONSOLE:
137
UI::ToggleConsole();
138
break;
139
140
case RFU_TRAYMENU_GITHUB:
141
ShellExecuteA(NULL, "open", "https://github.com/" RFU_GITHUB_REPO, NULL, NULL, SW_SHOWNORMAL);
142
break;
143
144
case RFU_TRAYMENU_LOADSET:
145
Settings::Load();
146
event_flags |= RFU::Event::SETTINGS_MASK;
147
break;
148
149
case RFU_TRAYMENU_CLIENT:
150
Settings::UnlockClient = !Settings::UnlockClient;
151
CheckMenuItem(popup, RFU_TRAYMENU_CLIENT, Settings::UnlockClient ? MF_CHECKED : MF_UNCHECKED);
152
event_flags |= RFU::Event::UNLOCK_CLIENT;
153
break;
154
155
case RFU_TRAYMENU_STUDIO:
156
Settings::UnlockStudio = !Settings::UnlockStudio;
157
CheckMenuItem(popup, RFU_TRAYMENU_STUDIO, Settings::UnlockStudio ? MF_CHECKED : MF_UNCHECKED);
158
event_flags |= RFU::Event::UNLOCK_STUDIO;
159
break;
160
161
case RFU_TRAYMENU_CFU:
162
Settings::CheckForUpdates = !Settings::CheckForUpdates;
163
CheckMenuItem(popup, RFU_TRAYMENU_CFU, Settings::CheckForUpdates ? MF_CHECKED : MF_UNCHECKED);
164
break;
165
166
case RFU_TRAYMENU_ALTENTERFIX:
167
Settings::AltEnterFix = !Settings::AltEnterFix;
168
CheckMenuItem(popup, RFU_TRAYMENU_ALTENTERFIX, Settings::AltEnterFix ? MF_CHECKED : MF_UNCHECKED);
169
event_flags |= RFU::Event::ALT_ENTER;
170
break;
171
172
case RFU_TRAYMENU_ADV_NBE:
173
Settings::NonBlockingErrors = !Settings::NonBlockingErrors;
174
CheckMenuItem(popup, RFU_TRAYMENU_ADV_NBE, Settings::NonBlockingErrors ? MF_CHECKED : MF_UNCHECKED);
175
break;
176
177
case RFU_TRAYMENU_ADV_SE:
178
Settings::SilentErrors = !Settings::SilentErrors;
179
CheckMenuItem(popup, RFU_TRAYMENU_ADV_SE, Settings::SilentErrors ? MF_CHECKED : MF_UNCHECKED);
180
if (Settings::SilentErrors) CheckMenuItem(popup, RFU_TRAYMENU_ADV_NBE, MF_GRAYED);
181
break;
182
183
case RFU_TRAYMENU_ADV_QS:
184
Settings::QuickStart = !Settings::QuickStart;
185
CheckMenuItem(popup, RFU_TRAYMENU_ADV_QS, Settings::QuickStart ? MF_CHECKED : MF_UNCHECKED);
186
break;
187
188
case RFU_TRAYMENU_ADV_RFOC:
189
Settings::RevertFlagsOnClose = !Settings::RevertFlagsOnClose;
190
CheckMenuItem(popup, RFU_TRAYMENU_ADV_RFOC, Settings::RevertFlagsOnClose ? MF_CHECKED : MF_UNCHECKED);
191
break;
192
193
default:
194
if (result >= RFU_FCS_FIRST
195
&& result <= RFU_FCS_FIRST + Settings::FPSCapValues.size())
196
{
197
Settings::FPSCapSelection = result - RFU_FCS_FIRST;
198
Settings::FPSCap = Settings::FPSCapSelection == 0 ? 0.0 : Settings::FPSCapValues[Settings::FPSCapSelection - 1];
199
event_flags |= RFU::Event::FPS_CAP;
200
}
201
else if (result >= RFU_TRAYMENU_UM
202
&& result <= RFU_TRAYMENU_UM_LAST)
203
{
204
const auto before = Settings::UnlockMethod;
205
Settings::UnlockMethod = static_cast<Settings::UnlockMethodType>(result - RFU_TRAYMENU_UM);
206
if (Settings::UnlockMethod != before)
207
{
208
event_flags |= RFU::Event::UNLOCK_METHOD;
209
}
210
}
211
}
212
213
if (event_flags != 0)
214
{
215
RFU::OnEvent(event_flags);
216
}
217
218
if (result != RFU_TRAYMENU_CONSOLE
219
&& result != RFU_TRAYMENU_LOADSET
220
&& result != RFU_TRAYMENU_GITHUB
221
&& result != RFU_TRAYMENU_EXIT)
222
{
223
Settings::Save();
224
}
225
}
226
227
return 1;
228
}
229
230
break;
231
}
232
default:
233
return DefWindowProc(hwnd, uMsg, wParam, lParam);
234
}
235
236
return 0;
237
}
238
239
bool IsConsoleVisible = false;
240
241
void UI::SetConsoleVisible(bool visible)
242
{
243
IsConsoleVisible = visible;
244
ShowWindow(GetConsoleWindow(), visible ? SW_SHOWNORMAL : SW_HIDE);
245
}
246
247
void UI::CreateHiddenConsole()
248
{
249
AllocConsole();
250
251
freopen("CONOUT$", "w", stdout);
252
freopen("CONIN$", "r", stdin);
253
254
if (!UI::IsConsoleOnly)
255
{
256
HMENU menu = GetSystemMenu(GetConsoleWindow(), FALSE);
257
EnableMenuItem(menu, SC_CLOSE, MF_GRAYED);
258
}
259
260
#ifdef _WIN64
261
SetConsoleTitleA("Roblox FPS Unlocker " RFU_VERSION " (64-bit) Console");
262
#else
263
SetConsoleTitleA("Roblox FPS Unlocker " RFU_VERSION " (32-bit) Console");
264
#endif
265
266
SetConsoleVisible(false);
267
}
268
269
bool UI::ToggleConsole()
270
{
271
if (!GetConsoleWindow())
272
UI::CreateHiddenConsole();
273
274
SetConsoleVisible(!IsConsoleVisible);
275
276
return IsConsoleVisible;
277
}
278
279
int UI::Start(HINSTANCE instance, LPTHREAD_START_ROUTINE watchthread)
280
{
281
WNDCLASSEX wcex;
282
283
wcex.cbSize = sizeof(wcex);
284
wcex.style = 0;
285
wcex.lpfnWndProc = WindowProc;
286
wcex.cbClsExtra = 0;
287
wcex.cbWndExtra = 0;
288
wcex.hInstance = instance;
289
wcex.hIcon = NULL;
290
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
291
wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
292
wcex.lpszMenuName = NULL;
293
wcex.lpszClassName = "RFUClass";
294
wcex.hIconSm = NULL;
295
296
RegisterClassEx(&wcex);
297
298
UI::Window = CreateWindow("RFUClass", "Roblox FPS Unlocker", 0, 0, 0, 0, 0, NULL, NULL, instance, NULL);
299
if (!UI::Window)
300
return 0;
301
302
NotifyIconData.cbSize = sizeof(NotifyIconData);
303
NotifyIconData.hWnd = UI::Window;
304
NotifyIconData.uID = IDI_ICON1;
305
NotifyIconData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
306
NotifyIconData.uCallbackMessage = RFU_TRAYICON;
307
NotifyIconData.hIcon = LoadIcon(instance, MAKEINTRESOURCE(IDI_ICON1));
308
strcpy_s(NotifyIconData.szTip, "Roblox FPS Unlocker");
309
310
Shell_NotifyIcon(NIM_ADD, &NotifyIconData);
311
312
WatchThread = CreateThread(NULL, 0, watchthread, NULL, NULL, NULL);
313
314
BOOL ret;
315
MSG msg;
316
317
while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0)
318
{
319
if (ret != -1)
320
{
321
TranslateMessage(&msg);
322
DispatchMessage(&msg);
323
}
324
}
325
326
return msg.wParam;
327
}
328
329