-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathProgram.cs
More file actions
323 lines (277 loc) · 9.78 KB
/
Program.cs
File metadata and controls
323 lines (277 loc) · 9.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
using DiscordRPC;
using DiscordRPC.Logging;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Drawing;
using Console = Colorful.Console;
// Configuration settings
using static ConfigSettings;
// ClientID and settings
using static ConfigValues;
// Memory stuff
using static Utils;
public static class Program
{
// Initialize a new Rich Presence client
public static DiscordRpcClient _Client;
// Configuration file location (in AppData to avoid permission issues)
public static readonly string ConfigPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"FLStudioRPC",
"fls_rpc_config.json"
);
// System tray icon
private static NotifyIcon _trayIcon;
// Mutex for single-instance checking
private static Mutex _mutex;
// Initialize a placeholder, because we don't want null reference exceptions
private static RichPresence _RPC = new RichPresence()
{
Details = "",
State = "",
Assets = new Assets()
{
LargeImageKey = "fl_studio_logo",
}
};
static void InitializeRPC()
{
// Create a new Rich Presence client, set the client ID, and most importantly, disable the autoEvents so we can update manually
_Client = new DiscordRpcClient(ClientID, -1, null, false);
// Don't update the presence if it didn't change
_Client.SkipIdenticalPresence = true;
// Use a file logger instead of console logger since we're running as WinExe
_Client.Logger = new DiscordRPC.Logging.FileLogger("discord_rpc_log.txt")
{
Level = DiscordRPC.Logging.LogLevel.Warning
};
// Register events
_Client.OnReady += Events.OnReady;
_Client.OnClose += Events.OnClose;
_Client.OnError += Events.OnError;
_Client.OnConnectionEstablished += Events.OnConnectionEstablished;
_Client.OnConnectionFailed += Events.OnConnectionFailed;
_Client.OnPresenceUpdate += Events.OnPresenceUpdate;
// Initialize the client
_Client.Initialize();
}
static void InitializeTrayIcon()
{
// Load the custom icon
Icon customIcon = null;
try
{
string iconPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FLStudioRPC.ico");
if (File.Exists(iconPath))
{
customIcon = new Icon(iconPath);
}
}
catch
{
// Fall back to system icon if loading fails
}
_trayIcon = new NotifyIcon()
{
Icon = customIcon ?? SystemIcons.Application,
Visible = true,
Text = "FL Studio Discord RPC"
};
// Create context menu
var contextMenu = new ContextMenuStrip();
// Secret mode toggle
var secretModeItem = new ToolStripMenuItem("Secret Mode (Hide Project Name)");
secretModeItem.Checked = SecretMode;
secretModeItem.Click += (s, e) =>
{
SecretMode = !SecretMode;
secretModeItem.Checked = SecretMode;
// Save the current setting to config
SaveCurrentConfig(ConfigPath);
};
contextMenu.Items.Add(secretModeItem);
contextMenu.Items.Add(new ToolStripSeparator());
// Auto-startup toggle
var autoStartItem = new ToolStripMenuItem("Start with Windows");
autoStartItem.Checked = IsAutoStartEnabled();
autoStartItem.Click += (s, e) =>
{
bool currentState = IsAutoStartEnabled();
SetAutoStart(!currentState);
autoStartItem.Checked = !currentState;
};
contextMenu.Items.Add(autoStartItem);
contextMenu.Items.Add(new ToolStripSeparator());
// About
var aboutItem = new ToolStripMenuItem("About");
aboutItem.Click += (s, e) =>
{
System.Diagnostics.Process.Start("https://github.com/zfi2/FL-Studio-Discord-RPC");
};
contextMenu.Items.Add(aboutItem);
// Exit
var exitItem = new ToolStripMenuItem("Exit");
exitItem.Click += (s, e) =>
{
_trayIcon.Visible = false;
_Client?.Dispose();
Application.Exit();
};
contextMenu.Items.Add(exitItem);
_trayIcon.ContextMenuStrip = contextMenu;
}
static bool IsAutoStartEnabled()
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", false))
{
return key?.GetValue("FLStudioRPC") != null;
}
}
catch
{
return false;
}
}
static void SetAutoStart(bool enable)
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true))
{
if (enable)
{
string exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
key?.SetValue("FLStudioRPC", $"\"{exePath}\"");
}
else
{
key?.DeleteValue("FLStudioRPC", false);
}
}
}
catch
{
// Silently fail if registry access denied
}
}
static void RunRPCLoop()
{
// Save default config with default values (also load it at startup, the function is already called in SaveConfig)
SaveConfig(ConfigPath);
bool wasRunning = false;
// Continuous monitoring loop
while (true)
{
try
{
// Retrieve the FL Studio data constantly
FLInfo FLStudioData = GetFLInfo();
// Check if FL Studio is running
bool isRunning = !string.IsNullOrEmpty(FLStudioData.AppName) || !string.IsNullOrEmpty(FLStudioData.ProjectName);
if (isRunning)
{
// FL Studio is running
if (!wasRunning)
{
// FL Studio just started - initialize RPC
InitializeRPC();
// Initialize timestamp if enabled
if (ShowTimestamp)
{
_RPC.Timestamps = new Timestamps()
{
Start = DateTime.UtcNow
};
}
wasRunning = true;
}
// Update presence with current FL Studio info
_RPC.Details = FLStudioData.AppName;
_RPC.State = FLStudioData.ProjectName ?? "Empty project";
// Check if secret mode is enabled
if (SecretMode)
_RPC.State = "Working on a hidden project";
// Invoke event handlers and set presence
_Client?.Invoke();
_Client?.SetPresence(_RPC);
}
else
{
// FL Studio is not running
if (wasRunning)
{
// FL Studio just closed - clear presence and dispose client
_Client?.ClearPresence();
_Client?.Dispose();
_Client = null;
wasRunning = false;
}
}
// Sleep for the interval defined in the config file
Thread.Sleep(UpdateInterval);
}
catch (Exception ex)
{
// Log errors to a user-writable location
try
{
string logPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"FLStudioRPC",
"rpc_error.txt"
);
// Create directory if it doesn't exist
Directory.CreateDirectory(Path.GetDirectoryName(logPath));
File.WriteAllText(logPath, $"Error: {ex.Message}\n{ex.StackTrace}");
}
catch
{
// If we can't even write the log, just silently continue
}
}
}
}
[STAThread]
static void Main(string[] args)
{
// Check if another instance is already running
bool createdNew;
_mutex = new Mutex(true, "FLStudioRPC_SingleInstance", out createdNew);
if (!createdNew)
{
// Another instance is already running
MessageBox.Show(
"FL Studio Discord RPC is already running.\n\nCheck your system tray for the icon.",
"Already Running",
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
return; // Exit this instance
}
// Enable auto-start by default if not already configured
if (!IsAutoStartEnabled())
{
SetAutoStart(true);
}
// Initialize system tray icon
InitializeTrayIcon();
// Start RPC monitoring in a background thread
Thread rpcThread = new Thread(RunRPCLoop)
{
IsBackground = true
};
rpcThread.Start();
// Keep the application running (for the tray icon)
Application.Run();
// Release the mutex when application exits
_mutex?.ReleaseMutex();
_mutex?.Dispose();
}
}