summaryrefslogblamecommitdiffstats
path: root/driver/dpms.c
blob: 15721eae31e0ee5fc2b99bbefe0afddedb4868b3 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12

                                                                    









                                                                              





                     
                          
 
                         
 
                                                         
 





                                                                          
 






                                                             
 











                                                             
                                                                     






                                                 

















                                                                      
                                   



                                                                          





















                                                                            


                                                                         




                          


                                                              




                                             


                                                                    






                                                               


                                                                        


                         
                                                         





                                           
                                                                           






                                                                
                                                                       









                                                                       
                                                                           


                         
                                                                      




                                                       
                                   





                                 
                                                             


                                                          
                             




                                                                            
                                    


















                                                                           
                                                 


                                     
                                   


                                                                       
                                               
                            
                                                                           
                                                     
                          






















                                                                         
                                                                         
                        
                                                                          




                                                        
                                              
/* dpms.c --- syncing the X Display Power Management System values
 * xscreensaver, Copyright © 2001-2021 Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or 
 * implied warranty.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>

#include "xscreensaver.h"

#ifndef HAVE_DPMS_EXTENSION   /* almost the whole file */

void
sync_server_dpms_settings (Display *dpy, struct saver_preferences *p)
{
  if (p->verbose_p)
    fprintf (stderr, "%s: DPMS not supported at compile time\n", blurb());
}

Bool monitor_powered_on_p (Display *dpy) { return True; }
void monitor_power_on (saver_info *si, Bool on_p) { return; }

#else /* HAVE_DPMS_EXTENSION -- whole file */

# include <X11/Xproto.h>
# include <X11/extensions/dpms.h>


static Bool error_handler_hit_p = False;

static int
ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
{
  error_handler_hit_p = True;
  return 0;
}


void
sync_server_dpms_settings (Display *dpy, struct saver_preferences *p)
{
  int event = 0, error = 0;
  BOOL o_enabled = False;
  CARD16 o_power = 0;
  CARD16 o_standby = 0, o_suspend = 0, o_off = 0;
  Bool bogus_p = False;

  Bool enabled_p       = (p->dpms_enabled_p && p->mode != DONT_BLANK);
  Bool dpms_quickoff_p = p->dpms_quickoff_p;
  int standby_secs     = p->dpms_standby / 1000;
  int suspend_secs     = p->dpms_suspend / 1000;
  int off_secs         = p->dpms_off / 1000;
  Bool verbose_p       = p->verbose_p;
  static Bool warned_p = False;

  /* If the monitor is currently powered off, defer any changes until
     we are next called while it is powered on. */
  if (! monitor_powered_on_p (dpy))
    return;

  /* Why did I do this?  It makes DPMS never happen.
     XSetScreenSaver (dpy, 0, 0, 0, 0);
     XForceScreenSaver (dpy, ScreenSaverReset);
   */

  if (dpms_quickoff_p && !off_secs)
    {
      /* To do this, we might need to temporarily re-enable DPMS first. */
      off_secs = 0xFFFF;
    }

  if (standby_secs == 0 && suspend_secs == 0 && off_secs == 0)
    /* all zero implies "DPMS disabled" */
    enabled_p = False;

  else if ((standby_secs != 0 && standby_secs < 10) ||
           (suspend_secs != 0 && suspend_secs < 10) ||
           (off_secs     != 0 && off_secs     < 10))
    /* any negative, or any positive-and-less-than-10-seconds, is crazy. */
    bogus_p = True;

  if (bogus_p) enabled_p = False;

  /* X protocol sends these values in a CARD16, so truncate them to 16 bits.
     This means that the maximum timeout is 18:12:15.
   */
  if (standby_secs > 0xFFFF) standby_secs = 0xFFFF;
  if (suspend_secs > 0xFFFF) suspend_secs = 0xFFFF;
  if (off_secs     > 0xFFFF) off_secs     = 0xFFFF;

  if (! DPMSQueryExtension (dpy, &event, &error))
    {
      if (verbose_p && !warned_p)
        fprintf (stderr, "%s: XDPMS extension not supported\n", blurb());
      warned_p = True;
      return;
    }

  if (! DPMSCapable (dpy))
    {
      if (verbose_p && !warned_p)
        fprintf (stderr, "%s: DPMS not supported\n", blurb());
      warned_p = True;
      return;
    }

  if (! DPMSInfo (dpy, &o_power, &o_enabled))
    {
      if (verbose_p && !warned_p)
        fprintf (stderr, "%s: unable to get DPMS state\n", blurb());
      warned_p = True;
      return;
    }

  if (o_enabled != enabled_p)
    {
      if (! (enabled_p ? DPMSEnable (dpy) : DPMSDisable (dpy)))
        {
          if (verbose_p && !warned_p)
            fprintf (stderr, "%s: unable to set DPMS state\n", blurb());
          warned_p = True;
          return;
        }
      else if (verbose_p)
        fprintf (stderr, "%s: turned DPMS %s\n", blurb(),
                 enabled_p ? "on" : "off");
    }

  if (bogus_p)
    {
      if (verbose_p)
        fprintf (stderr, "%s: not setting bogus DPMS timeouts: %d %d %d\n",
                 blurb(), standby_secs, suspend_secs, off_secs);
      return;
    }

  if (!DPMSGetTimeouts (dpy, &o_standby, &o_suspend, &o_off))
    {
      if (verbose_p)
        fprintf (stderr, "%s: unable to get DPMS timeouts\n", blurb());
      return;
    }

  if (o_standby != standby_secs ||
      o_suspend != suspend_secs ||
      o_off != off_secs)
    {
      if (!DPMSSetTimeouts (dpy, standby_secs, suspend_secs, off_secs))
        {
          if (verbose_p)
            fprintf (stderr, "%s: unable to set DPMS timeouts\n", blurb());
          return;
        }
      else if (verbose_p)
        fprintf (stderr, "%s: set DPMS timeouts: %d %d %d\n", blurb(),
                 standby_secs, suspend_secs, off_secs);
    }
}

Bool
monitor_powered_on_p (Display *dpy)
{
  Bool result;
  int event_number, error_number;
  BOOL onoff = False;
  CARD16 state;

  if (!DPMSQueryExtension(dpy, &event_number, &error_number))
    /* Server doesn't know -- assume the monitor is on. */
    result = True;

  else if (!DPMSCapable(dpy))
    /* Server says the monitor doesn't do power management -- so it's on. */
    result = True;

  else
    {
      DPMSInfo(dpy, &state, &onoff);
      if (!onoff)
	/* Server says DPMS is disabled -- so the monitor is on. */
	result = True;
      else
	switch (state) {
	case DPMSModeOn:      result = True;  break;  /* really on */
	case DPMSModeStandby: result = False; break;  /* kinda off */
	case DPMSModeSuspend: result = False; break;  /* pretty off */
	case DPMSModeOff:     result = False; break;  /* really off */
	default:	      result = True;  break;  /* protocol error? */
	}
    }

  return result;
}

void
monitor_power_on (saver_info *si, Bool on_p)
{
  if ((!!on_p) != monitor_powered_on_p (si->dpy))
    {
      XErrorHandler old_handler;
      int event_number, error_number;
      static Bool warned_p = False;
      if (!DPMSQueryExtension(si->dpy, &event_number, &error_number) ||
          !DPMSCapable(si->dpy))
        {
          if (si->prefs.verbose_p && !warned_p)
            fprintf (stderr,
                     "%s: unable to power %s monitor: no DPMS extension\n",
                     blurb(), (on_p ? "on" : "off"));
          warned_p = True;
          return;
        }

      /* The manual for DPMSForceLevel() says that it throws BadMatch if
         "DPMS is disabled on the specified display."

         The manual for DPMSCapable() says that it "returns True if the X
         server is capable of DPMS."

         Apparently they consider "capable of DPMS" and "DPMS is enabled"
         to be different things, and so even if DPMSCapable() returns
         True, DPMSForceLevel() *might* throw an X Error.  Isn't that
         just fucking special.
       */
      XSync (si->dpy, False);
      error_handler_hit_p = False;
      old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
      XSync (si->dpy, False);
      DPMSForceLevel(si->dpy, (on_p ? DPMSModeOn : DPMSModeOff));
      XSync (si->dpy, False);
      XSetErrorHandler (old_handler);
      /* Ignore error_handler_hit_p, just probe monitor instead */

      if ((!!on_p) != monitor_powered_on_p (si->dpy))  /* double-check */
	fprintf (stderr,
       "%s: DPMSForceLevel(dpy, %s) did not change monitor power state\n",
		 blurb(),
                 (on_p ? "DPMSModeOn" : "DPMSModeOff"));
    }
}

#endif /* HAVE_DPMS_EXTENSION -- whole file */