An idle hack

An occasionally recurring question we get is how to tell how long an X session has been idle without having the xauth cookie to open a connection to the display. While thinking about this recently, I thought of a way to do it, if you have root access and can poke around in the X server process space.

Warning: The technique that follows is for educational & entertainment purposes only. It is not a supported interface, is subject to change or break at any time, and may not even work for you right now. Use at your own risk. Do not fold, spindle, or mutilate.

The X server keeps track internally of the time that it last saw an event from an input device such as a keyboard or mouse and uses this to determine when to blank the screen or remove power from the monitor, among other things. You can see these checks in the source code in Xserver/os/WaitFor.c (all the references here are to the open source Xorg code base - Xsun code in these areas is almost identical so it's close enough for reference). For instance, if you look at the function ScreenSaverTimeoutExpire, you'll see it calculate the time since the last event as:

    INT32 timeout = now - lastDeviceEventTime.milliseconds;
We simply need to duplicate this to determine the idle time of the current session.

The lastDeviceEventTime is a global variable, which means we can query it via the Solaris mdb debugger in a live process. It's defined as type TimeStamp which is:

typedef struct _TimeStamp {
    CARD32 months;      /\* really ~49.7 days \*/
    CARD32 milliseconds;
}           TimeStamp;
CARD32 is an X type defined to be a 32-bit unsigned integer. So to get the value of lastDeviceEventTime.milliseconds we can use this mdb command which prints in decimal form the value of the 4-byte word that is 4 bytes after the address defined as lastDeviceEventTime:
# echo "lastDeviceEventTime+4/D" | mdb -p 5755
Xsun`lastDeviceEventTime+4:     1804124248
(5755 is the process id of the currently running Xsun process on this system.)

now represents the current time and is set earlier in WaitFor.c by calling GetTimeInMillis, which can be found in Xserver/os/utils.c, and is simply:

CARD32
GetTimeInMillis(void)
{
    struct timeval  tp;

    X_GETTIMEOFDAY(&tp);
    return(tp.tv_sec \* 1000) + (tp.tv_usec / 1000);
}
We can replicate this in perl using the gettimeofday function provided in the Time::HiRes module provided in perl 5.8 (which means it's included in Solaris 10, but you need to provide it yourself for older versions). A perl one-liner that mimics the above function, including truncating to 32-bit (important when using a perl built with 64-bit integer support) is (formatted for readability - put this all on one line to run):
perl -MTime::HiRes -e '
	use integer;
	@t = Time::HiRes::gettimeofday(); 
	print ( ($t[0]\*1000 + $t[1]/1000) % 0x100000000 ); 
	print "\\n";'

Now we just need to subtract the lastEventDeviceTime we got from mdb with the current time we got from gettimeofday, which in a burst of barely readable perl code looks like:

echo "lastDeviceEventTime+4/D" | mdb -p process_id | perl -MTime::HiRes -n -e '
      use integer; 
      @t = Time::HiRes::gettimeofday(); 
      @l = split /\\:/, $_; 
      $i = ((($t[0]\*1000 + $t[1]/1000) % 0x100000000) - $l[1]); 
      print $i/1000 . " seconds since last device event \\n";'

Running on the test system next to me:
# echo "lastDeviceEventTime+4/D" | mdb -p 5755 | perl -MTime::HiRes -n -e 'use integer; @t=Time::HiRes::gettimeofday(); @l=split /\\:/, $_; $i = ((($t[0]\*1000 + $t[1]/1000)%0x100000000) - $l[1]); print $i/1000 . " seconds since last device event \\n";'
2212 seconds since last device event
[ hit a key on the keyboard and run again ]
# echo "lastDeviceEventTime+4/D" | mdb -p 5755 | perl -MTime::HiRes -n -e 'use integer; @t=Time::HiRes::gettimeofday(); @l=split /\\:/, $_; $i = ((($t[0]\*1000 + $t[1]/1000)%0x100000000) - $l[1]); print $i/1000 . " seconds since last device event \\n";'
2 seconds since last device event

We have now picked the X server's brain and found out when it last heard from an input device. It's definitely messy, but scarily seems to work.

Comments:

Is there a variable/structure member which tracks the idle time of a single window or screen ?

Posted by Roland Mainz on October 20, 2004 at 11:21 AM PDT #

Not that I'm aware of - as far as I know the X server just keeps track of the time since the last input event seen on any input device to the server.

Posted by Alan Coopersmith on October 20, 2004 at 11:46 AM PDT #

Is it possible to add a new variable to the server-internal |Window| structure to track such stuff or would that break the Xfree86/X.org DDX module ABI ?

Posted by Roland Mainz on October 21, 2004 at 06:57 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

Engineer working on Oracle Solaris and with the X.Org open source community.

Disclaimer

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle, the X.Org Foundation, or anyone else.

See Also
Follow me on twitter

Search

Categories
Archives
« July 2015
SunMonTueWedThuFriSat
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 
       
Today