Recent

Author Topic: Is Screensaver running? Is monitor off?  (Read 4873 times)

TiredOldMan

  • Newbie
  • Posts: 4
Is Screensaver running? Is monitor off?
« on: January 08, 2022, 12:59:39 am »
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?


balazsszekely

  • Guest
Re: Is Screensaver running? Is monitor off?
« Reply #1 on: January 08, 2022, 10:34:48 am »
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?
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  [Select][+][-]
  1. uses jwawindows;
  2.  
  3. var
  4.   PrevWndProc: WNDPROC;
  5.  
  6. function WndCallback(AHWND: HWND; uMsg: UINT; wParam: WParam; lParam: LParam): LRESULT; stdcall;
  7. begin
  8.   if uMsg = WM_SYSCOMMAND then
  9.   begin
  10.     case wParam of
  11.       SC_SCREENSAVE:
  12.            begin
  13.              Form1.Memo1.Lines.Add('Screen saver started');
  14.              //make sure you don't block the hook chain with a blocking function like showmessage
  15.            end;
  16.     end;
  17.   end;
  18.   Result := CallWindowProc(PrevWndProc, Ahwnd, uMsg, WParam, LParam);
  19. end;
  20.  
  21. procedure TForm1.FormCreate(Sender: TObject);
  22. begin
  23.   PrevWndProc := jwawindows.WNDPROC(SetWindowLongPtr(Self.Handle, GWL_WNDPROC, PtrUInt(@WndCallback)));
  24. 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  [Select][+][-]
  1. uses jwawindows;
  2.  
  3. type
  4.   TMonitorState = (msUnknown, msOff, msOn);
  5.  
  6. var
  7.   MonitorState: TMonitorState;
  8.  
  9. function CheckMonitorState: TMonitorState;
  10. var
  11.   P: Point;
  12.   Monitor: HMONITOR;
  13.   Res: BOOL;
  14. begin
  15.   Result := msUnknown;
  16.   P.X := 0;
  17.   P.Y := 0;
  18.   Monitor := MonitorFromPoint(P, MONITOR_DEFAULTTOPRIMARY);
  19.   if Monitor <> NULL then
  20.   begin
  21.     Res := False;
  22.     if GetDevicePowerState(Monitor, Res) then
  23.     begin
  24.       case Res of
  25.         False: Result := msOff;
  26.         True: Result := msOn;
  27.       end;
  28.     end;
  29.   end;
  30. end;
  31.  
  32. procedure TForm1.FormCreate(Sender: TObject);
  33. begin
  34.   MonitorState := CheckMonitorState;
  35. end;
  36.  
  37. procedure TForm1.Timer1Timer(Sender: TObject);
  38. var
  39.   Temp_MonitorState: TMonitorState;
  40. begin
  41.   Temp_MonitorState := CheckMonitorState;
  42.   if MonitorState <> Temp_MonitorState then
  43.   begin
  44.     Temp_MonitorState := MonitorState;
  45.     case MonitorState of
  46.       msUnknown: Memo1.Lines.Add('Monitor state unknown');
  47.       msOff: Memo1.Lines.Add('Monitor state off');
  48.       msOn: Memo1.Lines.Add('Monitor state on');
  49.     end;
  50.   end;
  51. end;


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

ASerge

  • Hero Member
  • *****
  • Posts: 2246
Re: Is Screensaver running? Is monitor off?
« Reply #2 on: January 08, 2022, 11:11:56 pm »
However you can try the following:
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  [Select][+][-]
  1. uses Windows, CommCtrl;
  2.  
  3. type
  4.   PPOWERBROADCAST_SETTING = ^POWERBROADCAST_SETTING;
  5.   POWERBROADCAST_SETTING = record
  6.     PowerSetting: GUID;
  7.     DataLength: DWORD;
  8.     Data: array[0..0] of UCHAR;
  9.   end;
  10.  
  11. const
  12.   GUID_CONSOLE_DISPLAY_STATE: TGuid = '{6FE69556-704A-47A0-8F24-C28D936FDA47}';
  13.   GUID_MONITOR_POWER_ON: TGuid = '{02731015-4510-4526-99E6-E5A17EBD1AEA}';
  14.   CSomeSubclassId = 1;
  15.  
  16. function NewWndProc(Wnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM; uISubClass: UINT_PTR; dwRefData: DWORD_PTR): LRESULT; stdcall;
  17. var
  18.   Form: TForm1 absolute dwRefData;
  19.  
  20.   procedure LogEvent(const Event: string);
  21.   begin
  22.     Form.Memo1.Append(Event + ' at ' + DateTimeToStr(Now));
  23.   end;
  24.  
  25. const
  26.   WM_POWERBROADCAST = 536;
  27.   PBT_POWERSETTINGCHANGE = 32787;
  28. var
  29.   Event: PPOWERBROADCAST_SETTING absolute lParam;
  30. begin
  31.   case uMsg of
  32.     WM_SYSCOMMAND:
  33.       if wParam and $FFF0 = SC_SCREENSAVE then
  34.         LogEvent('Screen saver started');
  35.     WM_POWERBROADCAST:
  36.       if (wParam = PBT_POWERSETTINGCHANGE)
  37.         and (IsEqualGUID(Event^.PowerSetting, GUID_CONSOLE_DISPLAY_STATE)
  38.           or IsEqualGUID(Event^.PowerSetting, GUID_MONITOR_POWER_ON)) then
  39.         begin
  40.           case Event^.Data[0] of
  41.             0:
  42.               LogEvent('The monitor is off');
  43.             1:
  44.               LogEvent('The monitor is on');
  45.             2:
  46.               LogEvent('The monitor is dimmed');
  47.           end;
  48.         end;
  49.   end;
  50.   Result := DefSubclassProc(Wnd, uMsg, wParam, lParam);
  51. end;
  52.  
  53. procedure TForm1.Button1Click(Sender: TObject);
  54. begin
  55.   RegisterPowerHook;
  56. end;
  57.  
  58. procedure TForm1.Button2Click(Sender: TObject);
  59. begin
  60.   UnregisterPowerHook;
  61. end;
  62.  
  63. procedure TForm1.FormDestroy(Sender: TObject);
  64. begin
  65.   UnregisterPowerHook;
  66. end;
  67.  
  68. function RegisterPowerSettingNotification(hRecipient: THandle; PowerSettingGuid: PGuid; Flags: DWORD): Pointer; stdcall; external User32;
  69.  
  70. function UnregisterPowerSettingNotification(Handle: Pointer): BOOL; stdcall; external User32;
  71.  
  72. procedure TForm1.RegisterPowerHook;
  73. const
  74.   DEVICE_NOTIFY_WINDOW_HANDLE = 0;
  75. begin
  76.   if not Assigned(PNewRegStateInfo) then
  77.     PNewRegStateInfo := RegisterPowerSettingNotification(Handle, @GUID_CONSOLE_DISPLAY_STATE, DEVICE_NOTIFY_WINDOW_HANDLE);
  78.   if not Assigned(PNewRegStateInfo) then // check PNewRegStateInfo not POldRegStateInfo
  79.     POldRegStateInfo := RegisterPowerSettingNotification(Handle, @GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_WINDOW_HANDLE);
  80.   SetWindowSubclass(Handle, @NewWndProc, CSomeSubclassId, DWORD_PTR(Self));
  81. end;
  82.  
  83. procedure TForm1.UnregisterPowerHook;
  84. begin
  85.   RemoveWindowSubclass(Handle, @NewWndProc, CSomeSubclassId);
  86.   if Assigned(PNewRegStateInfo) then
  87.   begin
  88.     UnregisterPowerSettingNotification(PNewRegStateInfo);
  89.     PNewRegStateInfo := nil;
  90.   end;
  91.   if Assigned(POldRegStateInfo) then
  92.   begin
  93.     UnregisterPowerSettingNotification(POldRegStateInfo);
  94.     POldRegStateInfo := nil;
  95.   end;
  96. end;

balazsszekely

  • Guest
Re: Is Screensaver running? Is monitor off?
« Reply #3 on: January 08, 2022, 11:30:18 pm »
@ASerge
Thanks! Cool stuff. I will run a few tests tomorrow and report back.

TiredOldMan

  • Newbie
  • Posts: 4
Re: Is Screensaver running? Is monitor off?
« Reply #4 on: January 09, 2022, 08:37:50 am »
Haven't had a chance to play with this yet, but I did want to say thanks for the responses.

balazsszekely

  • Guest
Re: Is Screensaver running? Is monitor off?
« Reply #5 on: January 09, 2022, 10:54:04 am »
@ASerge
I had to wrestle with SetWindowSubclass, RemoveWindowSubclass, DefSubclassProc APIs. Anyways the project compiles fine now, however the monitor off is still not detected at my side. Does it work for you?

@TiredOldMan
Quote
Haven't had a chance to play with this yet, but I did want to say thanks for the responses
Please test attached project.

ASerge

  • Hero Member
  • *****
  • Posts: 2246
Re: Is Screensaver running? Is monitor off?
« Reply #6 on: January 09, 2022, 04:43:31 pm »
@ASerge
Anyways the project compiles fine now, however the monitor off is still not detected at my side. Does it work for you?
It works for me.

 

TinyPortal © 2005-2018