Forum > Windows

Is Screensaver running? Is monitor off?

(1/2) > >>

TiredOldMan:
I have a program (just a personal project) that watches for a few things and gives me an audio alert in certain cases. 

When I leave the computer, I have it set to run a screensaver after 15 minutes, and then to turn off the monitor after that.  (Not sleep mode, just the power settings "turn off the display" that makes it black.)

It tends to run all the time, but of course, I go to sleep.  And often notice it in the middle of the night, from the other room.

Is there a way I can tell, using Lazarus code, whether the monitor has been shut off?  Whether the screensaver is running?

GetMem:
Hi TiredOldMan,

Welcome to the forum!

--- Quote ---Is there a way I can tell, using Lazarus code, whether the monitor has been shut off?  Whether the screensaver is running?
--- End quote ---
Unfortunately there is no reliable way to tell if the monitor is off or the screensaver is running. The issue is not related to Lazarus, Microsoft does not provide a clear API.
However you can try the following:

1. Detect screensaver status
   a. Create a new project
   b. Add a memo to your form
   c. Add a FormCreate event

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---uses jwawindows; var  PrevWndProc: WNDPROC; function WndCallback(AHWND: HWND; uMsg: UINT; wParam: WParam; lParam: LParam): LRESULT; stdcall;begin  if uMsg = WM_SYSCOMMAND then  begin    case wParam of      SC_SCREENSAVE:            begin             Form1.Memo1.Lines.Add('Screen saver started');             //make sure you don't block the hook chain with a blocking function like showmessage           end;    end;  end;  Result := CallWindowProc(PrevWndProc, Ahwnd, uMsg, WParam, LParam);end; procedure TForm1.FormCreate(Sender: TObject);begin  PrevWndProc := jwawindows.WNDPROC(SetWindowLongPtr(Self.Handle, GWL_WNDPROC, PtrUInt(@WndCallback))); end;
1. Detect monitor On/Off
   a. Create a new project
   b. Add a memo to your form
   c. Add a timer to your form, create an OnTimer event
   c. Add a FormCreate event

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---uses jwawindows; type  TMonitorState = (msUnknown, msOff, msOn); var  MonitorState: TMonitorState; function CheckMonitorState: TMonitorState;var  P: Point;  Monitor: HMONITOR;  Res: BOOL;begin  Result := msUnknown;  P.X := 0;  P.Y := 0;  Monitor := MonitorFromPoint(P, MONITOR_DEFAULTTOPRIMARY);  if Monitor <> NULL then  begin    Res := False;    if GetDevicePowerState(Monitor, Res) then    begin      case Res of        False: Result := msOff;        True: Result := msOn;      end;    end;  end;end; procedure TForm1.FormCreate(Sender: TObject);begin  MonitorState := CheckMonitorState;end; procedure TForm1.Timer1Timer(Sender: TObject);var  Temp_MonitorState: TMonitorState;begin  Temp_MonitorState := CheckMonitorState;  if MonitorState <> Temp_MonitorState then  begin    Temp_MonitorState := MonitorState;    case MonitorState of      msUnknown: Memo1.Lines.Add('Monitor state unknown');      msOff: Memo1.Lines.Add('Monitor state off');      msOn: Memo1.Lines.Add('Monitor state on');    end;  end;end;

For me the screensaver status detection works fine, the monitor state doesn't. The later depends on your hardware.

ASerge:

--- Quote from: GetMem on January 08, 2022, 10:34:48 am ---However you can try the following:

--- End quote ---
A few remarks:
1. For the WM_SYSCOMMAND message, wParam must be masked with $FFF0. Source WM_SYSCOMMAND.
2. SetWindowLongPtr is a very old message interception technique. Starting with XP, windows subclassing is used.
3. From GetDevicePowerState "This function cannot be used to query the power state of a display device". In most cases, this is not the true, but for some manufacturers it is true (depends on the device driver).
This is my example:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---uses Windows, CommCtrl; type  PPOWERBROADCAST_SETTING = ^POWERBROADCAST_SETTING;  POWERBROADCAST_SETTING = record    PowerSetting: GUID;    DataLength: DWORD;    Data: array[0..0] of UCHAR;  end; const  GUID_CONSOLE_DISPLAY_STATE: TGuid = '{6FE69556-704A-47A0-8F24-C28D936FDA47}';  GUID_MONITOR_POWER_ON: TGuid = '{02731015-4510-4526-99E6-E5A17EBD1AEA}';  CSomeSubclassId = 1; function NewWndProc(Wnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM; uISubClass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall;var  Form: TForm1 absolute dwRefData;   procedure LogEvent(const Event: string);  begin    Form.Memo1.Append(Event + ' at ' + DateTimeToStr(Now));  end; const  WM_POWERBROADCAST = 536;  PBT_POWERSETTINGCHANGE = 32787;var  Event: PPOWERBROADCAST_SETTING absolute lParam;begin  case uMsg of    WM_SYSCOMMAND:      if wParam and $FFF0 = SC_SCREENSAVE then        LogEvent('Screen saver started');    WM_POWERBROADCAST:      if (wParam = PBT_POWERSETTINGCHANGE)        and (IsEqualGUID(Event^.PowerSetting, GUID_CONSOLE_DISPLAY_STATE)          or IsEqualGUID(Event^.PowerSetting, GUID_MONITOR_POWER_ON)) then        begin          case Event^.Data[0] of            0:              LogEvent('The monitor is off');            1:              LogEvent('The monitor is on');            2:              LogEvent('The monitor is dimmed');          end;        end;  end;  Result := DefSubclassProc(Wnd, uMsg, wParam, lParam);end; procedure TForm1.Button1Click(Sender: TObject);begin  RegisterPowerHook;end; procedure TForm1.Button2Click(Sender: TObject);begin  UnregisterPowerHook;end; procedure TForm1.FormDestroy(Sender: TObject);begin  UnregisterPowerHook;end; function RegisterPowerSettingNotification(hRecipient: THandle; PowerSettingGuid: PGuid; Flags: DWORD): Pointer; stdcall; external User32; function UnregisterPowerSettingNotification(Handle: Pointer): BOOL; stdcall; external User32; procedure TForm1.RegisterPowerHook;const  DEVICE_NOTIFY_WINDOW_HANDLE = 0;begin  if not Assigned(PNewRegStateInfo) then    PNewRegStateInfo := RegisterPowerSettingNotification(Handle, @GUID_CONSOLE_DISPLAY_STATE, DEVICE_NOTIFY_WINDOW_HANDLE);  if not Assigned(PNewRegStateInfo) then // check PNewRegStateInfo not POldRegStateInfo    POldRegStateInfo := RegisterPowerSettingNotification(Handle, @GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_WINDOW_HANDLE);  SetWindowSubclass(Handle, @NewWndProc, CSomeSubclassId, DWORD_PTR(Self));end; procedure TForm1.UnregisterPowerHook;begin  RemoveWindowSubclass(Handle, @NewWndProc, CSomeSubclassId);  if Assigned(PNewRegStateInfo) then  begin    UnregisterPowerSettingNotification(PNewRegStateInfo);    PNewRegStateInfo := nil;  end;  if Assigned(POldRegStateInfo) then  begin    UnregisterPowerSettingNotification(POldRegStateInfo);    POldRegStateInfo := nil;  end;end;

GetMem:
@ASerge
Thanks! Cool stuff. I will run a few tests tomorrow and report back.

TiredOldMan:
Haven't had a chance to play with this yet, but I did want to say thanks for the responses.

Navigation

[0] Message Index

[#] Next page

Go to full version