Recent

Author Topic: How to shutdown computer when idle  (Read 1800 times)

nikel

  • Full Member
  • ***
  • Posts: 210
How to shutdown computer when idle
« on: August 10, 2024, 09:00:48 am »
Hello, I'm trying to shutdown my computer if idle 10 seconds. Here's my code:

Code: Pascal  [Select][+][-]
  1. program detectidle;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$typedaddress on}
  5.  
  6. uses
  7.   {$IFDEF UNIX}
  8.   cthreads,
  9.   {$ENDIF}
  10.   Classes, SysUtils, CustApp,
  11.   { you can add units after this }
  12.   Lcl, ExtCtrls, Process;
  13.  
  14. type
  15.  
  16.   { TDetectIdle }
  17.  
  18.   TDetectIdle = class(TCustomApplication)
  19.   protected
  20.     procedure DoRun; override;
  21.   public
  22.     constructor Create(TheOwner: TComponent); override;
  23.     destructor Destroy; override;
  24.     procedure Timer_ItTimer(Sender: TObject); virtual;
  25.     procedure WriteHelp; virtual;
  26.   end;
  27.  
  28. { TDetectIdle }
  29.  
  30. procedure TDetectIdle.DoRun;
  31. var
  32.   ErrorMsg     : string;
  33.   Timer_It     : TIdleTimer;
  34.  
  35. begin
  36.   // quick check parameters
  37.   ErrorMsg:=CheckOptions('h', 'help');
  38.   if ErrorMsg<>'' then begin
  39.     ShowException(Exception.Create(ErrorMsg));
  40.     Terminate;
  41.     Exit;
  42.   end;
  43.  
  44.   // parse parameters
  45.   if HasOption('h', 'help') then begin
  46.     WriteHelp;
  47.     Terminate;
  48.     Exit;
  49.   end;
  50.  
  51.   { add your program here }
  52.  
  53.   Timer_It:=TIdleTimer.Create(nil);
  54.   with Timer_It do
  55.   begin
  56.     Enabled:=False;
  57.     AutoEnabled:=True;
  58.     Interval:=10000;
  59.     OnTimer:=@Timer_ItTimer;
  60.   end;
  61.  
  62.   // stop program loop
  63.   // Terminate;
  64. end;
  65.  
  66. constructor TDetectIdle.Create(TheOwner: TComponent);
  67. begin
  68.   inherited Create(TheOwner);
  69.   StopOnException:=True;
  70. end;
  71.  
  72. destructor TDetectIdle.Destroy;
  73. begin
  74.   inherited Destroy;
  75. end;
  76.  
  77. procedure TDetectIdle.Timer_ItTimer(Sender: TObject);
  78. var
  79.   StrPtr: ^string;
  80. begin
  81.   New(StrPtr);
  82.  
  83.   if RunCommand('/bin/bash',['-c', 'shutdown -h now'], StrPtr^) then
  84.   begin
  85.  
  86.   end;
  87.  
  88.   Dispose(StrPtr);
  89. end;
  90.  
  91. procedure TDetectIdle.WriteHelp;
  92. begin
  93.   { add your help code here }
  94.   writeln('Usage: ', ExeName, ' -h');
  95. end;
  96.  
  97. var
  98.   Application  : TDetectIdle;
  99.  
  100. begin
  101.   Application:=TDetectIdle.Create(nil);
  102.   Application.Title:='Detect Idle';
  103.   Application.Run;
  104.   Application.Free;
  105. end.
  106.  

This doesn't shutdown and loops within function DoRun. How can I fix this?

MarkMLl

  • Hero Member
  • *****
  • Posts: 7622
Re: How to shutdown computer when idle
« Reply #1 on: August 10, 2024, 09:14:11 am »
Does that shutdown command work when you type it into a shell?

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

TRon

  • Hero Member
  • *****
  • Posts: 3234
Re: How to shutdown computer when idle
« Reply #2 on: August 10, 2024, 10:17:38 am »
afaik there are possible two issues there.
1) I am not sure if the TIdleTimer works as there is no message loop. For example a normal TTimer requires a thread for that,
2) in your dorun method override make a loop that waits for the terminated status to be true.

And to backup markMLI's question, I require admin rights to be able to do that (but it might vary per distribution).

edit: And I am a bit confused to see ...
Code: Pascal  [Select][+][-]
  1. var
  2.   StrPtr: ^string;
  3.  
... that declaration. Any reason why ?
« Last Edit: August 10, 2024, 10:26:55 am by TRon »
All software is open source (as long as you can read assembler)

TRon

  • Hero Member
  • *****
  • Posts: 3234
Re: How to shutdown computer when idle
« Reply #3 on: August 10, 2024, 10:59:38 am »
I just verified that the idletimer does not get fired at all (because there is no message(pump)loop in a terminal application so the idle state is never 'reached/received').

Besides previous comments your current code indefinitely creates instances of TIdleTimer (see also my earlier made remark about adding a loop).

I don't think you can solve your problem this way. Perhaps something like this might be of help.

BTW, the 'normal' way to accomplish this is answered here.
« Last Edit: August 10, 2024, 11:08:06 am by TRon »
All software is open source (as long as you can read assembler)

Thaddy

  • Hero Member
  • *****
  • Posts: 15641
  • Censorship about opinions does not belong here.
Re: How to shutdown computer when idle
« Reply #4 on: August 10, 2024, 02:20:00 pm »
Waaaaay to complex, why not:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. begin
  3.   fpSystem('shutdown -P now');// if the program idles, call this.
  4. end.
 
« Last Edit: August 10, 2024, 02:21:39 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

nikel

  • Full Member
  • ***
  • Posts: 210
Re: How to shutdown computer when idle
« Reply #5 on: August 10, 2024, 09:24:44 pm »
Thanks for the replies. I'd made the same application before using forms and it worked fine. I'd dragged TIdleTimer component to form. It shutdowns computer properly.

Yes, shutdown command works fine in terminal and my forms program.

My code never reaches to TDetectIdle.Timer_ItTimer(Sender: TObject);
procedure.

So, my problem is here

Code: [Select]
OnTimer:=@Timer_ItTimer;
« Last Edit: August 10, 2024, 10:20:28 pm by nikel »

Thaddy

  • Hero Member
  • *****
  • Posts: 15641
  • Censorship about opinions does not belong here.
Re: How to shutdown computer when idle
« Reply #6 on: August 11, 2024, 10:22:48 am »
You should not use a timer to detect idle. Just use the onidle event.
That already uses a kind of "timer" to compare between last user input and also CPU usage.
So basically you would introduce duplicate code.
Timers can indeed be used for idle detection, but the LCL does that already for you, so that is not a good idea, although theoretically correct.
If you need it in a console app, you should indeed use a timer, though, to poll user input, network traffic and cpu usage since TCustomApplication from the RTL does not provide an onidle event, as opposed to TApplication from the LCL.
« Last Edit: August 11, 2024, 10:33:44 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

nikel

  • Full Member
  • ***
  • Posts: 210
Re: How to shutdown computer when idle
« Reply #7 on: August 12, 2024, 01:14:25 am »
I need to make it for console. What's wrong with this code:

Code: Pascal  [Select][+][-]
  1. OnTimer:=@Timer_ItTimer;

TRon

  • Hero Member
  • *****
  • Posts: 3234
Re: How to shutdown computer when idle
« Reply #8 on: August 12, 2024, 04:06:18 am »
I need to make it for console. What's wrong with this code:
There is nothing wrong with that code.

As told before the event is never fired because there is no (GUI) message-loop that processes the messages.

TIdleTimer is a LCL component and does not work in/with a pure console application. It attaches itself to TApplication events in order to work as intended and you are not using (LCL) TApplication rather its (FCL) predecessor TCustomApplication.

To avoid any misconception also note that TIdleTimer will only start counting when your own application enters idle state (and not your system/OS)
All software is open source (as long as you can read assembler)

nikel

  • Full Member
  • ***
  • Posts: 210
Re: How to shutdown computer when idle
« Reply #9 on: August 12, 2024, 08:49:24 am »
Quote
To avoid any misconception also note that TIdleTimer will only start counting when your own application enters idle state (and not your system/OS)

Thanks for the hint.

Thaddy

  • Hero Member
  • *****
  • Posts: 15641
  • Censorship about opinions does not belong here.
Re: How to shutdown computer when idle
« Reply #10 on: August 13, 2024, 12:50:25 am »
Measuring  idle, linux only:
Code: Pascal  [Select][+][-]
  1. program idle;
  2. {$mode objfpc}{$H+}
  3. uses baseunix,sysutils;
  4. Type
  5.   TcpuStat = (cscpu,csuser,csnice,cssystem,csidle,csiowait,csirq,cssoftirq,cssteal,cguest,csguestnice);
  6.  
  7. function  get_idle_time:qword;
  8. var
  9.   fp:text;
  10.   buffer:array[0..255] of char;
  11.   a:TStringArray;
  12.   s:string;
  13. begin
  14.   assign(fp,'/proc/stat');
  15.   filemode := 0;
  16.   reset(fp);
  17.   read(fp,buffer);
  18.   close(fp);
  19.   s:=buffer;
  20.   a:=s.split([#32],TStringSplitOptions.ExcludeEmpty);
  21.   Result:=a[ord(csidle)].ToInt64;
  22. end;
  23.  
  24. var
  25.    idle_time1, idle_time2:qword;
  26. begin
  27.   idle_time1 := get_idle_time();
  28.   sleep(1000);// wait a sec
  29.   idle_time2 := get_idle_time();
  30.   writeln('Idle time difference:', idle_time2 - idle_time1);
  31. end.
For the timer you can use this code, I did not integrate it in the above code, since I found a sourcecode issue in the rtl: type only in implementation.
Code: Pascal  [Select][+][-]
  1. program sometimer;
  2. {$mode objfpc}{$J+}
  3. uses
  4.   baseunix,syscall;
  5. Type
  6.   ITimerVal= Record
  7.               It_Interval,
  8.               It_Value      : TimeVal;
  9.              end;
  10.  
  11. Const   ITimer_Real    =0;
  12.         ITimer_Virtual =1;
  13.         ITimer_Prof    =2;
  14.  
  15. Function SetITimer(Which : Longint;Const value : ItimerVal; var VarOValue:ItimerVal):Longint;
  16. Begin
  17.   SetItimer:=Do_Syscall(syscall_nr_setitimer,Which,TSysParam(@Value),TSysParam(@varovalue));
  18. End;
  19.  
  20. procedure timer_handler(signum:integer; info:PSiginfo;context:PSigContext);cdecl;
  21. const count:integer = 0;
  22. begin
  23.   inc(count);
  24.   writeln('Timer expired ',count, 'times':6);
  25. end;
  26.  
  27. var
  28.   sa:sigactionrec;
  29.   timer:itimerval;
  30. begin
  31.     // Install timer_handler as the signal handler for SIGALRM
  32.     sa.sa_handler := @timer_handler;
  33.     sa.sa_flags := SA_RESTART;
  34.     fpsigaction(SIGALRM, @sa, nil);
  35.  
  36.     // Configure the timer to expire after 1 sec, and every 1 sec after that
  37.     timer.it_value.tv_sec := 1;
  38.     timer.it_value.tv_usec := 0;
  39.     timer.it_interval.tv_sec := 1;
  40.     timer.it_interval.tv_usec := 0;
  41.  
  42.     // Start the timer
  43.     setitimer(ITIMER_REAL, timer, timer);
  44.  
  45.     // Do something while waiting for the timer to expire
  46.     while 1=1 do
  47.         fppause;
  48. end.
These two programs should help you on the way.
If you need it for windows instead of linux I will add a windows console example later. Let me kow.

« Last Edit: August 13, 2024, 08:30:46 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

ginoo

  • Jr. Member
  • **
  • Posts: 82
Re: How to shutdown computer when idle
« Reply #11 on: August 13, 2024, 09:09:07 am »
Hi @Thaddy,

in your last example at the instruction fppause it gives me the following error

Project project1 raised exception class 'External: Unknown exception code 14'.

 In file 'syscall.inc' at line 80

, how come?
am I missing something?

Thaddy

  • Hero Member
  • *****
  • Posts: 15641
  • Censorship about opinions does not belong here.
Re: How to shutdown computer when idle
« Reply #12 on: August 13, 2024, 09:58:00 am »
What version of fpc, linux and what bitness are you using?
I use trunk on Debian64-x86_64-Jammy, but I also tested on Raspberry Pi 3 and 4.
Does the example here work?
https://docs.freepascal.org/docs-html/rtl/baseunix/fpalarm.html
( example contains fppause );
« Last Edit: August 13, 2024, 02:24:16 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

ginoo

  • Jr. Member
  • **
  • Posts: 82
Re: How to shutdown computer when idle
« Reply #13 on: August 13, 2024, 06:08:09 pm »
Thank you @Thaddy for responding,
I also tried the example you pointed out (https://docs.freepascal.org/docs-html/rtl/baseunix/fpalarm.html) and it gives me the same error:

Project project1 raised exception class 'External: Unknown exception code 14'.

 In file 'syscall.inc' at line 80:
cmpq $-4095, %rax       { Check %rax for error.  }


I use Linux Mint 22 Wilma base: Ubuntu 24.04 noble. I tried with Lazarus 2.2.6 and 3.2, same result
« Last Edit: August 13, 2024, 06:11:18 pm by ginoo »

ginoo

  • Jr. Member
  • **
  • Posts: 82
Re: How to shutdown computer when idle
« Reply #14 on: August 13, 2024, 06:14:35 pm »
Sorry @Thaddy
however, it is also true that by continuing the program continues to run.
You probably need to ignore this error.

 

TinyPortal © 2005-2018