跳到主要内容

github/tai

公开

Tai · Windows 软件与网站使用时长统计。上游 Planshit/Tai 的源码镜像,非灵栈原创;MIT 许可。来源:https://github.com/Planshit/Tai 。本次为手动同步,暂不提供安装包。

浏览文件

Sleepdiscover.cs

384 行 · 10.5 KB历史下载
1using Core.Enums;
2using Core.Event;
3using Core.Librarys;
4using Core.Servicers.Interfaces;
5using Microsoft.Win32;
6using System;
7using System.Collections.Generic;
8using System.Diagnostics;
9using System.Linq;
10using System.Runtime.InteropServices;
11using System.Text;
12using System.Threading;
13using System.Threading.Tasks;
14using System.Windows;
15using System.Windows.Threading;
16
17namespace Core.Servicers.Instances
18{
19 public class Sleepdiscover : ISleepdiscover
20 {
21 public event SleepdiscoverEventHandler SleepStatusChanged;
22
23 private DispatcherTimer timer;
24
25 private Point lastPoint;
26
27 private SleepStatus status = SleepStatus.Wake;
28
29 /// <summary>
30 /// 播放声音开始时间
31 /// </summary>
32 private DateTime playSoundStartTime;
33
34 /// <summary>
35 /// 最后一次按键时间
36 /// </summary>
37 private DateTime pressKeyboardLastTime;
38
39 // 键盘钩子
40 private Win32API.LowLevelKeyboardProc keyboardProc;
41 private static IntPtr hookKeyboardID = IntPtr.Zero;
42
43
44 // 鼠标钩子
45 private Win32API.LowLevelKeyboardProc mouseProc;
46 private static IntPtr hookMouseID = IntPtr.Zero;
47 private IntPtr mouseHook;
48
49 private int emptyPointNum = 0;
50 public Sleepdiscover()
51 {
52 SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(OnPowerModeChanged);
53 SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
54
55 keyboardProc = HookCallback;
56 mouseProc = HookMouseCallback;
57 }
58
59 public void Start()
60 {
61 StartTimer();
62
63 lastPoint = Win32API.GetCursorPosition();
64 playSoundStartTime = DateTime.MinValue;
65 pressKeyboardLastTime = DateTime.Now;
66
67 // 设置键盘钩子
68 Win32API.SetKeyboardHook(keyboardProc);
69 }
70
71 public void Stop()
72 {
73 StopTimer();
74 }
75
76 private void StartTimer()
77 {
78 StopTimer();
79 timer = new DispatcherTimer();
80 //timer.Interval = new TimeSpan(0, 0, 10);
81 timer.Interval = new TimeSpan(0, 5, 0);
82 timer.Tick += Timer_Tick;
83 timer.Start();
84 }
85
86 private void StopTimer()
87 {
88 if (timer != null)
89 {
90 timer.Tick -= Timer_Tick;
91 timer.Stop();
92 }
93 }
94
95 private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
96 {
97 if (e.Reason == SessionSwitchReason.RemoteDisconnect || e.Reason == SessionSwitchReason.ConsoleDisconnect)
98 {
99 // 与这台设备远程桌面连接断开
100 if (status == SleepStatus.Wake)
101 {
102 Logger.Warn("与这台设备远程桌面连接断开");
103 Sleep();
104 }
105 }
106 }
107
108 private IntPtr HookMouseCallback(int nCode, IntPtr wParam, IntPtr lParam)
109 {
110
111 if (nCode >= 0)
112 {
113 if (wParam == (IntPtr)Win32API.WM_LBUTTONDBLCLK || wParam == (IntPtr)Win32API.WM_WHEEL)
114 {
115 Logger.Info("鼠标唤醒");
116
117 Wake();
118 }
119
120 }
121
122 return Win32API.CallNextHookEx(hookMouseID, nCode, wParam, lParam);
123 }
124 private IntPtr HookCallback(
125 int nCode, IntPtr wParam, IntPtr lParam)
126 {
127 if (nCode >= 0 && wParam == (IntPtr)Win32API.WM_KEYDOWN)
128 {
129 if (status == SleepStatus.Sleep)
130 {
131 Logger.Info("键盘唤醒");
132
133 Wake();
134 }
135 else
136 {
137 playSoundStartTime = DateTime.MinValue;
138 pressKeyboardLastTime = DateTime.Now;
139 }
140 }
141 return Win32API.CallNextHookEx(hookKeyboardID, nCode, wParam, lParam);
142 }
143
144 private void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
145 {
146 switch (e.Mode)
147 {
148 case PowerModes.Suspend:
149 // 电脑休眠
150 if (status == SleepStatus.Wake)
151 {
152 Logger.Info("设备已休眠");
153
154 Sleep();
155 }
156 break;
157 case PowerModes.Resume:
158 // 电脑恢复
159 if (status == SleepStatus.Sleep)
160 {
161 Logger.Info("设备已恢复");
162
163 Wake();
164 }
165 break;
166 }
167 }
168
169
170
171 #region 指示当前是否处于睡眠状态
172 /// <summary>
173 /// 指示当前是否处于睡眠状态
174 /// </summary>
175 /// <returns>睡眠返回true</returns>
176 private async Task<bool> IsSleepAsync()
177 {
178 Point point = Win32API.GetCursorPosition();
179
180 if (point.X + point.Y == 0)
181 {
182 emptyPointNum++;
183
184 if (emptyPointNum == 2)
185 {
186 emptyPointNum = 0;
187 return true;
188 }
189 }
190
191 bool isActive = await IsActiveAsync();
192 if (isActive)
193 {
194 return false;
195 }
196
197 // 鼠标和键盘都没有操作时判断是否在播放声音
198
199 // 持续30秒检测当前是否在播放声音
200 bool isPlaySound = await IsPlaySoundAsync();
201
202 if (!isPlaySound)
203 {
204 // 没有声音判定为睡眠状态
205 return true;
206 }
207
208 // 在播放声音
209 if (playSoundStartTime == DateTime.MinValue)
210 {
211 // 第一次记录声音开始时间
212 playSoundStartTime = DateTime.Now;
213 return false;
214 }
215
216 // 声音播放超过两个小时视为睡眠状态
217 TimeSpan timeSpan = DateTime.Now - playSoundStartTime;
218
219 if (timeSpan.TotalHours >= 2)
220 {
221 // 重置声音开始时间
222 playSoundStartTime = DateTime.MinValue;
223 return true;
224 }
225
226 return false;
227 }
228 #endregion
229
230 private async void Timer_Tick(object sender, EventArgs e)
231 {
232 Debug.WriteLine("检测");
233 timer.Stop();
234 bool isSleep = await IsSleepAsync();
235 if (isSleep)
236 {
237 Sleep();
238 }
239 else
240 {
241 timer.Start();
242 }
243 }
244
245 private void Sleep()
246 {
247 if (status == SleepStatus.Sleep)
248 {
249 return;
250 }
251
252 // 卸载事件
253 SystemEvents.SessionSwitch -= SystemEvents_SessionSwitch;
254
255 // 停止离开检测计时器
256 StopTimer();
257
258 status = SleepStatus.Sleep;
259
260 // 设置鼠标钩子
261 mouseHook = Win32API.SetMouseHook(mouseProc);
262
263 // 状态通知
264 SleepStatusChanged?.Invoke(status);
265 }
266
267 private void Wake()
268 {
269 if (status == SleepStatus.Wake)
270 {
271 return;
272 }
273 try
274 {
275 // 注册事件
276 SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
277
278 status = SleepStatus.Wake;
279
280 // 卸载鼠标钩子
281 Win32API.UnhookWindowsHookEx(mouseHook);
282
283 // 启动离开检测
284 StartTimer();
285
286
287
288 // 重置声音播放时间
289 playSoundStartTime = DateTime.MinValue;
290
291 // 重置鼠标坐标
292 lastPoint = Win32API.GetCursorPosition();
293
294 // 状态通知
295 SleepStatusChanged?.Invoke(status);
296 }
297 catch (Exception e)
298 {
299 Logger.Error(e.ToString());
300 }
301 }
302
303 /// <summary>
304 /// 指示当前是否在播放声音(持续30秒检测)
305 /// </summary>
306 /// <returns>播放返回true</returns>
307 private async Task<bool> IsPlaySoundAsync()
308 {
309 bool result = false;
310
311 await Task.Run(() =>
312 {
313 // 持续30秒
314 int time = 30;
315
316 while (time > 0)
317 {
318 bool isPlay = Win32API.IsWindowsPlayingSound();
319 if (isPlay)
320 {
321 result = true;
322 break;
323 }
324 else
325 {
326 time--;
327 Debug.WriteLine(time);
328 Thread.Sleep(1000);
329 }
330 }
331 });
332 return result;
333 }
334
335 /// <summary>
336 /// 指示当前是否处于活跃状态(持续30秒检测)
337 /// </summary>
338 /// <returns>活跃返回true</returns>
339 private async Task<bool> IsActiveAsync()
340 {
341 bool result = false;
342
343 await Task.Run(() =>
344 {
345 // 持续30秒
346 int time = 30;
347
348 while (time > 0)
349 {
350 Point point = Win32API.GetCursorPosition();
351 bool isMouseMove = lastPoint.ToString() != point.ToString();
352 bool isKeyboardActive = !IsKeyboardOuttime();
353
354 if (isMouseMove || isKeyboardActive)
355 {
356 lastPoint = Win32API.GetCursorPosition();
357 result = true;
358 break;
359 }
360 else
361 {
362 time--;
363 Debug.WriteLine(time);
364 Thread.Sleep(1000);
365 }
366 }
367 });
368 return result;
369 }
370
371 /// <summary>
372 /// 指示是否键盘行为超时(超过10分钟)
373 /// </summary>
374 /// <returns>超时返回true</returns>
375 private bool IsKeyboardOuttime()
376 {
377 TimeSpan timeSpan = DateTime.Now - pressKeyboardLastTime;
378#if DEBUG
379 return timeSpan.TotalSeconds >= 10;
380#endif
381 return timeSpan.TotalMinutes >= 10;
382 }
383 }
384}