Use the XLib DPMS extension from C wrappers instead of subprocess calls
This commit is contained in:
		@@ -1,48 +1,74 @@
 | 
			
		||||
package display
 | 
			
		||||
 | 
			
		||||
// #cgo CFLAGS: --std=gnu99 -pedantic -Wall
 | 
			
		||||
// #cgo LDFLAGS: -lX11 -lXext
 | 
			
		||||
// #include "dpms.h"
 | 
			
		||||
// #include <stdlib.h>
 | 
			
		||||
// #include <stdbool.h>
 | 
			
		||||
import "C"
 | 
			
		||||
import (
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Xset monitors/controls the display using xset
 | 
			
		||||
// Xset monitors/controls the display using the DPMS X extension
 | 
			
		||||
type Xset struct {}
 | 
			
		||||
 | 
			
		||||
// Suspend the display
 | 
			
		||||
func (Xset) Suspend() error {
 | 
			
		||||
	cmd := exec.Command("xset",
 | 
			
		||||
		"dpms",
 | 
			
		||||
		"force",
 | 
			
		||||
		"suspend",
 | 
			
		||||
	)
 | 
			
		||||
	return cmd.Run()
 | 
			
		||||
	xDisplayName := C.CString(tryGetXDisplay())
 | 
			
		||||
	defer C.free(unsafe.Pointer(xDisplayName))
 | 
			
		||||
 | 
			
		||||
	err := C.turn_display_off(xDisplayName)
 | 
			
		||||
	if err != C.int(0) {
 | 
			
		||||
		return convertErrorIntToGoError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func tryGetXDisplay() string {
 | 
			
		||||
	display := os.Getenv("DISPLAY")
 | 
			
		||||
	if display == "" {
 | 
			
		||||
		panic(errors.New("DISPLAY envvar not set"))
 | 
			
		||||
	}
 | 
			
		||||
	return display
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertErrorIntToGoError(err C.int) error {
 | 
			
		||||
	errorMsg := ""
 | 
			
		||||
	switch err {
 | 
			
		||||
	case C.BADDISPLAY:
 | 
			
		||||
		errorMsg = "Failed to open X display"
 | 
			
		||||
	case C.NODPMSEXTENSION:
 | 
			
		||||
		errorMsg = "DPMS extension is not available"
 | 
			
		||||
	case C.NOTDPMSCAPABLE:
 | 
			
		||||
		errorMsg = "Display is not DPMS capable"
 | 
			
		||||
	case C.UNKNOWNDPMSMODE:
 | 
			
		||||
		errorMsg = "Reported DPMS state unknown"
 | 
			
		||||
	case C.DPMSDISABLED:
 | 
			
		||||
		errorMsg = "DPMS is disabled"
 | 
			
		||||
	default:
 | 
			
		||||
		errorMsg = "Unknown error from dpms.h"
 | 
			
		||||
	}
 | 
			
		||||
	return errors.New(errorMsg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsOn determines if the display is on
 | 
			
		||||
func (Xset) IsOn() (bool, error) {
 | 
			
		||||
	cmd := exec.Command("xset", "q")
 | 
			
		||||
	outputBytes, err := cmd.CombinedOutput()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	return determineIfDisplayIsOn(string(outputBytes)), nil
 | 
			
		||||
}
 | 
			
		||||
	xDisplayName := C.CString(tryGetXDisplay())
 | 
			
		||||
	defer C.free(unsafe.Pointer(xDisplayName))
 | 
			
		||||
	var isDisplayOn C.bool
 | 
			
		||||
 | 
			
		||||
func determineIfDisplayIsOn(xsetOutput string) bool {
 | 
			
		||||
	if ! strings.Contains(xsetOutput, "Monitor") {
 | 
			
		||||
		// after booting xset q is missing this line
 | 
			
		||||
		// lacking better ideas assume display is on
 | 
			
		||||
		return true
 | 
			
		||||
	err := C.is_display_on(xDisplayName, &isDisplayOn)
 | 
			
		||||
	if err != C.int(0) {
 | 
			
		||||
		return false, convertErrorIntToGoError(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	re := regexp.MustCompile(`Monitor is (\w+)`)
 | 
			
		||||
	matches := re.FindStringSubmatch(xsetOutput)
 | 
			
		||||
	if len(matches) >= 2 {
 | 
			
		||||
		if matches[1] == "On" {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	if isDisplayOn {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										130
									
								
								xdg/display/dpms.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								xdg/display/dpms.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
/*
 | 
			
		||||
* Based on xorg-xset
 | 
			
		||||
* https://github.com/freedesktop/xorg-xset/blob/master/xset.c
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "dpms.h"
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <X11/Xlib.h>
 | 
			
		||||
#include <X11/extensions/dpms.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int check_dpms_extension(Display* disp)
 | 
			
		||||
{
 | 
			
		||||
    int err = 0;
 | 
			
		||||
    int dummy;
 | 
			
		||||
 | 
			
		||||
    if (DPMSQueryExtension(disp, &dummy, &dummy))
 | 
			
		||||
    {
 | 
			
		||||
        if (!DPMSCapable(disp))
 | 
			
		||||
            err = NOTDPMSCAPABLE;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        err = NODPMSEXTENSION;
 | 
			
		||||
 | 
			
		||||
    return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
* status->state can result in:
 | 
			
		||||
*  - DPMSModeOn
 | 
			
		||||
*  - DPMSModeStandby
 | 
			
		||||
*  - DPMSModeSuspend
 | 
			
		||||
*  - DPMSModeOff
 | 
			
		||||
*
 | 
			
		||||
* anything else means unknown state
 | 
			
		||||
*/
 | 
			
		||||
int check_dpms_status(Display* disp, CARD16* state)
 | 
			
		||||
{
 | 
			
		||||
    BOOL dpmsEnabled;
 | 
			
		||||
 | 
			
		||||
    int error = check_dpms_extension(disp);
 | 
			
		||||
    if (!error)
 | 
			
		||||
    {
 | 
			
		||||
        DPMSInfo(disp, state, &dpmsEnabled);
 | 
			
		||||
        if (!dpmsEnabled)
 | 
			
		||||
            return DPMSDISABLED;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        return error;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int is_display_on(const char* xDisplayName, bool* isOn)
 | 
			
		||||
{
 | 
			
		||||
    Display* disp = XOpenDisplay(xDisplayName);
 | 
			
		||||
    if (disp == NULL)
 | 
			
		||||
        return BADDISPLAY;
 | 
			
		||||
 | 
			
		||||
    CARD16 state;
 | 
			
		||||
    int returnError = 0;
 | 
			
		||||
    int error = check_dpms_status(disp, &state);
 | 
			
		||||
 | 
			
		||||
    if (!error)
 | 
			
		||||
    {
 | 
			
		||||
        switch (state)
 | 
			
		||||
        {
 | 
			
		||||
        case DPMSModeOn:
 | 
			
		||||
            *isOn = true;
 | 
			
		||||
            break;
 | 
			
		||||
        case DPMSModeStandby: case DPMSModeSuspend: case DPMSModeOff:
 | 
			
		||||
            *isOn = false;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            returnError = UNKNOWNDPMSMODE;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else if (error == DPMSDISABLED)
 | 
			
		||||
        *isOn = true;
 | 
			
		||||
    else if (error)
 | 
			
		||||
        returnError = error;
 | 
			
		||||
 | 
			
		||||
    XCloseDisplay(disp);
 | 
			
		||||
    return returnError;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
* Citing the original xorg-xset:
 | 
			
		||||
*
 | 
			
		||||
* The calls to usleep below are necessary to
 | 
			
		||||
* delay the actual DPMS mode setting briefly.
 | 
			
		||||
* Without them, it's likely that the mode will be
 | 
			
		||||
* set between the Down and Up key transitions, in
 | 
			
		||||
* which case the Up transition may immediately
 | 
			
		||||
* turn the display back on.
 | 
			
		||||
*/
 | 
			
		||||
int dpms_force_off(Display* disp)
 | 
			
		||||
{
 | 
			
		||||
    int error = check_dpms_extension(disp);
 | 
			
		||||
    if (!error)
 | 
			
		||||
    {
 | 
			
		||||
        DPMSEnable(disp);
 | 
			
		||||
        usleep(100000);
 | 
			
		||||
        DPMSForceLevel(disp, DPMSModeOff);
 | 
			
		||||
    }
 | 
			
		||||
    return error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int turn_display_off(const char* xDisplayName)
 | 
			
		||||
{
 | 
			
		||||
    Display* disp = XOpenDisplay(xDisplayName);
 | 
			
		||||
    if (disp == NULL)
 | 
			
		||||
        return BADDISPLAY;
 | 
			
		||||
 | 
			
		||||
    int returnError = 0;
 | 
			
		||||
    int error = dpms_force_off(disp);
 | 
			
		||||
    if (error)
 | 
			
		||||
        returnError = error;
 | 
			
		||||
 | 
			
		||||
    XCloseDisplay(disp);
 | 
			
		||||
    return returnError;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								xdg/display/dpms.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								xdg/display/dpms.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define BADDISPLAY -1
 | 
			
		||||
#define DPMSDISABLED -2
 | 
			
		||||
#define NODPMSEXTENSION -3
 | 
			
		||||
#define NOTDPMSCAPABLE -4
 | 
			
		||||
#define UNKNOWNDPMSMODE -5
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int turn_display_off(const char* xDisplayName);
 | 
			
		||||
int is_display_on(const char* xDisplayName, bool* isOn);
 | 
			
		||||
		Reference in New Issue
	
	Block a user