]> gitweb.ps.run Git - autorec/blob - src/main.cpp
start obs manually
[autorec] / src / main.cpp
1 #ifndef WIN32\r
2   #define WIN32\r
3 #endif\r
4 #include <WinSock2.h>\r
5 #include "win.h"\r
6 #include "ws.h"\r
7 \r
8 #define LAY_IMPLEMENTATION\r
9 #include "layout.h"\r
10 \r
11 #include <fstream>\r
12 #include <vector>\r
13 #include <string>\r
14 \r
15 #include <Psapi.h>\r
16 #include <stdbool.h>\r
17 #include <stdio.h>\r
18 #include <string.h>\r
19 #include <windows.h>\r
20 #include <shlwapi.h>\r
21 \r
22 bool recording = false;\r
23 HANDLE process = NULL;\r
24 HWND hwnd;\r
25 HINSTANCE hInstance;\r
26 HICON icon_white, icon_green, icon_red;\r
27 \r
28 std::vector<std::string> gameExes;\r
29 \r
30 \r
31 NOTIFYICONDATAA niData = { 0 };\r
32 const UINT ICON_MSG = WM_APP+1;\r
33 void\r
34 ShowNotificationIcon()\r
35 {\r
36   Shell_NotifyIconA(NIM_ADD, &niData);\r
37   Shell_NotifyIconA(NIM_SETVERSION, &niData);\r
38 }\r
39 \r
40 void\r
41 HideNotificationIcon()\r
42 {\r
43   Shell_NotifyIconA(NIM_DELETE, &niData);\r
44 }\r
45 \r
46 void changeIcon(HWND hwnd, HICON icon)\r
47 {\r
48   niData.hIcon = icon;\r
49   if (! IsWindowVisible(hwnd))\r
50     Shell_NotifyIconA(NIM_MODIFY, &niData);\r
51   SendMessage(hwnd, WM_SETICON, 0, (LPARAM)icon);\r
52   SendMessage(hwnd, WM_SETICON, 1, (LPARAM)icon);\r
53 }\r
54 \r
55 \r
56 \r
57 void\r
58 startRecording()\r
59 {\r
60   ws::sendRequest("StartRecord");\r
61   changeIcon(hwnd, icon_green);\r
62 }\r
63 \r
64 void\r
65 stopRecording()\r
66 {\r
67   ws::sendRequest("StopRecord");\r
68   changeIcon(hwnd, icon_red);\r
69 }\r
70 \r
71 bool\r
72 checkProcessRunning(HANDLE handle)\r
73 {\r
74   DWORD exit_code;\r
75 \r
76   if (handle != NULL && GetExitCodeProcess(handle, &exit_code)) {\r
77     return exit_code == STILL_ACTIVE;\r
78   }\r
79 \r
80   return false;\r
81 }\r
82 \r
83 HANDLE\r
84 getHwndProcess(HWND hwnd)\r
85 {\r
86   DWORD processId, threadId = GetWindowThreadProcessId(hwnd, &processId);\r
87   return OpenProcess(PROCESS_QUERY_INFORMATION, false, processId);\r
88 }\r
89 \r
90 // HWND getProcessHwnd(HANDLE handle) {}\r
91 \r
92 bool\r
93 checkFullscreenWindow()\r
94 {\r
95   HWND desktopHwnd = GetDesktopWindow();\r
96   HWND fgHwnd = GetForegroundWindow();\r
97 \r
98   if (fgHwnd != desktopHwnd && fgHwnd != GetShellWindow()) {\r
99     RECT windowRect, desktopRect;\r
100     // Get Window and Desktop size\r
101     GetWindowRect(fgHwnd, &windowRect);\r
102     GetWindowRect(desktopHwnd, &desktopRect);\r
103 \r
104     bool fullscreen = windowRect.bottom == desktopRect.bottom &&\r
105                       windowRect.top == desktopRect.top &&\r
106                       windowRect.left == desktopRect.left &&\r
107                       windowRect.right == desktopRect.right;\r
108 \r
109     return fullscreen;\r
110   }\r
111 \r
112   return false;\r
113 }\r
114 \r
115 bool\r
116 checkForegroundProcess(std::string exeName)\r
117 {\r
118   HWND fgHwnd = GetForegroundWindow();\r
119   HANDLE fgHandle = getHwndProcess(fgHwnd);\r
120 \r
121   char filename[MAX_PATH];\r
122   int len = GetModuleFileNameExA(fgHandle, NULL, filename, MAX_PATH);\r
123 \r
124   if (strcmp(filename, exeName.c_str()) == 0)\r
125     return true;\r
126 \r
127   PathStripPathA(filename);\r
128 \r
129   return strcmp(filename, exeName.c_str()) == 0;\r
130 }\r
131 \r
132 void ReadGameExes()\r
133 {\r
134   std::ifstream ifs("games.txt");\r
135 \r
136   gameExes.clear();\r
137 \r
138   while (ifs) {\r
139     std::string str;\r
140     std::getline(ifs, str);\r
141     if (! str.empty())\r
142       gameExes.push_back(str);\r
143   }\r
144 \r
145   ifs.close();\r
146 }\r
147 \r
148 void WriteGameExes()\r
149 {\r
150   std::ofstream ofs("games.txt", std::ios::trunc);\r
151 \r
152   for (const auto &exe : gameExes)\r
153   {\r
154     ofs.write(exe.c_str(), exe.size());\r
155     ofs.write("\n", 1);\r
156   }\r
157 \r
158   ofs.close();\r
159 }\r
160 \r
161 PROCESS_INFORMATION StartOBS()\r
162 {\r
163   // delete .sentinel folder to stop obs from displaying\r
164   // error message on startup\r
165   system("del %appdata%\\obs-studio\\.sentinel /Q");\r
166 \r
167   PROCESS_INFORMATION pi;\r
168   STARTUPINFOA sui;\r
169   GetStartupInfoA(&sui);\r
170   CreateProcessA(nullptr, "C:\\Program Files\\obs-studio\\bin\\64bit\\obs64.exe",\r
171     nullptr, nullptr, false, NORMAL_PRIORITY_CLASS, nullptr, "C:\\Program Files\\obs-studio\\bin\\64bit", &sui, &pi);\r
172   return pi;\r
173 }\r
174 \r
175 void StopOBS(PROCESS_INFORMATION &pi)\r
176 {\r
177   EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {\r
178     PROCESS_INFORMATION *pi = (PROCESS_INFORMATION*)lParam;\r
179 \r
180     DWORD pid, tid;\r
181     tid = GetWindowThreadProcessId(hwnd, &pid);\r
182     if (tid == pi->dwThreadId && pid == pi->dwProcessId) {\r
183 \r
184       char title[1024]; GetWindowTextA(hwnd, title, 1024);\r
185 \r
186       if (strncmp(title, "OBS", 3) == 0) {\r
187         PostMessage(hwnd, WM_CLOSE, 0, 0);\r
188         return FALSE; \r
189       }\r
190     }\r
191     return TRUE;\r
192   }, (LPARAM)&pi);\r
193 }\r
194 \r
195 /*\r
196 TODO:\r
197   - Disconnect while recording\r
198 */\r
199 \r
200 \r
201 \r
202 int WINAPI\r
203 WinMain(HINSTANCE hInstance,\r
204         HINSTANCE hPrevInstance,\r
205         LPSTR lpCmdLine,\r
206         int nCmdShow)\r
207 //int main(int argc, char **argv)\r
208 {\r
209   hInstance = GetModuleHandle(0);\r
210 \r
211   icon_white = LoadIconA(hInstance, MAKEINTRESOURCEA(IDI_ICON_WHITE));\r
212   icon_green = LoadIconA(hInstance, MAKEINTRESOURCEA(IDI_ICON_GREEN));\r
213   icon_red = LoadIconA(hInstance, MAKEINTRESOURCEA(IDI_ICON_RED));\r
214 \r
215   win::Window window("Title", "MyWindowClass", hInstance);\r
216   hwnd = window.hwnd;\r
217 \r
218   niData.cbSize = sizeof(niData);\r
219   niData.uID = 12345;\r
220   niData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;\r
221   niData.hIcon = icon_white;\r
222   niData.hWnd = window.hwnd;\r
223   niData.uCallbackMessage = ICON_MSG;\r
224   niData.uVersion = NOTIFYICON_VERSION_4;\r
225 \r
226   PROCESS_INFORMATION pi_obs = StartOBS();\r
227 \r
228   window.handlers[WM_SIZE].push_back([](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
229     if (wParam == SIZE_MINIMIZED) {\r
230       ShowNotificationIcon();\r
231       ShowWindow(hwnd, false);\r
232     }\r
233   });\r
234   window.handlers[WM_DESTROY].push_back([](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
235     HideNotificationIcon();\r
236   });  \r
237   window.handlers[ICON_MSG].push_back([](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
238     if (LOWORD(lParam) == NIN_SELECT) {\r
239       HideNotificationIcon();\r
240       ShowWindow(hwnd, true);\r
241       SetForegroundWindow(hwnd);\r
242       SetActiveWindow(hwnd);\r
243     }\r
244   });\r
245   window.handlers[WM_GETMINMAXINFO].push_back([](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
246     MINMAXINFO *mmInfo = (MINMAXINFO*)lParam;\r
247     mmInfo->ptMinTrackSize.x = 400;\r
248     mmInfo->ptMinTrackSize.y = 200;\r
249   });\r
250   window.handlers[WM_QUERYENDSESSION].push_back([&pi_obs](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
251     StopOBS(pi_obs);\r
252   });  window.handlers[WM_ENDSESSION].push_back([&pi_obs](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
253     StopOBS(pi_obs);\r
254   });\r
255   window.handlers[WM_CLOSE].push_back([&pi_obs](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
256     StopOBS(pi_obs);\r
257   });\r
258 \r
259   lay_context *ctx = &window.ctx;\r
260   lay_id root = window.lId;\r
261 \r
262   lay_id row1 = win::createLayId(&window.ctx, window.lId, 0, 25, LAY_ROW, LAY_LEFT);\r
263   lay_set_margins_ltrb(ctx, row1, 5, 5, 5, 5);\r
264   lay_id row2 = win::createLayId(&window.ctx, window.lId, 0, 0, LAY_ROW, LAY_FILL);\r
265   lay_set_margins_ltrb(ctx, row2, 5, 5, 5, 5);\r
266 \r
267   win::CheckBox cbWindowTitle(&window, "Window Title", row1, 100, 25, 0, 0);\r
268   win::CheckBox cbFullscreenWindow(&window, "Any Fullscreen Application", row1, 200, 25, 0, 0);\r
269 \r
270   win::Button btnConnect(&window, "Connect", row1, 100, 25, 0, 0);\r
271   btnConnect.onClick([&]() {\r
272     if (! ws::isConnected)\r
273       ws::connect("ws://127.0.0.1:4455");\r
274   });\r
275 \r
276   win::ListBox lstActiveProcesses(&window, row2, 0, 0, 0, LAY_FILL);\r
277   \r
278   lay_id col1 = win::createLayId(&window.ctx, row2, 80, 0, LAY_COLUMN, LAY_VCENTER);\r
279   lstActiveProcesses.addStyle(WS_VSCROLL);\r
280 \r
281   lay_set_margins_ltrb(ctx, col1, 5, 0, 5, 0);\r
282 \r
283   win::ListBox lstMonitoredProcesses(&window, row2, 0, 0, 0, LAY_FILL);\r
284   lstMonitoredProcesses.addStyle(WS_VSCROLL);\r
285 \r
286   ReadGameExes();\r
287   for (const auto &exe : gameExes)\r
288     lstMonitoredProcesses.addString(exe);\r
289 \r
290   win::Button btnUpdateWindows(&window, "Update", col1, 85, 25, 0, 0);\r
291   win::Button btnStartMonitoringName(&window, "Exe name >>", col1, 85, 25, 0, 0);\r
292   win::Button btnStartMonitoringPath(&window, "Full path >>", col1, 85, 25, 0, 0);\r
293   win::Button btnStopMonitoring(&window, "Remove", col1, 85, 25, 0, 0);\r
294   btnUpdateWindows.onClick([&]() {\r
295     lstActiveProcesses.clear();\r
296     for (HWND hwnd = GetTopWindow(NULL); hwnd != nullptr;\r
297          hwnd = GetNextWindow(hwnd, GW_HWNDNEXT)) {\r
298       if (!IsWindowVisible(hwnd))\r
299         continue;\r
300 \r
301       RECT rect;\r
302       GetWindowRect(hwnd, &rect);\r
303 \r
304       char str[1024];\r
305       if (GetModuleFileNameExA(getHwndProcess(hwnd), 0, str, 1024) != 0 &&\r
306           lstActiveProcesses.findString(str) == LB_ERR) {\r
307         lstActiveProcesses.addString(str);\r
308       }\r
309     }\r
310   });\r
311   btnStartMonitoringName.onClick([&]() {\r
312     int sel = lstActiveProcesses.getSelectedIndex();\r
313     if (sel < 0) return;\r
314 \r
315     std::string selStr = lstActiveProcesses.getText(sel);\r
316     \r
317     char *filename = new char[selStr.size()+1];\r
318     std::memcpy(filename, selStr.c_str(), selStr.size());\r
319     filename[selStr.size()] = '\0';\r
320     PathStripPathA(filename);\r
321 \r
322     std::string filenameStr(filename);\r
323 \r
324     if (lstMonitoredProcesses.findString(filenameStr) == LB_ERR)\r
325     {\r
326       lstMonitoredProcesses.addString(filenameStr);\r
327       gameExes.push_back(filenameStr);\r
328       WriteGameExes();\r
329     }\r
330 \r
331     delete[] filename;\r
332   });\r
333   btnStartMonitoringPath.onClick([&]() {\r
334     int sel = lstActiveProcesses.getSelectedIndex();\r
335     if (sel < 0) return;\r
336     std::string selStr = lstActiveProcesses.getText(sel);\r
337     if (lstMonitoredProcesses.findString(selStr) == LB_ERR)\r
338     {\r
339       lstMonitoredProcesses.addString(selStr);\r
340       gameExes.push_back(selStr);\r
341       WriteGameExes();  \r
342     }\r
343   });\r
344   btnStopMonitoring.onClick([&]() {\r
345     int sel = lstMonitoredProcesses.getSelectedIndex();\r
346     if (sel < 0) return;\r
347     lstMonitoredProcesses.remove(sel);\r
348     gameExes.erase(gameExes.begin() + sel);\r
349     WriteGameExes();\r
350   });\r
351 \r
352   window.show();\r
353   window.setDefaultFont();\r
354 \r
355   ws::onConnect = [&]() {\r
356     changeIcon(window.hwnd, icon_red);\r
357     btnConnect.setActive(false);\r
358   };\r
359   ws::onClose = [&]() {\r
360     changeIcon(window.hwnd, icon_white);\r
361     btnConnect.setActive(true);\r
362   };\r
363   ws::onError = ws::onClose;\r
364   ws::init();\r
365 \r
366   window.setTimer(1000, [&btnConnect]() {\r
367     if (! ws::isConnected)\r
368       ws::connect("ws://127.0.0.1:4455");\r
369   });\r
370 \r
371   window.setTimer(100, []() {\r
372     if (!recording) {\r
373       for (auto exe : gameExes) {\r
374         if (checkForegroundProcess(exe)) {\r
375           recording = true;\r
376           process = getHwndProcess(GetForegroundWindow());\r
377           startRecording();\r
378         } \r
379       }\r
380     } else {\r
381       if (!checkProcessRunning(process)) {\r
382         recording = false;\r
383         process = NULL;\r
384         stopRecording();\r
385       }\r
386     }\r
387   });\r
388 \r
389   while (window.update()) {\r
390     ws::update();\r
391   }\r
392 \r
393   return 0;\r
394 }\r