Recent

Author Topic: How to get user idle time in Linux?  (Read 1914 times)

MarkMLl

  • Hero Member
  • *****
  • Posts: 4753
Re: How to get user idle time in Linux?
« Reply #30 on: June 24, 2022, 03:00:28 pm »
Then, give another solution, don't just boosting a forum stat here.

I don't need to boost any stats, you're the noisy nooby.

Look at the evtest program, and the link I posted which shows exactly where the evdev library fits into the layered API and is explicit about the way that it underlies both X11 and Wayland. I've never needed to do it, but a Pascal shim for that library should be trivial.

My apologies to the mods and wider community for allowing my irritation to show.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

AFFRIZA 亜風実

  • Full Member
  • ***
  • Posts: 136
Re: How to get user idle time in Linux?
« Reply #31 on: June 24, 2022, 04:19:02 pm »
I don't need to boost any stats, you're the noisy nooby.

Look at the evtest program, and the link I posted which shows exactly where the evdev library fits into the layered API and is explicit about the way that it underlies both X11 and Wayland. I've never needed to do it, but a Pascal shim for that library should be trivial.

My apologies to the mods and wider community for allowing my irritation to show.

MarkMLl

What evdev library, I've no idea you're talking about. Is that a Pascal sample demo? Then, you don't say, elitism like just post link here and there what make noobist have a hard time to start Pascal. Just like what you've replied in my threads, is that a helps or just a trying to make presence there, I don't know.
Thanks, I am a noisy nooby, just a noisy nooby and I didn't contribute anything here. Bye, I won't reply your replies anymore.

I tried this, but it detects any activity only when I resize form or move mouse over program tab in task bar. I use Linux Mint with Mate.

Hi, I've tried to use capture using grabpointer but in my distro it freeze the screen. Actually, to detect the mouse move you don't need to use the XEvent, using "Controls" unit will works in all platform but not sure to about the keys but you can modify and experimenting with the masks in the "x" unit.

This is what I've modified of XEventWatcher unit:
Code: Pascal  [Select][+][-]
  1. unit XEventWatcher;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, x, xlib, Dialogs, syncobjs, Controls;
  9.  
  10. type
  11.   TNotifyUpdate = procedure of object;
  12.  
  13.   TXEventWatcherThread = class(TThread)
  14.   private
  15.     fEvent: TEvent;
  16.     fPaused: Boolean;
  17.     procedure SetPaused(const Value: Boolean);
  18.     procedure Update;
  19.   protected
  20.     procedure Execute; override;
  21.   public
  22.     Display: PDisplay;
  23.     NotifyUpdate: TNotifyUpdate;
  24.     constructor Create(CreateSuspended: Boolean; const StackSize: SizeUInt =
  25.       DefaultStackSize);
  26.     destructor Destroy; override;
  27.     property Paused: Boolean read fPaused write SetPaused;
  28.   end;
  29.  
  30. implementation
  31.  
  32. procedure TXEventWatcherThread.Update;
  33. begin
  34.   if Assigned(NotifyUpdate) then
  35.     NotifyUpdate;
  36. end;
  37.  
  38. procedure TXEventWatcherThread.SetPaused(const Value: Boolean);
  39. begin
  40.   if (not Terminated) and (fPaused <> Value) then
  41.   begin
  42.     fPaused := Value;
  43.     if fPaused then
  44.     begin
  45.       fEvent.ResetEvent
  46.     end
  47.     else
  48.     begin
  49.       fEvent.SetEvent;
  50.     end;
  51.   end;
  52.   Update;
  53. end;
  54.  
  55. procedure TXEventWatcherThread.Execute;
  56. var
  57.   ev: TXEvent;
  58.   root: TWindow;
  59.   //root_window: TWindow;
  60.   //root_x, root_y: Integer;
  61.   //rmask: integer;
  62.   //NET_ACTIVE_WINDOW: TAtom;
  63.   mouse_x, mouse_y: integer;
  64.   attr: TXSetWindowAttributes;
  65. begin
  66.   Display := XOpenDisplay(Pchar(GetEnvironmentVariable('DISPLAY')));
  67.   root := RootWindow(Display, DefaultScreen(Display));
  68.   //NET_ACTIVE_WINDOW := XInternAtom(Display, '_NET_ACTIVE_WINDOW', LongBool(1));
  69.   attr.event_mask := StructureNotifyMask or SubstructureNotifyMask or ExposureMask or PointerMotionMask or ButtonPressMask or ButtonReleaseMask;
  70.   //XAllowEvents(display, AsyncBoth, CurrentTime);
  71.  
  72.   { I tried this to grab the mouse but it freeze the screen }
  73.   //XGrabPointer(Display, root, false,
  74.   //PointerMotionMask or ButtonPressMask or ButtonReleaseMask,
  75.   //  GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
  76.   XChangeWindowAttributes(Display, root, CWEventMask, @attr);
  77.   XNextEvent(Display, @ev);
  78.   while not Terminated do
  79.   begin
  80.     fEvent.WaitFor(INFINITE);
  81.     if XPending(Display) > 1 then
  82.     begin
  83.       { I have no idea about these codes, just copied from FPWM and it works }
  84.       XNextEvent(Display, @ev);
  85.     end
  86.     else if XPending(Display) = 1 then
  87.     begin
  88.       Synchronize(@Update);
  89.       // sometimes there's so much events
  90.       Sleep(500);
  91.       XNextEvent(Display, @ev);
  92.     end
  93.     else
  94.     begin
  95.       if (mouse_x <> Mouse.CursorPos.X) or (mouse_x <> Mouse.CursorPos.X) then
  96.       begin
  97.         Synchronize(@Update);
  98.         mouse_x := Mouse.CursorPos.X;
  99.         mouse_y := Mouse.CursorPos.Y;
  100.       end;
  101.       // if there's no sleep, the CPU usage going wild
  102.       Sleep(200);
  103.     end;
  104.   end;
  105.   if Assigned(Display) then XCloseDisplay(Display);
  106. end;
  107.  
  108. constructor TXEventWatcherThread.Create(CreateSuspended: Boolean; const StackSize: SizeUInt =
  109.   DefaultStackSize);
  110. begin
  111.   inherited Create(CreateSuspended, StackSize);
  112.   fPaused := CreateSuspended;
  113.   fEvent := TEvent.Create(nil, true, CreateSuspended, '');
  114.   FreeOnTerminate := True;
  115. end;
  116.  
  117. destructor TXEventWatcherThread.Destroy;
  118. begin
  119.   inherited Destroy;
  120. end;
  121.  
  122. end.
  123.  
  124.  
« Last Edit: June 24, 2022, 04:21:35 pm by AFFRIZA 亜風実 »
Kyoukai Framework: https://github.com/afuriza/kyoukai_framework

Dukung kemerdekaan Donetsk dan Lugansk! Tidak membalas profil berbendera biru-kuning apalagi ber-Bandera.

Thaddy

  • Hero Member
  • *****
  • Posts: 11774
Re: How to get user idle time in Linux?
« Reply #32 on: June 24, 2022, 04:29:18 pm »
My apologies to the mods and wider community for allowing my irritation to show.
Why apologize when you are right? Sometimes a rude wording can teach people more.
(Unfortunate but true)
« Last Edit: June 24, 2022, 04:31:24 pm by Thaddy »
Black themes should be banned.

artem101

  • New Member
  • *
  • Posts: 46
Re: How to get user idle time in Linux?
« Reply #33 on: June 24, 2022, 04:37:27 pm »
Hi, I've tried to use capture using grabpointer but in my distro it freeze the screen. Actually, to detect the mouse move you don't need to use the XEvent, using "Controls" unit will works in all platform but not sure to about the keys but you can modify and experimenting with the masks in the "x" unit.

This is what I've modified of XEventWatcher unit:
Code: Pascal  [Select][+][-]
  1. unit XEventWatcher;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, x, xlib, Dialogs, syncobjs, Controls;
  9.  
  10. type
  11.   TNotifyUpdate = procedure of object;
  12.  
  13.   TXEventWatcherThread = class(TThread)
  14.   private
  15.     fEvent: TEvent;
  16.     fPaused: Boolean;
  17.     procedure SetPaused(const Value: Boolean);
  18.     procedure Update;
  19.   protected
  20.     procedure Execute; override;
  21.   public
  22.     Display: PDisplay;
  23.     NotifyUpdate: TNotifyUpdate;
  24.     constructor Create(CreateSuspended: Boolean; const StackSize: SizeUInt =
  25.       DefaultStackSize);
  26.     destructor Destroy; override;
  27.     property Paused: Boolean read fPaused write SetPaused;
  28.   end;
  29.  
  30. implementation
  31.  
  32. procedure TXEventWatcherThread.Update;
  33. begin
  34.   if Assigned(NotifyUpdate) then
  35.     NotifyUpdate;
  36. end;
  37.  
  38. procedure TXEventWatcherThread.SetPaused(const Value: Boolean);
  39. begin
  40.   if (not Terminated) and (fPaused <> Value) then
  41.   begin
  42.     fPaused := Value;
  43.     if fPaused then
  44.     begin
  45.       fEvent.ResetEvent
  46.     end
  47.     else
  48.     begin
  49.       fEvent.SetEvent;
  50.     end;
  51.   end;
  52.   Update;
  53. end;
  54.  
  55. procedure TXEventWatcherThread.Execute;
  56. var
  57.   ev: TXEvent;
  58.   root: TWindow;
  59.   //root_window: TWindow;
  60.   //root_x, root_y: Integer;
  61.   //rmask: integer;
  62.   //NET_ACTIVE_WINDOW: TAtom;
  63.   mouse_x, mouse_y: integer;
  64.   attr: TXSetWindowAttributes;
  65. begin
  66.   Display := XOpenDisplay(Pchar(GetEnvironmentVariable('DISPLAY')));
  67.   root := RootWindow(Display, DefaultScreen(Display));
  68.   //NET_ACTIVE_WINDOW := XInternAtom(Display, '_NET_ACTIVE_WINDOW', LongBool(1));
  69.   attr.event_mask := StructureNotifyMask or SubstructureNotifyMask or ExposureMask or PointerMotionMask or ButtonPressMask or ButtonReleaseMask;
  70.   //XAllowEvents(display, AsyncBoth, CurrentTime);
  71.  
  72.   { I tried this to grab the mouse but it freeze the screen }
  73.   //XGrabPointer(Display, root, false,
  74.   //PointerMotionMask or ButtonPressMask or ButtonReleaseMask,
  75.   //  GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
  76.   XChangeWindowAttributes(Display, root, CWEventMask, @attr);
  77.   XNextEvent(Display, @ev);
  78.   while not Terminated do
  79.   begin
  80.     fEvent.WaitFor(INFINITE);
  81.     if XPending(Display) > 1 then
  82.     begin
  83.       { I have no idea about these codes, just copied from FPWM and it works }
  84.       XNextEvent(Display, @ev);
  85.     end
  86.     else if XPending(Display) = 1 then
  87.     begin
  88.       Synchronize(@Update);
  89.       // sometimes there's so much events
  90.       Sleep(500);
  91.       XNextEvent(Display, @ev);
  92.     end
  93.     else
  94.     begin
  95.       if (mouse_x <> Mouse.CursorPos.X) or (mouse_x <> Mouse.CursorPos.X) then
  96.       begin
  97.         Synchronize(@Update);
  98.         mouse_x := Mouse.CursorPos.X;
  99.         mouse_y := Mouse.CursorPos.Y;
  100.       end;
  101.       // if there's no sleep, the CPU usage going wild
  102.       Sleep(200);
  103.     end;
  104.   end;
  105.   if Assigned(Display) then XCloseDisplay(Display);
  106. end;
  107.  
  108. constructor TXEventWatcherThread.Create(CreateSuspended: Boolean; const StackSize: SizeUInt =
  109.   DefaultStackSize);
  110. begin
  111.   inherited Create(CreateSuspended, StackSize);
  112.   fPaused := CreateSuspended;
  113.   fEvent := TEvent.Create(nil, true, CreateSuspended, '');
  114.   FreeOnTerminate := True;
  115. end;
  116.  
  117. destructor TXEventWatcherThread.Destroy;
  118. begin
  119.   inherited Destroy;
  120. end;
  121.  
  122. end.
  123.  
  124.  

After modifications it raises External: SIGABRT in '../sysdeps/unix/sysv/linux/raise.c' at line 50.

AFFRIZA 亜風実

  • Full Member
  • ***
  • Posts: 136
Re: How to get user idle time in Linux?
« Reply #34 on: June 24, 2022, 04:39:50 pm »
After modifications it raises External: SIGABRT in '../sysdeps/unix/sysv/linux/raise.c' at line 50.
May you give me the Call Stack window information?
Kyoukai Framework: https://github.com/afuriza/kyoukai_framework

Dukung kemerdekaan Donetsk dan Lugansk! Tidak membalas profil berbendera biru-kuning apalagi ber-Bandera.

artem101

  • New Member
  • *
  • Posts: 46
Re: How to get user idle time in Linux?
« Reply #35 on: June 24, 2022, 04:44:21 pm »
May you give me the Call Stack window information?

Yes, of course.

MarkMLl

  • Hero Member
  • *****
  • Posts: 4753
Re: How to get user idle time in Linux?
« Reply #36 on: June 24, 2022, 04:52:43 pm »
What evdev library, I've no idea you're talking about.

In that case since it was @artem101 asking for help, I suggest it would be appropriate for you to stop insisting that you know the answer to his problem.

evdev is a kernel API library, as you would see if you'd taken the trouble to follow the link I posted. It's expressed in C, but would be easy enough to wrap in Pascal. I've worked with this stuff fairly extensively, albeit using evtest etc. rather than Pascal code.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

AFFRIZA 亜風実

  • Full Member
  • ***
  • Posts: 136
Re: How to get user idle time in Linux?
« Reply #37 on: June 24, 2022, 05:09:31 pm »
Yes, of course.
I didn't expect that. I think GTK not allowing to access the mouse position outside the application thread. How about to put that in TForm, and remove any last modification? What I've doing here is to give an idea to do it what available Free Pascal. I can help you at least until Sunday but you can see those replies yourself, so I'll stop in this reply.

Anyway, like that person said, the thing that I know is what available in Pascal. I don't have a time to convert any C or Python code nor any of those people will do your homework, and even the who suggest to use libevdev doesn't give code to wrap that, especially with external lib, that's not my homework (at least for now) and especially I've never dealing with C codes.

Since I am a nooby, my main job is farming, I only deal with some shallow programming to automate my job with Raspberry Pi. There's a one person insisting this is not appropriate, so I'll stop this for today. You can learn yourself how to wrap other language to Pascal, there's no shortest path to Roma.
« Last Edit: June 24, 2022, 05:11:53 pm by AFFRIZA 亜風実 »
Kyoukai Framework: https://github.com/afuriza/kyoukai_framework

Dukung kemerdekaan Donetsk dan Lugansk! Tidak membalas profil berbendera biru-kuning apalagi ber-Bandera.

artem101

  • New Member
  • *
  • Posts: 46
Re: How to get user idle time in Linux?
« Reply #38 on: June 24, 2022, 05:53:42 pm »
Yes, of course.
I didn't expect that. I think GTK not allowing to access the mouse position outside the application thread. How about to put that in TForm, and remove any last modification? What I've doing here is to give an idea to do it what available Free Pascal. I can help you at least until Sunday but you can see those replies yourself, so I'll stop in this reply.

Anyway, like that person said, the thing that I know is what available in Pascal. I don't have a time to convert any C or Python code nor any of those people will do your homework, and even the who suggest to use libevdev doesn't give code to wrap that, especially with external lib, that's not my homework (at least for now) and especially I've never dealing with C codes.

Since I am a nooby, my main job is farming, I only deal with some shallow programming to automate my job with Raspberry Pi. There's a one person insisting this is not appropriate, so I'll stop this for today. You can learn yourself how to wrap other language to Pascal, there's no shortest path to Roma.

Ok. Thank you for help.

artem101

  • New Member
  • *
  • Posts: 46
Re: How to get user idle time in Linux?
« Reply #39 on: June 24, 2022, 09:29:10 pm »
I spent some time and make implementation based on X11 Screen Saver Extension. This Python code was used as source: https://dev.gajim.org/gajim/gajim/blob/89c7eb6e6ab3f61a188c6cee063a000526df522c/gajim/common/idle.py.

Code: Pascal  [Select][+][-]
  1. unit Utils2;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. function UserIdleTime: Cardinal;
  11.  
  12. implementation
  13.  
  14. uses
  15.   x, xlib, dynlibs, ctypes;
  16.  
  17. type
  18.   TXScreenSaverInfo = record
  19.     Window: culong;
  20.     State: cint;
  21.     Kind: cint;
  22.     TilOrSince: culong;
  23.     Idle: culong;
  24.     EventMask: culong;
  25.   end;
  26.   PXScreenSaverInfo = ^TXScreenSaverInfo;
  27.  
  28.   TXScreenSaverAllocInfoFunc = function: Pointer {PXScreenSaverInfo}; cdecl;
  29.   TXScreenSaverQueryInfoFunc = function(ADisplay: PXDisplay; ADrawable: TDrawable;
  30.       AScreenSaverInfo: PXScreenSaverInfo): TStatus; cdecl;
  31.  
  32. var
  33.   XScreenSaverLib: TLibHandle;
  34.   XScreenSaverAllocInfo: TXScreenSaverAllocInfoFunc;
  35.   XScreenSaverQueryInfo: TXScreenSaverQueryInfoFunc;
  36.  
  37. function UserIdleTime: Cardinal;
  38. var
  39.   DisplayName: String;
  40.   Display: PDisplay;
  41.   RootWnd: TWindow;
  42.   XScreenSaverInfo: PXScreenSaverInfo;
  43. begin
  44.   Result := 0;
  45.  
  46.   if XScreenSaverLib = dynlibs.NilHandle then
  47.     Exit;
  48.  
  49.   XScreenSaverInfo := XScreenSaverAllocInfo();
  50.   if Assigned(XScreenSaverInfo) then
  51.   begin
  52.     DisplayName := GetEnvironmentVariable('DISPLAY');
  53.     Display := XOpenDisplay(PChar(DisplayName));
  54.     RootWnd := RootWindow(Display, DefaultScreen(Display));
  55.  
  56.     if XScreenSaverQueryInfo(Display, RootWnd, XScreenSaverInfo) <> 0 then
  57.     begin
  58.       Result := XScreenSaverInfo^.Idle;
  59.     end;
  60.  
  61.     XFree(XScreenSaverInfo);
  62.   end;
  63. end;
  64.  
  65. initialization
  66.   XScreenSaverLib := LoadLibrary('libXss.so.1');
  67.   if XScreenSaverLib = dynlibs.NilHandle then
  68.     raise Exception.Create('Could not load library libXss');
  69.  
  70.   XScreenSaverAllocInfo := TXScreenSaverAllocInfoFunc(GetProcedureAddress(XScreenSaverLib, 'XScreenSaverAllocInfo'));
  71.   if not Assigned(XScreenSaverAllocInfo) then
  72.     raise Exception.Create('Could not find XScreenSaverAllocInfo function in libXss');
  73.  
  74.   XScreenSaverQueryInfo := TXScreenSaverQueryInfoFunc(GetProcedureAddress(XScreenSaverLib, 'XScreenSaverQueryInfo'));
  75.   if not Assigned(XScreenSaverQueryInfo) then
  76.     raise Exception.Create('Could not find XScreenSaverQueryInfo function in libXss');
  77.  
  78. finalization;
  79.   if XScreenSaverLib <> dynlibs.NilHandle then
  80.     UnloadLibrary(XScreenSaverLib);
  81. end.
  82.  

Seems that it works as needed, but sometimes program crashes with SIGSEGV after some period of work.

Call stack:
Code: [Select]
#0 XLIB_$$_DEFAULTSCREEN$PDISPLAY$$LONGINT at :0
#1 USERIDLETIME at utils2.pas:54
#2 TIMER1TIMER(0x7ffff58ae490, 0x7ffff5909d00) at unit1.pas:66
#3 DOONTIMER(0x7ffff5909d00) at customtimer.pas:175
#4 TIMER(0x7ffff5909d00) at customtimer.pas:150
#5 GTKTIMERCB(0x7ffff5728f00) at gtk2callback.inc:2715
#6 ?? at :0
#7 g_main_context_dispatch at :0
#8 ?? at :0
#9 g_main_context_iteration at :0
#10 APPWAITMESSAGE(0x7ffff622f4b0) at gtk2widgetset.inc:2434
#11 IDLE(0x7ffff622ecd0, true) at include/application.inc:414
#12 HANDLEMESSAGE(0x7ffff622ecd0) at include/application.inc:1281
#13 RUNLOOP(0x7ffff622ecd0) at include/application.inc:1417
#14 APPRUN(0x7ffff622f4b0, {Proc = {procedure (POINTER)} 0x7fffffffe420, Self = 0x7ffff622ecd0}) at include/interfacebase.inc:54
#15 RUN(0x7ffff622ecd0) at include/application.inc:1405
#16 main at UserIdleTime.lpr:20

Fred vS

  • Hero Member
  • *****
  • Posts: 2602
    • StrumPract is the musicians best friend
Re: How to get user idle time in Linux?
« Reply #40 on: June 24, 2022, 09:40:25 pm »
Sorry to disturb one more time but all what you want can easy be done with MSEgui

There are 2 methods for forms that will do your job:

One when the form loose focus, you may init the timer there:
Code: Pascal  [Select][+][-]
  1. procedure tmainfo.ondefocus(const sender: TObject);

And one when the form get the focus, you may stop the timer here:
Code: Pascal  [Select][+][-]
  1. procedure tmainfo.onfocus(const sender: TObject);

Tested on Unix system and it works.

Your post is in "General" section, so it seems not LCL widget only, if LCL is needed sorry for the noise.


I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs

MarkMLl

  • Hero Member
  • *****
  • Posts: 4753
Re: How to get user idle time in Linux?
« Reply #41 on: June 24, 2022, 10:18:10 pm »
Seems that it works as needed, but sometimes program crashes with SIGSEGV after some period of work.

I'm working from memory here since a mains glitch blew my main development system a few days ago and I don't want to pull loads of source from the server while I've also got major paperwork on my plate... but that sounds familiar.

Having called XOpenDisplay(), I think you also need a matching XCloseDisplay() since otherwise you will run out of handles even if that is not obvious at this level of the API: use a try...finally structure.

Also if XScreenSaverQueryInfo() fails is it correct to subsequently do an XFree(XScreenSaverInfo)?

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Roland57

  • Sr. Member
  • ****
  • Posts: 287
Re: How to get user idle time in Linux?
« Reply #42 on: June 24, 2022, 10:28:14 pm »
I spent some time and make implementation based on X11 Screen Saver Extension. This Python code was used as source: https://dev.gajim.org/gajim/gajim/blob/89c7eb6e6ab3f61a188c6cee063a000526df522c/gajim/common/idle.py.

Hello! Your code seems to work well here (Mageia). I have not yet seen it crash. Thank you for sharing.

P.-S. After some time I get this message (in the terminal):
Code: Text  [Select][+][-]
  1. Maximum number of clients reached

@AFFRIZA 亜風実

Thank you for your interesting code example.

« Last Edit: June 24, 2022, 10:37:10 pm by Roland57 »

artem101

  • New Member
  • *
  • Posts: 46
Re: How to get user idle time in Linux?
« Reply #43 on: June 25, 2022, 10:13:15 am »
Sorry to disturb one more time but all what you want can easy be done with MSEgui

My program written with LCL and I do not plan to change it to anything else.

Having called XOpenDisplay(), I think you also need a matching XCloseDisplay()

Adding XCloseDisplay() fixed problem. Thanks.

Also if XScreenSaverQueryInfo() fails is it correct to subsequently do an XFree(XScreenSaverInfo)?

I think yes: memory allocated by XScreenSaverAllocInfo() and regardless of the XScreenSaverQueryInfo() call result should be released.


Fixed code:
Code: Pascal  [Select][+][-]
  1. unit Utils2;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. function UserIdleTime: Cardinal;
  11.  
  12. implementation
  13.  
  14. uses
  15.   x, xlib, dynlibs, ctypes;
  16.  
  17. type
  18.   TXScreenSaverInfo = record
  19.     Window: culong;
  20.     State: cint;
  21.     Kind: cint;
  22.     TilOrSince: culong;
  23.     Idle: culong;
  24.     EventMask: culong;
  25.   end;
  26.   PXScreenSaverInfo = ^TXScreenSaverInfo;
  27.  
  28.   TXScreenSaverAllocInfoFunc = function: Pointer {PXScreenSaverInfo}; cdecl;
  29.   TXScreenSaverQueryInfoFunc = function(ADisplay: PXDisplay; ADrawable: TDrawable;
  30.       AScreenSaverInfo: PXScreenSaverInfo): TStatus; cdecl;
  31.  
  32. var
  33.   XScreenSaverLib: TLibHandle;
  34.   XScreenSaverAllocInfo: TXScreenSaverAllocInfoFunc;
  35.   XScreenSaverQueryInfo: TXScreenSaverQueryInfoFunc;
  36.  
  37. function UserIdleTime: Cardinal;
  38. var
  39.   DisplayName: String;
  40.   Display: PDisplay;
  41.   RootWnd: TWindow;
  42.   XScreenSaverInfo: PXScreenSaverInfo;
  43. begin
  44.   Result := 0;
  45.  
  46.   if XScreenSaverLib = dynlibs.NilHandle then
  47.     Exit;
  48.  
  49.   XScreenSaverInfo := XScreenSaverAllocInfo();
  50.   if Assigned(XScreenSaverInfo) then
  51.   begin
  52.     DisplayName := GetEnvironmentVariable('DISPLAY');
  53.     Display := XOpenDisplay(PChar(DisplayName));
  54.     RootWnd := RootWindow(Display, DefaultScreen(Display));
  55.  
  56.     if XScreenSaverQueryInfo(Display, RootWnd, XScreenSaverInfo) <> 0 then
  57.     begin
  58.       Result := XScreenSaverInfo^.Idle;
  59.     end;
  60.  
  61.     if (Assigned(Display)) then
  62.       XCloseDisplay(Display);
  63.  
  64.     XFree(XScreenSaverInfo);
  65.   end;
  66. end;
  67.  
  68. initialization
  69.   XScreenSaverLib := LoadLibrary('libXss.so.1');
  70.   if XScreenSaverLib = dynlibs.NilHandle then
  71.     raise Exception.Create('Could not load library libXss');
  72.  
  73.   XScreenSaverAllocInfo := TXScreenSaverAllocInfoFunc(GetProcedureAddress(XScreenSaverLib, 'XScreenSaverAllocInfo'));
  74.   if not Assigned(XScreenSaverAllocInfo) then
  75.     raise Exception.Create('Could not find XScreenSaverAllocInfo function in libXss');
  76.  
  77.   XScreenSaverQueryInfo := TXScreenSaverQueryInfoFunc(GetProcedureAddress(XScreenSaverLib, 'XScreenSaverQueryInfo'));
  78.   if not Assigned(XScreenSaverQueryInfo) then
  79.     raise Exception.Create('Could not find XScreenSaverQueryInfo function in libXss');
  80.  
  81. finalization;
  82.   if XScreenSaverLib <> dynlibs.NilHandle then
  83.     UnloadLibrary(XScreenSaverLib);
  84. end.
  85.  
« Last Edit: June 25, 2022, 10:52:19 am by artem101 »

MarkMLl

  • Hero Member
  • *****
  • Posts: 4753
Re: How to get user idle time in Linux?
« Reply #44 on: June 25, 2022, 11:11:32 am »
I'd suggest moving reading the DISPLAY shell variable into the program initialisation, this can't (realistically) change while the program is running and it's probably worth being able to treat that missing or malformed as a startup error... not to mention saving time if the function is called repeatedly.

I'd add that when I've done this sort of thing I've statically linked the X11 libraries, since realistically they can't be absent if an LCL-based program is running (they /could/ potentially be the wrong version etc...).

Also you might find that if you're using dynamic linkage it relies on symbolic links set up when the distro installs a -dev package, I'm not sure about the libx11 situation but it's a major problem with e.g. database libraries.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018