1#!/bin/sh
2#---------------------------------------------
3#   xdg-screensaver
4#
5#   Utility script to control screensaver.
6#
7#   Refer to the usage() function below for usage.
8#
9#   Copyright 2006, Bryce Harrington <bryce@osdl.org>
10#
11#   LICENSE:
12#
13#---------------------------------------------
14
15manualpage()
16{
17cat << _MANUALPAGE
18_MANUALPAGE
19}
20
21usage()
22{
23cat << _USAGE
24_USAGE
25}
26
27#@xdg-utils-common@
28
29# Check if we can use "mv -T"
30if mv -T ... ... 2>&1 | grep '\.\.\.' > /dev/null ; then
31   # We can securely move files in /tmp with mv -T
32   DEBUG 1 "mv -T available"
33   MV="mv -T"
34   screensaver_file="/tmp/xdg-screensaver-$USER-"`echo $DISPLAY | sed 's/:/-/g'`
35else
36   # No secure moves available, use home dir
37   DEBUG 1 "mv -T not available"
38   MV="mv"
39   screensaver_file="$HOME/.xdg-screensaver-"`echo $(hostname)-$DISPLAY | sed 's/:/-/g'`
40fi
41lockfile_command=`which lockfile 2> /dev/null`
42
43lockfile()
44{
45  if [ -n "$lockfile_command" ] ; then
46     $lockfile_command -1 -l 10 -s 3 "$screensaver_file".lock
47  else
48     # Poor man's attempt at doing a lockfile
49     # Be careful not to facilitate a symlink attack
50     local try
51     try=0
52     while ! ln -s "$screensaver_file".lock "$screensaver_file".lock 2> /dev/null;
53     do
54        sleep 1
55        try=$(($try+1))
56        if [ $try -eq 3 ] ; then
57            rm -f "$screensaver_file".lock || return # Can't remove lockfile
58            try=0
59        fi
60     done
61  fi
62}
63
64unlockfile()
65{
66  rm -f "$screensaver_file".lock
67}
68
69perform_action()
70{
71  result=1
72
73  if [ "$1" = "resume" ] ; then
74      # Restore DPMS state
75      if [ -f "$screensaver_file.dpms" ]; then
76          rm "$screensaver_file.dpms"
77          # Re-enable DPMS
78          xset +dpms
79      fi
80  fi
81  if [ "$1" = "reset" ] ; then
82      if xset -q | grep 'DPMS is Enabled' > /dev/null 2> /dev/null; then
83          xset dpms force on
84      fi
85  fi
86
87  case "$DE" in
88    kde)
89      if [ x"$KDE_SESSION_VERSION" = x"4" ]; then
90          screensaver_freedesktop "$1"
91      else
92          screensaver_kde "$1"
93      fi
94      ;;
95
96    gnome_screensaver)
97      screensaver_gnome_screensaver "$1"
98      ;;
99
100    xscreensaver)
101      screensaver_xscreensaver "$1"
102      ;;
103
104    '')
105      screensaver_xserver "$1"
106      ;;
107  esac
108
109  if [ "$1" = "suspend" ] ; then
110      # Save DPMS state
111      if xset -q | grep 'DPMS is Enabled' > /dev/null 2> /dev/null; then
112          test "${TMPDIR+set}" = set || TMPDIR=/tmp
113          tmpfile=`mktemp $TMPDIR/tmp.XXXXXXXXXX`
114          $MV "$tmpfile" "$screensaver_file.dpms"
115          # Disable DPMS
116          xset -dpms
117      fi
118  fi
119
120}
121
122cleanup_suspend()
123{
124  lockfile
125  test "${TMPDIR+set}" = set || TMPDIR=/tmp
126  tmpfile=`mktemp $TMPDIR/tmp.XXXXXXXXXX`
127  grep -v "$window_id:$xprop_pid\$" "$screensaver_file" > "$tmpfile" 2> /dev/null
128  $MV "$tmpfile" "$screensaver_file"
129  if [ ! -s "$screensaver_file" ] ; then
130      rm "$screensaver_file"
131      unlockfile
132      # $screensaver_file is empty, do resume
133      perform_action resume
134  else
135      unlockfile
136  fi
137}
138
139do_resume()
140{
141  lockfile # Obtain lockfile
142  # Find the PID of the trackingprocess
143  xprop_pid=`grep "$window_id:" "$screensaver_file" 2> /dev/null | cut -d ':' -f 2`
144  unlockfile # Free lockfile
145  if [ -n "$xprop_pid" ] && ps -p "$xprop_pid" 2> /dev/null | grep xprop > /dev/null; then
146     # Kill the tracking process
147     kill -s TERM $xprop_pid
148  fi
149  cleanup_suspend
150}
151
152XPROP=`which xprop 2> /dev/null`
153
154check_window_id()
155{
156  if [ -z "$XPROP" ]; then
157     DEBUG 3 "xprop not found"
158     return
159  fi
160  DEBUG 2 "Running $XPROP -id $window_id"
161  if $XPROP -id $window_id > /dev/null 2> /dev/null; then
162     DEBUG 3 Window $window_id exists
163  else
164     DEBUG 3 Window $window_id does not exist
165     exit_failure_operation_failed "Window $window_id does not exist"
166  fi
167}
168
169track_window()
170{
171  if [ -z "$XPROP" ]; then
172     # Don't track window if we don't have xprop
173     return
174  fi
175  lockfile
176
177  test "${TMPDIR+set}" = set || TMPDIR=/tmp
178  tmpfile=`mktemp $TMPDIR/tmp.XXXXXXXXXX`
179  # Filter stale entries from the xdg-screensaver status file
180  # Return if $window_id is being tracked already
181  (
182    already_tracked=1
183    IFS_save="$IFS"
184    IFS=":"
185    while read wid pid; do
186      if ps -p "$pid" 2> /dev/null | grep xprop > /dev/null; then
187        echo "$wid:$pid"
188        if [ $wid = $window_id ] ; then
189          already_tracked=0
190        fi
191      fi
192    done
193    IFS="$IFS_save"
194    exit $already_tracked
195  ) < $screensaver_file > $tmpfile
196  already_tracked=$?
197
198  if [ "$already_tracked" -eq "0" ] ; then
199    $MV "$tmpfile" "$screensaver_file"
200    # We are already tracking $window_id, don't do anything
201    unlockfile
202    return
203  fi
204
205  # Start tracking $window_id
206  $XPROP -id $window_id -spy > /dev/null &
207  xprop_pid=$!
208  # Add window_id and xprop_pid to the xdg-screensave status file
209  echo "$window_id:$xprop_pid" >> $tmpfile
210  $MV "$tmpfile" "$screensaver_file"
211  unlockfile
212  # Wait for xprop to edit, it means that the window disappeared
213  wait $xprop_pid
214  # Clean up the administration and resume the screensaver
215  cleanup_suspend
216}
217
218screensaver_freedesktop()
219{
220    case "$1" in
221        suspend)
222        dbus-send --session \
223                  --dest=org.freedesktop.ScreenSaver \
224                  --type=method_call \
225                  --print-reply \
226                  --reply-timeout=2000 \
227                  /ScreenSaver \
228                  org.freedesktop.ScreenSaver.Inhibit \
229                  string:$window_id \
230                  string:xdg-screensaver \
231                  | grep uint32 | cut -d ' ' -f 5 >| "$screensaver_file.cookie" \
232                  2> /dev/null
233        result=$?
234        ;;
235
236        resume)
237        if [ -f "$screensaver_file.cookie" ] ; then
238            value=`cat "$screensaver_file.cookie"`
239            dbus-send --session \
240                      --dest=org.freedesktop.ScreenSaver \
241                      --type=method_call \
242                      /ScreenSaver \
243                      org.freedesktop.ScreenSaver.UnInhibit \
244                      uint32:$value \
245                      2> /dev/null
246            rm -f "$screensaver_file.cookie"
247        fi
248        result=$?
249        ;;
250
251        activate)
252        dbus-send --session \
253                  --dest=org.freedesktop.ScreenSaver \
254                  --type=method_call \
255                  /ScreenSaver \
256                  org.freedesktop.ScreenSaver.SetActive \
257                  boolean:true \
258                  2> /dev/null
259        result=$?
260        ;;
261
262        lock)
263        dbus-send --session \
264                  --dest=org.freedesktop.ScreenSaver \
265                  --type=method_call \
266                  /ScreenSaver \
267                  org.freedesktop.ScreenSaver.Lock \
268                  2> /dev/null
269        ;;
270
271        reset)
272        if [ -f "$screensaver_file.cookie" ] ; then
273            value=`cat "$screensaver_file.cookie"`
274            dbus-send --session \
275                      --dest=org.freedesktop.ScreenSaver \
276                      --type=method_call \
277                      /ScreenSaver \
278                      org.freedesktop.ScreenSaver.UnInhibit \
279                      uint32:$value \
280                      2> /dev/null
281            rm -f "$screensaver_file.cookie"
282        fi
283        result=$?
284        ;;
285
286        status)
287        status=`dbus-send --session \
288                          --dest=org.freedesktop.ScreenSaver \
289                          --type=method_call \
290                          --print-reply \
291                          --reply-timeout=2000 \
292                          /ScreenSaver \
293                          org.freedesktop.ScreenSaver.GetActive \
294                          | grep boolean | cut -d ' ' -f 5`
295        result=$?
296        if [ x"$status" = "xtrue" ]; then
297            echo "enabled"
298        elif [ x"$status" = "xfalse" ]; then
299            echo "disabled"
300        else
301            echo "ERROR: dbus org.freedesktop.ScreenSaver.GetActive returned '$status'" >&2
302            return 1
303        fi
304        ;;
305
306        *)
307        echo "ERROR: Unknown command '$1'" >&2
308        return 1
309        ;;
310    esac
311}
312
313screensaver_kde()
314{
315    case "$1" in
316        suspend)
317        dcop kdesktop KScreensaverIface enable false > /dev/null
318        result=$?
319        ;;
320
321        resume)
322        dcop kdesktop KScreensaverIface configure > /dev/null
323        result=$?
324        ;;
325
326        activate)
327        dcop kdesktop KScreensaverIface save > /dev/null
328        result=$?
329        ;;
330
331        lock)
332        dcop kdesktop KScreensaverIface lock > /dev/null
333        result=$?
334        ;;
335
336        reset)
337        # Turns the screensaver off right now
338        dcop kdesktop KScreensaverIface quit > /dev/null
339        result=$?
340        ;;
341
342        status)
343        status=`dcop kdesktop KScreensaverIface isEnabled`
344        result=$?
345        if [ x"$status" = "xtrue" ]; then
346            echo "enabled"
347        elif [ x"$status" = "xfalse" ]; then
348            echo "disabled"
349        else
350            echo "ERROR: kdesktop KScreensaverIface isEnabled returned '$status'" >&2
351            return 1
352        fi
353        ;;
354
355        *)
356        echo "ERROR: Unknown command '$1'" >&2
357        return 1
358        ;;
359    esac
360}
361
362screensaver_xserver()
363{
364    case "$1" in
365        suspend)
366        xset s off > /dev/null
367        result=$?
368        ;;
369
370        resume)
371        xset s default > /dev/null
372        result=$?
373        ;;
374
375        activate)
376        xset s activate > /dev/null
377        result=$?
378        ;;
379
380        reset)
381        xset s reset > /dev/null
382        result=$?
383        ;;
384
385        status)
386        timeout=`xset q | sed '/^Screen Saver:/,/^[^ ]/ { s/.*timeout: *\([0-9]*\).*/\1/; t }; d'`
387        result=$?
388        if [ "$timeout" -gt 0 ]; then
389            echo "enabled"
390        elif [ "$timeout" -eq 0 ]; then
391            echo "disabled"
392        else
393            echo "ERROR: xset q did not report the screensaver timeout" >&2
394            return 1
395        fi
396        ;;
397
398        *)
399        echo "ERROR: Unknown command '$1'" >&2
400        return 1
401        ;;
402    esac
403}
404
405screensaver_suspend_loop()
406{
407  lockfile
408  test "${TMPDIR+set}" = set || TMPDIR=/tmp
409  tmpfile=`mktemp $TMPDIR/tmp.XXXXXXXXXX`
410  # Filter stale entries from the xdg-screensaver status file
411  cat "$screensaver_file" 2> /dev/null | (
412    IFS_save="$IFS"
413    IFS=":"
414    while read wid pid; do
415      if ps -p "$pid" 2> /dev/null | grep xprop > /dev/null; then
416        echo "$wid:$pid"
417      fi
418    done
419    IFS="$IFS_save"
420  ) > $tmpfile
421  if [ -s "$tmpfile" ] ; then
422    # Suspend pending, don't do a thing
423    $MV "$tmpfile" "$screensaver_file"
424    unlockfile
425    return
426  fi
427  $MV "$tmpfile" "$screensaver_file"
428  unlockfile
429  (while [ -f "$screensaver_file" ]; do $*; sleep 50; done) > /dev/null 2> /dev/null &
430}
431
432screensaver_gnome_screensaver()
433{
434# DBUS interface for gnome-screensaver
435# http://people.gnome.org/~mccann/gnome-screensaver/docs/gnome-screensaver.html
436    case "$1" in
437        suspend)
438        screensaver_suspend_loop \
439        dbus-send --session \
440                  --dest=org.gnome.ScreenSaver \
441                  --type=method_call \
442                  /org/gnome/ScreenSaver \
443                  org.gnome.ScreenSaver.SimulateUserActivity \
444                  2> /dev/null
445        result=$?
446        ;;
447
448        resume)
449        # Automatic resume when $screensaver_file disappears
450        result=0
451        ;;
452
453        activate)
454        dbus-send --session \
455                  --dest=org.gnome.ScreenSaver \
456                  --type=method_call \
457                  /org/gnome/ScreenSaver \
458                  org.gnome.ScreenSaver.SetActive \
459                  boolean:true \
460                  2> /dev/null
461        result=$?
462        ;;
463
464        lock)
465        gnome-screensaver-command --lock > /dev/null 2> /dev/null
466        result=$?
467        ;;
468
469        reset)
470        # Turns the screensaver off right now
471        dbus-send --session \
472                  --dest=org.gnome.ScreenSaver \
473                  --type=method_call \
474                  /org/gnome/ScreenSaver \
475                  org.gnome.ScreenSaver.SimulateUserActivity \
476                 2> /dev/null
477        result=$?
478        ;;
479
480        status)
481        status=`dbus-send --session \
482                          --dest=org.gnome.ScreenSaver \
483                          --type=method_call \
484                          --print-reply \
485                          --reply-timeout=2000 \
486                          /org/gnome/ScreenSaver \
487                          org.gnome.ScreenSaver.GetActive \
488                          | grep boolean | cut -d ' ' -f 5`
489        result=$?
490        if [ x"$status" = "xtrue" -o x"$status" = "xfalse" ]; then
491            echo "enabled"
492        elif [ x"$result" != "x0" ]; then
493            echo "ERROR: dbus org.gnome.ScreenSaver.GetActive returned '$status'" >&2
494            return 1
495        else
496            echo "disabled"
497        fi
498        ;;
499
500        *)
501        echo "ERROR: Unknown command '$1" >&2
502        return 1
503        ;;
504    esac
505}
506
507screensaver_xscreensaver()
508{
509    case "$1" in
510        suspend)
511        screensaver_suspend_loop xscreensaver-command -deactivate
512        result=0
513        ;;
514
515        resume)
516        # Automatic resume when $screensaver_file disappears
517        result=0
518        ;;
519
520        activate)
521        xscreensaver-command -activate > /dev/null 2> /dev/null
522        result=$?
523        ;;
524
525        lock)
526        xscreensaver-command -lock > /dev/null 2> /dev/null
527        result=$?
528        ;;
529
530        reset)
531        # Turns the screensaver off right now
532        xscreensaver-command -deactivate > /dev/null 2> /dev/null
533        result=$?
534        ;;
535
536        status)
537        result=0
538        if [ -f "$screensaver_file" ] ; then
539            echo "disabled"
540        else
541            echo "enabled"
542        fi
543        ;;
544
545        *)
546        echo "ERROR: Unknown command '$1" >&2
547        return 1
548        ;;
549    esac
550}
551
552[ x"$1" != x"" ] || exit_failure_syntax
553
554action=
555window_id=
556
557case $1 in
558  suspend)
559    action="$1"
560
561    shift
562
563    if [ -z "$1" ] ; then
564        exit_failure_syntax "WindowID argument missing"
565    fi
566
567    window_id="$1"
568    check_window_id
569    ;;
570
571  resume)
572    action="$1"
573
574    shift
575
576    if [ -z "$1" ] ; then
577        exit_failure_syntax "WindowID argument missing"
578    fi
579
580    window_id="$1"
581    check_window_id
582    ;;
583
584  activate)
585    action="$1"
586    ;;
587
588  lock)
589    action="$1"
590    ;;
591
592  reset)
593    action="$1"
594    ;;
595
596  status)
597    action="$1"
598    ;;
599
600  *)
601    exit_failure_syntax "unknown command '$1'"
602    ;;
603esac
604
605detectDE
606# Consider "xscreensaver" a separate DE
607xscreensaver-command -version 2> /dev/null | grep XScreenSaver > /dev/null && DE="xscreensaver"
608# Consider "gnome-screensaver" a separate DE
609gnome-screensaver-command -q > /dev/null 2>&1 && DE="gnome_screensaver"
610
611if [ "$action" = "resume" ] ; then
612    do_resume
613    exit_success
614fi
615
616perform_action "$action"
617
618if [ "$action" = "suspend" ] ; then
619    # Start tracking $window_id and resume the screensaver once it disappears
620    ( track_window  ) 2> /dev/null > /dev/null &
621fi
622
623if [ $result -eq 0 ]; then
624    exit_success
625else
626    exit_failure_operation_failed
627fi
628