diff --git a/LGTV Companion Service/Service.cpp b/LGTV Companion Service/Service.cpp index f1e6faf..803d9ba 100644 --- a/LGTV Companion Service/Service.cpp +++ b/LGTV Companion Service/Service.cpp @@ -410,11 +410,13 @@ DWORD SvcCtrlHandler(DWORD dwCtrl, DWORD dwEventType, LPVOID lpEventData, LPVOI EventCallbackStatus = NULL;; Log("** System resumed from low power state (Automatic)."); DispatchSystemPowerEvent(SYSTEM_EVENT_RESUMEAUTO); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = true; break; case PBT_APMRESUMESUSPEND: EventCallbackStatus = NULL;; Log("** System resumed from low power state."); DispatchSystemPowerEvent(SYSTEM_EVENT_RESUME); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = true; break; case PBT_APMSUSPEND: @@ -427,16 +429,19 @@ DWORD SvcCtrlHandler(DWORD dwCtrl, DWORD dwEventType, LPVOID lpEventData, LPVOI { Log("** System is shutting down (low power mode)."); DispatchSystemPowerEvent(SYSTEM_EVENT_SUSPEND); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = false; } else if (EventCallbackStatus == SYSTEM_EVENT_UNSURE) { Log("WARNING! Unable to determine if system is shutting down or restarting. Please check 'additional settings' in the UI."); DispatchSystemPowerEvent(SYSTEM_EVENT_UNSURE); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = false; } else { Log("** System is suspending to a low power state."); DispatchSystemPowerEvent(SYSTEM_EVENT_SUSPEND); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = false; } break; case PBT_POWERSETTINGCHANGE: @@ -451,16 +456,19 @@ DWORD SvcCtrlHandler(DWORD dwCtrl, DWORD dwEventType, LPVOID lpEventData, LPVOI { Log("** System requests displays OFF."); DispatchSystemPowerEvent(SYSTEM_EVENT_DISPLAYOFF); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = false; } else if (PBS->Data[0] == 2) { Log("** System requests displays OFF(DIMMED)."); DispatchSystemPowerEvent(SYSTEM_EVENT_DISPLAYDIMMED); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = false; } else { Log("** System requests displays ON."); DispatchSystemPowerEvent(SYSTEM_EVENT_DISPLAYON); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = true; } } else @@ -493,17 +501,20 @@ DWORD SvcCtrlHandler(DWORD dwCtrl, DWORD dwEventType, LPVOID lpEventData, LPVOI { Log("** System is shutting down."); DispatchSystemPowerEvent(SYSTEM_EVENT_SHUTDOWN); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = false; } else if (EventCallbackStatus == SYSTEM_EVENT_UNSURE) { Log("WARNING! Unable to determine if system is shutting down or restarting. Please check 'additional settings in the UI."); DispatchSystemPowerEvent(SYSTEM_EVENT_UNSURE); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = false; } else { //This does happen sometimes, probably for timing reasons when shutting down the system. Log("WARNING! The application did not receive an Event Subscription Callback prior to system shutting down. Unable to determine if system is shutting down or restarting."); DispatchSystemPowerEvent(SYSTEM_EVENT_UNSURE); + Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows = false; } ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 20000); @@ -693,6 +704,10 @@ bool ReadConfigFile() if (!j.empty() && j.is_number()) Prefs.BlankScreenWhenIdleDelay = j.get(); + j = jsonPrefs[JSON_PREFS_NODE][JSON_RDP_POWEROFF]; + if (!j.empty() && j.is_boolean()) + Prefs.PowerOffDuringRDP = j.get(); + Log(st); Log("Configuration file successfully read"); Log(ty); @@ -1064,7 +1079,51 @@ void IPCThread(void) Log("IPC, User is idle."); DispatchSystemPowerEvent(SYSTEM_EVENT_USERIDLE); } - + else if (param == "remoteconnect_busy") + { + if (Prefs.PowerOffDuringRDP) + { + Log("IPC, Remote session connected. User idle management disabled, Powering off managed displays."); + DispatchSystemPowerEvent(SYSTEM_EVENT_DISPLAYOFF); + } + else + Log("IPC, Remote session connected. User idle management disabled."); + } + else if (param == "remoteconnect_idle") + { + if (Prefs.PowerOffDuringRDP) + { + Log("IPC, Remote session connected. User idle management disabled. Powering off managed displays."); + DispatchSystemPowerEvent(SYSTEM_EVENT_DISPLAYOFF); + } + else + { + Log("IPC, Remote session connected. User idle management disabled"); + DispatchSystemPowerEvent(SYSTEM_EVENT_UNBLANK); + } + } + else if (param == "remoteconnect") + { + if (Prefs.PowerOffDuringRDP) + { + Log("IPC, Remote session connected. Powering off managed displays."); + DispatchSystemPowerEvent(SYSTEM_EVENT_DISPLAYOFF); + } + else + Log("IPC, Remote session connected."); + } + else if (param == "remotedisconnect") + { + if (Prefs.DisplayIsCurrentlyRequestedPoweredOnByWindows) + { + Log("IPC, Remote session disconnected. Powering on managed displays."); + DispatchSystemPowerEvent(SYSTEM_EVENT_DISPLAYON); + } + else + { + Log("IPC, Remote session disconnected."); + } + } } else { diff --git a/LGTV Companion Service/Service.h b/LGTV Companion Service/Service.h index ab32346..218ed7b 100644 --- a/LGTV Companion Service/Service.h +++ b/LGTV Companion Service/Service.h @@ -37,7 +37,7 @@ #pragma comment(lib, "Iphlpapi.lib") #define APPNAME L"LGTV Companion" -#define APPVERSION L"1.6.2" +#define APPVERSION L"1.7.0" #define SVCNAME L"LGTVsvc" #define SVCDISPLAYNAME L"LGTV Companion Service" #define SERVICE_PORT "3000" @@ -52,6 +52,7 @@ #define DEFAULT_SHUTDOWN {"shutdown","power off"} #define JSON_IDLEBLANK "BlankWhenIdle" #define JSON_IDLEBLANKDELAY "BlankWhenIdleDelay" +#define JSON_RDP_POWEROFF "PowerOffDuringRDP" #define SERVICE_DEPENDENCIES L"Dhcp\0Dnscache\0LanmanServer\0\0" #define SERVICE_ACCOUNT NULL //L"NT AUTHORITY\\LocalService" @@ -75,6 +76,7 @@ #define SYSTEM_EVENT_USERIDLE 14 #define SYSTEM_EVENT_FORCESETHDMI 15 #define SYSTEM_EVENT_BOOT 16 +#define SYSTEM_EVENT_UNBLANK 17 #define APP_CMDLINE_ON 1 #define APP_CMDLINE_OFF 2 @@ -116,6 +118,9 @@ struct PREFS { int PowerOnTimeout = 40; bool BlankWhenIdle = false; int BlankScreenWhenIdleDelay = 10; + bool PowerOffDuringRDP = false; + bool DisplayIsCurrentlyRequestedPoweredOnByWindows = false; + }; struct SESSIONPARAMETERS { @@ -134,6 +139,7 @@ struct SESSIONPARAMETERS { int BlankScreenWhenIdleDelay = 10; bool SetHDMIInputOnResume = false; int SetHDMIInputOnResumeToNumber = 1; + }; class CSession { @@ -152,7 +158,7 @@ class CSession { bool ThreadedOpDisplayOff = false; bool ThreadedOpDisplaySetHdmiInput = false; time_t ThreadedOpDisplayOffTime = 0; - void TurnOnDisplay(void); + void TurnOnDisplay(bool SendWOL); void TurnOffDisplay(bool forced, bool dimmed, bool blankscreen); void SetDisplayHdmiInput(int HdmiInput); @@ -177,7 +183,7 @@ void Log(std::string); DWORD WINAPI SubCallback(EVT_SUBSCRIBE_NOTIFY_ACTION Action, PVOID UserContext, EVT_HANDLE Event); std::wstring widen(std::string); std::string narrow(std::wstring); -void DisplayPowerOnThread(SESSIONPARAMETERS *, bool *, int); +void DisplayPowerOnThread(SESSIONPARAMETERS *, bool *, int, bool); void DisplayPowerOffThread(SESSIONPARAMETERS*, bool *, bool, bool); void SetDisplayHdmiInputThread(SESSIONPARAMETERS*, bool*, int, int); diff --git a/LGTV Companion Service/Session.cpp b/LGTV Companion Service/Session.cpp index 31d7595..ca1e6b2 100644 --- a/LGTV Companion Service/Session.cpp +++ b/LGTV Companion Service/Session.cpp @@ -156,7 +156,7 @@ void CSession::SetDisplayHdmiInput(int HdmiInput) Log(s); return; } -void CSession::TurnOnDisplay() +void CSession::TurnOnDisplay(bool SendWOL) { string s; @@ -175,7 +175,7 @@ void CSession::TurnOnDisplay() ThreadedOpDisplayOn = true; // ThreadedOperationsTimeStamp = time(0); - thread thread_obj(DisplayPowerOnThread, &Parameters, &ThreadedOpDisplayOn, Parameters.PowerOnTimeout); + thread thread_obj(DisplayPowerOnThread, &Parameters, &ThreadedOpDisplayOn, Parameters.PowerOnTimeout, SendWOL); thread_obj.detach(); } else @@ -233,7 +233,7 @@ void CSession::SystemEvent(DWORD dwMsg, int param) { // forced events are always processed, i.e. the user has issued this command. if (dwMsg == SYSTEM_EVENT_FORCEON) - TurnOnDisplay(); + TurnOnDisplay(true); if (dwMsg == SYSTEM_EVENT_FORCEOFF) TurnOffDisplay(true, false, false); if (dwMsg == SYSTEM_EVENT_FORCESCREENOFF) @@ -279,7 +279,7 @@ void CSession::SystemEvent(DWORD dwMsg, int param) case SYSTEM_EVENT_DISPLAYON: { - TurnOnDisplay(); + TurnOnDisplay(true); }break; case SYSTEM_EVENT_DISPLAYOFF: { @@ -297,18 +297,22 @@ void CSession::SystemEvent(DWORD dwMsg, int param) case SYSTEM_EVENT_USERBUSY: { if(Parameters.BlankWhenIdle) - TurnOnDisplay(); + TurnOnDisplay(true); }break; case SYSTEM_EVENT_BOOT: { if (Parameters.SetHDMIInputOnResume) SetDisplayHdmiInput(Parameters.SetHDMIInputOnResumeToNumber); }break; + case SYSTEM_EVENT_UNBLANK: + { + TurnOnDisplay(false); + }break; default:break; } } // THREAD: Spawned when the device should power ON. This thread manages the pairing key from the display and verifies that the display has been powered on -void DisplayPowerOnThread(SESSIONPARAMETERS * CallingSessionParameters, bool * CallingSessionThreadRunning, int Timeout) +void DisplayPowerOnThread(SESSIONPARAMETERS * CallingSessionParameters, bool * CallingSessionThreadRunning, int Timeout, bool SendWOL) { string screenonmess = "{ \"id\":\"2\",\"type\" : \"request\",\"uri\" : \"ssap://com.webos.service.tvpower/power/turnOnScreen\"}"; @@ -323,8 +327,11 @@ void DisplayPowerOnThread(SESSIONPARAMETERS * CallingSessionParameters, bool * C string handshake; time_t origtim = time(0); - thread wolthread(WOLthread, CallingSessionParameters, CallingSessionThreadRunning, Timeout); - wolthread.detach(); + if (SendWOL) + { + thread wolthread(WOLthread, CallingSessionParameters, CallingSessionThreadRunning, Timeout); + wolthread.detach(); + } // build the appropriate WebOS handshake if (key == "") diff --git a/LGTV Companion Setup/Product.wxs b/LGTV Companion Setup/Product.wxs index e75bfd5..2cc8fdc 100644 --- a/LGTV Companion Setup/Product.wxs +++ b/LGTV Companion Setup/Product.wxs @@ -3,7 +3,7 @@ - + diff --git a/LGTV Companion UI/LGTV Companion UI.cpp b/LGTV Companion UI/LGTV Companion UI.cpp index fca498f..843cecc 100644 --- a/LGTV Companion UI/LGTV Companion UI.cpp +++ b/LGTV Companion UI/LGTV Companion UI.cpp @@ -126,6 +126,11 @@ CHANGELOG v 1.6.0 - Implementation of option for setting HDMI input on system start / resume from low power mode - Command line parameter to set HDMI input added + + v 1.7.0 - More robust operations during RDP remote host + - Option to power off devices during RDP + - Some additional help texts in the options dialog + - Bugfixes and optimisations Todo: Option to manage on active display outputs only @@ -690,6 +695,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) //EnableWindow(GetDlgItem(hWnd, IDOK), false); PostMessage(hWnd, APP_MESSAGE_APPLY, (WPARAM) NULL, NULL); + }break; + case IDC_TEST: + { + PostMessage((HWND) -1, WM_SYSCOMMAND, SC_MONITORPOWER, 2); + }break; case IDC_SPLIT: { @@ -1223,9 +1233,16 @@ LRESULT CALLBACK OptionsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l SendDlgItemMessage(hWnd, IDC_SPIN2, UDM_SETPOS, (WPARAM)NULL, (LPARAM)Prefs.BlankScreenWhenIdleDelay); for (auto& item : Prefs.EventLogRestartString) - str.push_back(widen(item)); + { + if (std::find(str.begin(), str.end(), widen(item)) == str.end()) + str.push_back(widen(item)); + } for (auto& item : Prefs.EventLogShutdownString) - str.push_back(widen(item)); + { + if (std::find(str.begin(), str.end(), widen(item)) == str.end()) + str.push_back(widen(item)); + } + hResults = EvtQuery(NULL, path.c_str(), query.c_str(), EvtQueryChannelPath | EvtQueryReverseDirection); if (hResults) { @@ -1311,6 +1328,7 @@ LRESULT CALLBACK OptionsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l CheckDlgButton(hWnd, IDC_AUTOUPDATE, Prefs.AutoUpdate ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hWnd, IDC_CHECK_BLANK, Prefs.BlankScreenWhenIdle ? BST_CHECKED : BST_UNCHECKED); EnableWindow(GetDlgItem(hWnd, IDC_EDIT_BLANK), Prefs.BlankScreenWhenIdle); + CheckDlgButton(hWnd, IDC_CHECK_RDPBLANK, Prefs.PowerOffDuringRDP ? BST_CHECKED : BST_UNCHECKED); EnableWindow(GetDlgItem(hWnd, IDOK), true); }break; @@ -1355,6 +1373,7 @@ LRESULT CALLBACK OptionsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l }break; case IDC_LOGGING: case IDC_AUTOUPDATE: + case IDC_CHECK_RDPBLANK: { EnableWindow(GetDlgItem(hWnd, IDOK), true); }break; @@ -1377,6 +1396,7 @@ LRESULT CALLBACK OptionsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l Prefs.AutoUpdate = IsDlgButtonChecked(hWnd, IDC_AUTOUPDATE); Prefs.BlankScreenWhenIdle = IsDlgButtonChecked(hWnd, IDC_CHECK_BLANK) == BST_CHECKED; Prefs.BlankScreenWhenIdleDelay = atoi(narrow(GetWndText(GetDlgItem(hWnd, IDC_EDIT_BLANK))).c_str()); + Prefs.PowerOffDuringRDP = IsDlgButtonChecked(hWnd, IDC_CHECK_RDPBLANK) == BST_CHECKED; int count = ListView_GetItemCount(GetDlgItem(hWnd, IDC_LIST)); Prefs.EventLogRestartString.clear(); @@ -1445,6 +1465,13 @@ LRESULT CALLBACK OptionsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l { MessageBox(hWnd, L"This application rely on events in the windows event log to determine whether a reboot or shutdown was initiated by the user.\n\nThese events are localised in the language of your operating system, and the user must therefore assist with manually indicating which strings refers to the system restarting.\n\nPlease put a checkmark for every string which refers to 'restart'", L"Information", MB_OK | MB_ICONINFORMATION); } + // explain the power saving options + if (wParam == IDC_SYSLINK5) + { + MessageBox(hWnd, L"The option to automatically blank the screen triggers in the absence of user input from keyboard, mouse and/or controllers.The difference, when compared to both the screensaver and windows power plan settings, is that those OS implemented power saving functions utilize more obscured variables for determining user idle / busy states, and which can also be programmatically overridden f.e. by games, media players, production software or your web browser, In short, and simplified, this option is a more aggressively configured screen and power saver. Plese note that this feature is incompatible with-, and will therefore be disabled, while the system is remoted into, using RDP.\n\nThe option to turn off the device while the system is being remoted into, using RDP, is useful to avoid the screen displaying a static login screen during a remote RDP session. There is a delay of 10 seconds after RDP connection. Please note that depending on the configuration of your system (primarily time to turn off screen in power options) there may be occasions where the display cannot be woken by using the local mouse and keyboard and you must rely on the remote to see the login screen. It is recommended to combine this option with a short (<30 minutes) time to turn off displays in Windows Power Plan Options.", L"Information", MB_OK | MB_ICONINFORMATION); + + } + }break; case LVN_ITEMCHANGED: { @@ -1551,17 +1578,25 @@ bool ReadConfigFile() j = jsonPrefs[JSON_PREFS_NODE][JSON_EVENT_RESTART_STRINGS]; if (!j.empty() && j.size() > 0) { - Prefs.EventLogRestartString.clear(); + // Prefs.EventLogRestartString.clear(); for (auto& elem : j.items()) - Prefs.EventLogRestartString.push_back(elem.value().get()); + { + string temp = elem.value().get(); + if (std::find(Prefs.EventLogRestartString.begin(), Prefs.EventLogRestartString.end(), temp) == Prefs.EventLogRestartString.end()) + Prefs.EventLogRestartString.push_back(temp); + } } j = jsonPrefs[JSON_PREFS_NODE][JSON_EVENT_SHUTDOWN_STRINGS]; if (!j.empty() && j.size() > 0) { - Prefs.EventLogShutdownString.clear(); + // Prefs.EventLogShutdownString.clear(); for (auto& elem : j.items()) - Prefs.EventLogShutdownString.push_back(elem.value().get()); + { + string temp = elem.value().get(); + if (std::find(Prefs.EventLogShutdownString.begin(), Prefs.EventLogShutdownString.end(), temp) == Prefs.EventLogShutdownString.end()) + Prefs.EventLogShutdownString.push_back(temp); + } } j = jsonPrefs[JSON_PREFS_NODE][JSON_VERSION]; @@ -1588,6 +1623,10 @@ bool ReadConfigFile() if (!j.empty() && j.is_number()) Prefs.BlankScreenWhenIdleDelay = j.get(); + j = jsonPrefs[JSON_PREFS_NODE][JSON_RDP_POWEROFF]; + if (!j.empty() && j.is_boolean()) + Prefs.PowerOffDuringRDP = j.get(); + return true; } } @@ -1846,6 +1885,7 @@ void WriteConfigFile(void) prefs[JSON_PREFS_NODE][JSON_AUTOUPDATE] = (bool)Prefs.AutoUpdate; prefs[JSON_PREFS_NODE][JSON_IDLEBLANK] = (bool)Prefs.BlankScreenWhenIdle; prefs[JSON_PREFS_NODE][JSON_IDLEBLANKDELAY] = (int)Prefs.BlankScreenWhenIdleDelay; + prefs[JSON_PREFS_NODE][JSON_RDP_POWEROFF] = (bool)Prefs.PowerOffDuringRDP; for (auto& item : Prefs.EventLogRestartString) prefs[JSON_PREFS_NODE][JSON_EVENT_RESTART_STRINGS].push_back(item); diff --git a/LGTV Companion UI/LGTV Companion UI.h b/LGTV Companion UI/LGTV Companion UI.h index 555e0d2..f085df5 100644 --- a/LGTV Companion UI/LGTV Companion UI.h +++ b/LGTV Companion UI/LGTV Companion UI.h @@ -53,7 +53,7 @@ #define APPNAME_SHORT L"LGTVcomp" #define APPNAME_FULL L"LGTV Companion" -#define APP_VERSION L"1.6.2" +#define APP_VERSION L"1.7.0" #define WINDOW_CLASS_UNIQUE L"YOLOx0x0x0181818" #define NOTIFY_NEW_COMMANDLINE 1 @@ -68,6 +68,7 @@ #define JSON_IDLEBLANKDELAY "BlankWhenIdleDelay" #define DEFAULT_RESTART {"restart"} #define DEFAULT_SHUTDOWN {"shutdown","power off"} +#define JSON_RDP_POWEROFF "PowerOffDuringRDP" #define APP_MESSAGE_ADD WM_USER+1 #define APP_MESSAGE_MANAGE WM_USER+2 @@ -108,6 +109,7 @@ struct PREFS { bool ResetAPIkeys = false; bool BlankScreenWhenIdle = false; int BlankScreenWhenIdleDelay = 10; + bool PowerOffDuringRDP = false; }; struct SESSIONPARAMETERS { std::string DeviceId; diff --git a/LGTV Companion UI/LGTV Companion UI.rc b/LGTV Companion UI/LGTV Companion UI.rc index 1499d96..96bd0c9 100644 Binary files a/LGTV Companion UI/LGTV Companion UI.rc and b/LGTV Companion UI/LGTV Companion UI.rc differ diff --git a/LGTV Companion UI/resource.h b/LGTV Companion UI/resource.h index 6767cbc..569ffe2 100644 --- a/LGTV Companion UI/resource.h +++ b/LGTV Companion UI/resource.h @@ -32,8 +32,10 @@ #define IDC_SYSLINK 1014 #define IDC_EDIT_BLANK 1015 #define IDC_SPIN2 1016 +#define IDC_CHECK_RDPBLANK 1017 #define IDC_LIST 1018 #define IDC_SYSLINK3 1019 +#define IDC_SYSLINK5 1020 #define IDC_NEWVERSION 1021 #define IDC_RADIO1 1022 #define IDC_DONATE 1022 @@ -44,8 +46,8 @@ #define IDC_HDMI_INPUT_NUMBER 1027 #define IDC_HDMI_INPUT_NUMBER_SPIN 1028 #define IDC_SET_HDMI_INPUT_CHECKBOX 1029 +#define IDC_TEST 1029 #define IDC_SET_HDMI_INPUT_NUMBER 1030 -#define IDC_HDMI_INPUT_NUMBER_SPIN2 1031 #define IDC_SET_HDMI_INPUT_SPIN 1031 #define ID_ADD_MANAGE 32771 #define ID_ADD_MANAGE32772 32772 @@ -73,7 +75,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 144 #define _APS_NEXT_COMMAND_VALUE 32788 -#define _APS_NEXT_CONTROL_VALUE 1029 +#define _APS_NEXT_CONTROL_VALUE 1030 #define _APS_NEXT_SYMED_VALUE 110 #endif #endif diff --git a/LGTV Companion User/Daemon.cpp b/LGTV Companion User/Daemon.cpp index 5a7e592..c22f2e4 100644 --- a/LGTV Companion User/Daemon.cpp +++ b/LGTV Companion User/Daemon.cpp @@ -68,8 +68,10 @@ int APIENTRY wWinMain(_In_ HINSTANCE Instance, if (!WinToast::instance()->initialize(&error)) { wchar_t buf[250]; swprintf_s(buf, L"Failed to initialize Toast Notifications :%d", error); - MessageBox(NULL, buf, L"Error", MB_OK); + Log(buf); } + else + Prefs.ToastInitialised = true; // if the app is already running as another process, tell the other process to exit MessageExistingProcess(); @@ -102,7 +104,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE Instance, SetTimer(hMainWnd, TIMER_MAIN, TIMER_MAIN_DELAY_WHEN_BUSY, (TIMERPROC)NULL); SetTimer(hMainWnd, TIMER_IDLE, Prefs.BlankScreenWhenIdleDelay * 60 * 1000, (TIMERPROC)NULL); } - + wstring startupmess = WindowTitle; startupmess += L" is running."; Log(startupmess); @@ -110,6 +112,8 @@ int APIENTRY wWinMain(_In_ HINSTANCE Instance, HPOWERNOTIFY rsrn =RegisterSuspendResumeNotification(hMainWnd, DEVICE_NOTIFY_WINDOW_HANDLE); HPOWERNOTIFY rpsn = RegisterPowerSettingNotification(hMainWnd, &(GUID_CONSOLE_DISPLAY_STATE), DEVICE_NOTIFY_WINDOW_HANDLE); + WTSRegisterSessionNotification(hMainWnd, NOTIFY_FOR_ALL_SESSIONS); + CommunicateWithService("-daemon started"); // message loop: @@ -121,20 +125,22 @@ int APIENTRY wWinMain(_In_ HINSTANCE Instance, DispatchMessage(&msg); } } - - if (idToastNewversion || idToastFirstrun ) + if (Prefs.ToastInitialised) { - if(idToastFirstrun) - WinToast::instance()->hideToast(idToastFirstrun); - if (idToastNewversion) - WinToast::instance()->hideToast(idToastNewversion); - Sleep(500); - WinToast::instance()->clear(); + if (idToastNewversion || idToastFirstrun) + { + if (idToastFirstrun) + WinToast::instance()->hideToast(idToastFirstrun); + if (idToastNewversion) + WinToast::instance()->hideToast(idToastNewversion); + Sleep(500); + WinToast::instance()->clear(); + } } UnregisterSuspendResumeNotification(rsrn); UnregisterPowerSettingNotification(rpsn); - + WTSUnRegisterSessionNotification(hMainWnd); return (int)msg.wParam; } @@ -146,7 +152,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { case WM_INITDIALOG: { - if (bFirstRun) + + if (bFirstRun && Prefs.ToastInitialised) { TCHAR buffer[MAX_PATH] = { 0 }; GetModuleFileName(NULL, buffer, MAX_PATH); @@ -187,30 +194,32 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) s += NEWRELEASELINK; Log(s); - - TCHAR buffer[MAX_PATH] = { 0 }; - GetModuleFileName(NULL, buffer, MAX_PATH); - wstring imgpath = buffer; - std::wstring::size_type pos = imgpath.find_last_of(L"\\/"); - imgpath = imgpath.substr(0, pos + 1); - imgpath += L"mainicon.ico"; - - WinToastTemplate templ; - templ = WinToastTemplate(WinToastTemplate::ImageAndText02); - templ.setImagePath(imgpath); - - templ.setTextField(L"Yay! A new version is available.", WinToastTemplate::FirstLine); - templ.setTextField(L"Please install the new version to keep up to date with bugfixes and features.", WinToastTemplate::SecondLine); - - templ.addAction(L"Download"); - - // Read the additional options section in the article - templ.setDuration(WinToastTemplate::Duration::Long); - templ.setAudioOption(WinToastTemplate::AudioOption::Default); - idToastNewversion = WinToast::instance()->showToast(templ, &m_WinToastHandler); - if (idToastNewversion == -1L) + if (Prefs.ToastInitialised) { - Log(L"Failed to show toast notification about updated version!"); + TCHAR buffer[MAX_PATH] = { 0 }; + GetModuleFileName(NULL, buffer, MAX_PATH); + wstring imgpath = buffer; + std::wstring::size_type pos = imgpath.find_last_of(L"\\/"); + imgpath = imgpath.substr(0, pos + 1); + imgpath += L"mainicon.ico"; + + WinToastTemplate templ; + templ = WinToastTemplate(WinToastTemplate::ImageAndText02); + templ.setImagePath(imgpath); + + templ.setTextField(L"Yay! A new version is available.", WinToastTemplate::FirstLine); + templ.setTextField(L"Please install the new version to keep up to date with bugfixes and features.", WinToastTemplate::SecondLine); + + templ.addAction(L"Download"); + + // Read the additional options section in the article + templ.setDuration(WinToastTemplate::Duration::Long); + templ.setAudioOption(WinToastTemplate::AudioOption::Default); + idToastNewversion = WinToast::instance()->showToast(templ, &m_WinToastHandler); + if (idToastNewversion == -1L) + { + Log(L"Failed to show toast notification about updated version!"); + } } } }break; @@ -251,6 +260,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { dwLastInputTick = lii.dwTime; bIdlePreventEarlyWakeup = true; + Prefs.DisableSendingViaIPC = false; } else { @@ -276,6 +286,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) bIdlePreventEarlyWakeup = false; } } + return 0; }break; case TIMER_IDLE: @@ -289,6 +300,21 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return 0; }break; + case TIMER_RDP: + { + KillTimer(hWnd, TIMER_RDP); + if (Prefs.BlankScreenWhenIdle) + { + if (bIdle) + CommunicateWithService("-daemon remoteconnect_idle"); + else + CommunicateWithService("-daemon remoteconnect_busy"); + } + else + CommunicateWithService("-daemon remoteconnect"); + Prefs.DisableSendingViaIPC = true; + + }break; default:break; } }break; @@ -357,6 +383,20 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) default:break; } } + case WM_WTSSESSION_CHANGE: + { + if (wParam == WTS_REMOTE_CONNECT) + { + SetTimer(hMainWnd, TIMER_RDP, TIMER_RDP_DELAY, (TIMERPROC)NULL); + + } + else if (wParam == WTS_REMOTE_DISCONNECT) + { + Prefs.DisableSendingViaIPC = false; + CommunicateWithService("-daemon remotedisconnect"); + Prefs.DisableSendingViaIPC = true; // to prevent user idle screen blanking on login screen, which cannot be unblanked without using the remote. + } + }break; case WM_COPYDATA: { if (!lParam) @@ -374,16 +414,20 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) }break; case WM_ENDSESSION: //PC is shutting down so let's do some cleaning up { - if (idToastNewversion || idToastFirstrun) + if (Prefs.ToastInitialised) { - if (idToastFirstrun) - WinToast::instance()->hideToast(idToastFirstrun); - if (idToastNewversion) - WinToast::instance()->hideToast(idToastNewversion); - Sleep(500); - WinToast::instance()->clear(); + if (idToastNewversion || idToastFirstrun) + { + if (idToastFirstrun) + WinToast::instance()->hideToast(idToastFirstrun); + if (idToastNewversion) + WinToast::instance()->hideToast(idToastNewversion); + Sleep(500); + WinToast::instance()->clear(); + } } }break; + case WM_DESTROY: { PostQuitMessage(0); @@ -507,23 +551,25 @@ void CommunicateWithService(string input) if (input == "") return; + if (!Prefs.DisableSendingViaIPC) + { + hPipe = CreateFile(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - hPipe = CreateFile(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hPipe != INVALID_HANDLE_VALUE) + { + WriteFile(hPipe, + input.c_str(), + (DWORD)input.length() + 1, // = length of string + terminating '\0' !!! + &dwWritten, + NULL); + Log(widen(input)); + } + else + Log(L"Failed to connect to named pipe. Service may be stopped."); - if (hPipe != INVALID_HANDLE_VALUE) - { - WriteFile(hPipe, - input.c_str(), - (DWORD)input.length() + 1, // = length of string + terminating '\0' !!! - &dwWritten, - NULL); - Log(widen(input)); + if (hPipe != INVALID_HANDLE_VALUE) + CloseHandle(hPipe); } - else - Log(L"Failed to connect to named pipe. Service may be stopped."); - - if (hPipe != INVALID_HANDLE_VALUE) - CloseHandle(hPipe); } void Log(wstring input) diff --git a/LGTV Companion User/Daemon.h b/LGTV Companion User/Daemon.h index 6434edd..464306b 100644 --- a/LGTV Companion User/Daemon.h +++ b/LGTV Companion User/Daemon.h @@ -1,5 +1,6 @@ #pragma once #pragma comment(lib, "urlmon.lib") +#pragma comment(lib, "Wtsapi32.lib") #if defined _M_IX86 #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") @@ -24,9 +25,10 @@ #include #include #include -#include +#include #include #include +#include #include "resource.h" #include "nlohmann/json.hpp" @@ -35,15 +37,18 @@ #define APPNAME_SHORT L"LGTVdaemon" #define APP_PATH L"LGTV Companion" #define APPNAME_FULL L"LGTV Companion Daemon" -#define APP_VERSION L"1.6.2" +#define APP_VERSION L"1.7.0" #define WINDOW_CLASS_UNIQUE L"YOLOx0x0x0181818" #define NOTIFY_NEW_PROCESS 1 #define TIMER_MAIN 18 #define TIMER_IDLE 19 +#define TIMER_RDP 20 #define TIMER_MAIN_DELAY_WHEN_BUSY 2000 #define TIMER_MAIN_DELAY_WHEN_IDLE 100 +#define TIMER_RDP_DELAY 10000 + #define APP_NEW_VERSION WM_USER+9 @@ -65,7 +70,8 @@ struct PREFS { int BlankScreenWhenIdleDelay = 10; bool Logging = false; int version = 2; - + bool ToastInitialised = false; + bool DisableSendingViaIPC = false; }; class WinToastHandler : public WinToastLib::IWinToastHandler @@ -94,4 +100,4 @@ std::string narrow(std::wstring); std::vector stringsplit(std::string, std::string); void CommunicateWithService(std::string); void VersionCheckThread(HWND); -void Log(std::wstring input); +void Log(std::wstring input); \ No newline at end of file