You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

526 lines
13KB

  1. /*
  2. © 2004 Kreisquadratur
  3. icq#56165405
  4. © 2004-2019 Christopher Snowhill
  5. 2020-02-28 22:33 UTC - kode54
  6. - Set sensible defaults to match the old versions
  7. - Version is now 0.8
  8. 2019-07-30 22:17 UTC - kode54
  9. - Remove Win9x code entirely
  10. - Add Advanced Configuration options to control default actions
  11. - Version is now 0.7
  12. 2017-02-04 04:45 UTC - kode54
  13. - Add link to about string
  14. - Version is now 0.6
  15. 2010-01-11 20:17 UTC - kode54
  16. - Added filename validator
  17. - Version is now 0.5
  18. 2009-07-22 00:55 UTC - kode54
  19. - Removed Win9x detection since it's been a long time since foobar2000 supported Win9x
  20. - Changed capitalization of menu command name
  21. - Updated component version description notice and copyright
  22. 2004-02-24 21:44 UTC - kode54
  23. - Disable function is now a little saner
  24. - Version is now 0.4
  25. 2004-02-09 18:15 UTC - kode54
  26. - Fixed multi-user session monitoring ... see large comment block below :)
  27. - Version is now 0.3
  28. 2004-02-09 15:59 UTC - kode54
  29. - Added new WTSAPI32-based window class for session monitoring in Windows XP and 2003 Server
  30. - Consolidated lock/unlock code into static functions
  31. - Safeguarded enable/disable functions with win_ver, set by initial call to GetVersion()
  32. - Changed cfg_waslocked/cfg_resume to static BOOL ... not sure these should persist across sessions
  33. - Version is now 0.2
  34. */
  35. #define _WIN32_WINNT 0x501
  36. #include <foobar2000.h>
  37. #include <wtsapi32.h>
  38. #define IsWinVer2000Plus() (LOBYTE(LOWORD(GetVersion()))>=5)
  39. #include "../patrons.h"
  40. DECLARE_COMPONENT_VERSION(
  41. "Pause on Lock",
  42. "0.8",
  43. "Pause/Resume when Workstation is Locked/Unlocked\n\n"
  44. "for foobar2000 v1.3\n\n"
  45. "(c) Kreisquadratur 2004\n"
  46. "(c) Christopher Snowhill 2004-2019\n\n"
  47. "https://www.patreon.com/kode54\n\n"
  48. MY_PATRONS);
  49. VALIDATE_COMPONENT_FILENAME("foo_lock.dll");
  50. // {79FC6DE5-0291-47ab-A806-B4764B1C2279}
  51. static const GUID guid_cfg_enabled =
  52. { 0x79fc6de5, 0x291, 0x47ab, { 0xa8, 0x6, 0xb4, 0x76, 0x4b, 0x1c, 0x22, 0x79 } };
  53. // {33D8F2BA-E737-4D1E-BDF3-8DB63298EE02}
  54. static const GUID guid_branch_main =
  55. { 0x33d8f2ba, 0xe737, 0x4d1e, { 0xbd, 0xf3, 0x8d, 0xb6, 0x32, 0x98, 0xee, 0x2 } };
  56. // {6D622351-BDBA-4687-BEBF-3E5BE94666E9}
  57. static const GUID guid_branch_on_lock =
  58. { 0x6d622351, 0xbdba, 0x4687, { 0xbe, 0xbf, 0x3e, 0x5b, 0xe9, 0x46, 0x66, 0xe9 } };
  59. // {C333A02D-F8AC-4F91-95C0-CE42372854B1}
  60. static const GUID guid_branch_on_unlock =
  61. { 0xc333a02d, 0xf8ac, 0x4f91, { 0x95, 0xc0, 0xce, 0x42, 0x37, 0x28, 0x54, 0xb1 } };
  62. // {26AD2444-3960-4732-9BBB-C0DD4D6E8134}
  63. static const GUID guid_cfg_on_lock_nothing =
  64. { 0x26ad2444, 0x3960, 0x4732, { 0x9b, 0xbb, 0xc0, 0xdd, 0x4d, 0x6e, 0x81, 0x34 } };
  65. // {D2A3AE2C-5ECE-45DE-9091-4966D27AB8A9}
  66. static const GUID guid_cfg_on_lock_pause =
  67. { 0xd2a3ae2c, 0x5ece, 0x45de, { 0x90, 0x91, 0x49, 0x66, 0xd2, 0x7a, 0xb8, 0xa9 } };
  68. // {420E0363-7CA9-48E0-BD22-3C3497C62F5B}
  69. static const GUID guid_cfg_on_lock_stop =
  70. { 0x420e0363, 0x7ca9, 0x48e0, { 0xbd, 0x22, 0x3c, 0x34, 0x97, 0xc6, 0x2f, 0x5b } };
  71. // {DABCC7D7-4194-4BFC-BEE9-A0174814087E}
  72. static const GUID guid_cfg_on_unlock_nothing =
  73. { 0xdabcc7d7, 0x4194, 0x4bfc, { 0xbe, 0xe9, 0xa0, 0x17, 0x48, 0x14, 0x8, 0x7e } };
  74. // {898BFFD4-A3EF-4B70-A661-A4A12C9E6428}
  75. static const GUID guid_cfg_on_unlock_unpause =
  76. { 0x898bffd4, 0xa3ef, 0x4b70, { 0xa6, 0x61, 0xa4, 0xa1, 0x2c, 0x9e, 0x64, 0x28 } };
  77. // {1727EEE9-6B88-4E83-925E-5C633F874059}
  78. static const GUID guid_cfg_on_unlock_play =
  79. { 0x1727eee9, 0x6b88, 0x4e83, { 0x92, 0x5e, 0x5c, 0x63, 0x3f, 0x87, 0x40, 0x59 } };
  80. enum
  81. {
  82. ol_nothing = 0,
  83. ol_pause,
  84. ol_stop
  85. };
  86. enum
  87. {
  88. ou_nothing = 0,
  89. ou_unpause,
  90. ou_play
  91. };
  92. static cfg_int cfg_enabled(guid_cfg_enabled, 0);
  93. static advconfig_branch_factory cfg_branch_main("Lock Actions", guid_branch_main, advconfig_entry::guid_branch_playback, 0.0);
  94. static advconfig_branch_factory cfg_branch_on_lock("On Lock", guid_branch_on_lock, guid_branch_main, 0.0);
  95. static advconfig_branch_factory cfg_branch_on_unlock("On Unlock", guid_branch_on_unlock, guid_branch_main, 1.0);
  96. static advconfig_radio_factory cfg_on_lock_nothing("Do nothing", guid_cfg_on_lock_nothing, guid_branch_on_lock, 0.0, false);
  97. static advconfig_radio_factory cfg_on_lock_pause("Pause playback", guid_cfg_on_lock_pause, guid_branch_on_lock, 1.0, true);
  98. static advconfig_radio_factory cfg_on_lock_stop("Stop playback", guid_cfg_on_lock_stop, guid_branch_on_lock, 2.0, false);
  99. static advconfig_radio_factory cfg_on_unlock_nothing("Do nothing", guid_cfg_on_unlock_nothing, guid_branch_on_unlock, 0.0, false);
  100. static advconfig_radio_factory cfg_on_unlock_unpause("Resume playback", guid_cfg_on_unlock_unpause, guid_branch_on_unlock, 1.0, true);
  101. static advconfig_radio_factory cfg_on_unlock_play("Start playback", guid_cfg_on_unlock_play, guid_branch_on_unlock, 2.0, false);
  102. static int query_cfg_on_lock()
  103. {
  104. if (cfg_on_lock_nothing.get()) return ol_nothing;
  105. else if (cfg_on_lock_pause.get()) return ol_pause;
  106. else return ol_stop;
  107. }
  108. static int query_cfg_on_unlock()
  109. {
  110. if (cfg_on_unlock_nothing.get()) return ou_nothing;
  111. else if (cfg_on_unlock_unpause.get()) return ou_unpause;
  112. else return ou_play;
  113. }
  114. // eh?
  115. //cfg_int cfg_waslocked("waslocked", 0);
  116. //cfg_int cfg_resume("resume", 0);
  117. static BOOL cfg_waslocked = FALSE;
  118. static BOOL cfg_resume = FALSE;
  119. static UINT hTimer = 0;
  120. class CSessionWnd;
  121. typedef BOOL (WINAPI * pWTSRegisterSessionNotification)(HWND hWnd, DWORD dwFlags);
  122. typedef BOOL (WINAPI * pWTSUnRegisterSessionNotification)(HWND hWnd);
  123. CSessionWnd * g_sessionwnd = NULL;
  124. static void lock()
  125. {
  126. pfc::string8 info("Workstation locked");
  127. static_api_ptr_t<playback_control> pc;
  128. cfg_waslocked = TRUE;
  129. int action = query_cfg_on_lock();
  130. if (action == ol_nothing)
  131. {
  132. }
  133. else if (action == ol_pause)
  134. {
  135. if (pc->is_playing() && !pc->is_paused())
  136. {
  137. standard_commands::main_pause();
  138. info += " - paused";
  139. cfg_resume = TRUE;
  140. }
  141. }
  142. else
  143. {
  144. if (pc->is_playing())
  145. {
  146. standard_commands::main_stop();
  147. info += " - stopped";
  148. cfg_resume = TRUE;
  149. }
  150. }
  151. info.add_byte('.');
  152. console::info(info);
  153. }
  154. static void unlock()
  155. {
  156. pfc::string8 info("Workstation unlocked");
  157. static_api_ptr_t<play_control> pc;
  158. cfg_waslocked = FALSE;
  159. int action = query_cfg_on_unlock();
  160. if (action == ou_nothing)
  161. {
  162. }
  163. else if (action == ou_unpause)
  164. {
  165. if (cfg_resume)
  166. {
  167. cfg_resume = FALSE;
  168. if (pc->is_paused())
  169. {
  170. standard_commands::main_play();
  171. info += " - resumed";
  172. }
  173. }
  174. }
  175. else
  176. {
  177. if (cfg_resume)
  178. {
  179. cfg_resume = FALSE;
  180. standard_commands::main_play();
  181. info += " - started playback";
  182. }
  183. }
  184. info.add_byte('.');
  185. console::info(info);
  186. }
  187. static const TCHAR class_name[] = _T( "870AC6B2-D141-46f4-A196-ADCB72B8AE4E" );
  188. class CSessionWnd
  189. {
  190. public:
  191. CSessionWnd()
  192. {
  193. m_bRegistered = FALSE;
  194. m_hWnd = NULL;
  195. hWTSAPI = NULL;
  196. m_bDisconnected = FALSE;
  197. }
  198. bool Initialize(HINSTANCE hInstance)
  199. {
  200. m_hInstance = hInstance;
  201. hWTSAPI = LoadLibrary(_T("wtsapi32.dll"));
  202. if (!hWTSAPI) return false;
  203. register_session = (pWTSRegisterSessionNotification) GetProcAddress(hWTSAPI, "WTSRegisterSessionNotification");
  204. if (!register_session) return false;
  205. unregister_session = (pWTSUnRegisterSessionNotification) GetProcAddress(hWTSAPI, "WTSUnRegisterSessionNotification");
  206. if (!unregister_session) return false;
  207. WNDCLASS wcl;
  208. memset(&wcl, 0, sizeof(wcl));
  209. wcl.hInstance = hInstance;
  210. wcl.lpfnWndProc = (WNDPROC)WndProc;
  211. wcl.lpszClassName = class_name;
  212. m_lpszClassName = ( const TCHAR * ) RegisterClass( & wcl );
  213. if ( ! m_lpszClassName ) return false;
  214. m_bRegistered = TRUE;
  215. m_hWnd = CreateWindowEx(0, m_lpszClassName, _T( "uninteresting" ), 0, 0, 0, 0, 0, 0, 0, hInstance, this);
  216. if (m_hWnd)
  217. {
  218. //modeless_dialog_manager::add(m_hWnd);
  219. return !!register_session(m_hWnd, NOTIFY_FOR_THIS_SESSION);
  220. }
  221. else
  222. {
  223. return false;
  224. }
  225. }
  226. ~CSessionWnd()
  227. {
  228. if (IsWindow(m_hWnd)) DestroyWindow(m_hWnd);
  229. if (m_bRegistered)
  230. UnregisterClass( m_lpszClassName, m_hInstance );
  231. if (hWTSAPI) FreeLibrary(hWTSAPI);
  232. }
  233. private:
  234. static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  235. {
  236. static CSessionWnd *pThis = NULL;
  237. if(uMessage == WM_CREATE)
  238. {
  239. pThis = (CSessionWnd *)((CREATESTRUCT *)(lParam))->lpCreateParams;
  240. }
  241. return pThis->WindowProc(hWnd, uMessage, wParam, lParam);
  242. }
  243. LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  244. {
  245. switch (uMessage)
  246. {
  247. case WM_DESTROY:
  248. unregister_session(hWnd);
  249. //modeless_dialog_manager::remove(hWnd);
  250. break;
  251. /* the story behind the madness....
  252. it seems that Microsoft handles things like this:
  253. For a normal workstation lock,
  254. WTS_SESSION_LOCK on lock,
  255. WTS_SESSION_UNLOCK on unlock.
  256. BUT, for multiple users at the same terminal...
  257. WTS_SESSION_LOCK on fast user switch,
  258. AND just as a new user logs in,
  259. WTS_SESSION_UNLOCK immediately before this account gets
  260. WTS_CONSOLE_DISCONNECT to signal local console has disconnected from the account
  261. another BUT... I don't get the WTS_SESSION_UNLOCK for that account
  262. but I do get
  263. WTS_CONSOLE_CONNECT after this account is unlocked again
  264. So, summarizing...
  265. Single-user:
  266. WTS_SESSION_LOCK (pause)
  267. WTS_SESSION_UNLOCK (unpause)
  268. Multi-user:
  269. WTS_SESSION_LOCK (pause)
  270. WTS_SESSION_UNLOCK (unpause)
  271. or
  272. WTS_SESSION_LOCK (pause)
  273. WTS_CONSOLE_DISCONNECT
  274. WTS_SESSION_UNLOCK (ignore)
  275. WTS_CONSOLE_CONNECT (unpause)
  276. ...WHEW! */
  277. case WM_WTSSESSION_CHANGE:
  278. if (wParam == WTS_SESSION_LOCK)
  279. {
  280. lock();
  281. }
  282. else if (wParam == WTS_SESSION_UNLOCK)
  283. {
  284. if (!m_bDisconnected) unlock();
  285. }
  286. else if (wParam == WTS_CONSOLE_CONNECT)
  287. {
  288. unlock();
  289. m_bDisconnected = FALSE;
  290. }
  291. else if (wParam == WTS_CONSOLE_DISCONNECT)
  292. {
  293. m_bDisconnected = TRUE;
  294. }
  295. break;
  296. default:
  297. return uDefWindowProc(hWnd, uMessage, wParam, lParam);
  298. }
  299. return 0;
  300. }
  301. HINSTANCE m_hInstance;
  302. HWND m_hWnd;
  303. const TCHAR * m_lpszClassName;
  304. BOOL m_bRegistered;
  305. HMODULE hWTSAPI;
  306. pWTSRegisterSessionNotification register_session;
  307. pWTSUnRegisterSessionNotification unregister_session;
  308. BOOL m_bDisconnected;
  309. };
  310. static VOID CALLBACK IsWorkstationLocked(HWND hwnd,UINT message,UINT idEvent,DWORD dwTime)
  311. {
  312. HDESK hd;
  313. char buf[256];
  314. BOOL isLocked = FALSE;
  315. hd = OpenInputDesktop(0,FALSE,MAXIMUM_ALLOWED);
  316. if (hd==NULL) isLocked=TRUE;
  317. else
  318. {
  319. GetUserObjectInformation(hd,UOI_NAME,buf,sizeof(buf),NULL);
  320. CloseDesktop(hd);
  321. if (strcmp(buf,"Winlogon")==0) isLocked=TRUE;
  322. }
  323. if (isLocked)
  324. {
  325. if (!cfg_waslocked)
  326. {
  327. lock();
  328. }
  329. }
  330. else
  331. {
  332. if (cfg_waslocked)
  333. {
  334. unlock();
  335. }
  336. }
  337. }
  338. static void enable()
  339. {
  340. g_sessionwnd = new CSessionWnd;
  341. if (!g_sessionwnd->Initialize(core_api::get_my_instance()))
  342. {
  343. delete g_sessionwnd;
  344. g_sessionwnd = NULL;
  345. hTimer = SetTimer(NULL, 0, 1000, IsWorkstationLocked);
  346. }
  347. }
  348. static void disable()
  349. {
  350. if (g_sessionwnd)
  351. {
  352. delete g_sessionwnd;
  353. g_sessionwnd = NULL;
  354. }
  355. else
  356. {
  357. if (hTimer)
  358. {
  359. KillTimer(NULL, hTimer);
  360. hTimer = 0;
  361. }
  362. }
  363. }
  364. class initquit_foolock : public initquit {
  365. virtual void on_init()
  366. {
  367. if (cfg_enabled) enable();
  368. }
  369. virtual void on_quit()
  370. {
  371. disable();
  372. }
  373. };
  374. static initquit_factory_t <initquit_foolock> foo_initquit;
  375. class mainmenu_command_foolock : public mainmenu_commands {
  376. virtual t_uint32 get_command_count()
  377. {
  378. return 1;
  379. }
  380. virtual GUID get_command(t_uint32 p_index)
  381. {
  382. // {AF0E45EF-3C49-4d54-A3D2-DE8134813FFA}
  383. static const GUID guid =
  384. { 0xaf0e45ef, 0x3c49, 0x4d54, { 0xa3, 0xd2, 0xde, 0x81, 0x34, 0x81, 0x3f, 0xfa } };
  385. return guid;
  386. }
  387. virtual void get_name(t_uint32 p_index,pfc::string_base & p_out)
  388. {
  389. p_out = "Pause On Lock";
  390. }
  391. virtual bool get_description(t_uint32 p_index,pfc::string_base & p_out)
  392. {
  393. p_out = "Pauses playback when workstation is locked or session is disconnected and resumes on unlock or reconnect.";
  394. return true;
  395. }
  396. virtual GUID get_parent()
  397. {
  398. return mainmenu_groups::playback_etc;
  399. }
  400. virtual bool get_display(t_uint32 p_index,pfc::string_base & p_text,t_uint32 & p_flags)
  401. {
  402. p_flags = ( ( cfg_enabled ) ? flag_checked : 0 );
  403. get_name(p_index,p_text);
  404. return true;
  405. }
  406. virtual void execute(t_uint32 p_index,service_ptr_t<service_base> p_callback)
  407. {
  408. if ( p_index == 0 && core_api::assert_main_thread() )
  409. {
  410. if (cfg_enabled)
  411. {
  412. cfg_enabled = 0;
  413. disable();
  414. }
  415. else
  416. {
  417. cfg_enabled = 1;
  418. enable();
  419. }
  420. }
  421. }
  422. };
  423. static mainmenu_commands_factory_t <mainmenu_command_foolock> g_mainmenu_commands_foolock_factory;