Use the XLib DPMS extension from C wrappers instead of subprocess calls
This commit is contained in:
parent
00bacdec3e
commit
0ce211b46d
@ -1,48 +1,74 @@
|
|||||||
package display
|
package display
|
||||||
|
|
||||||
|
// #cgo CFLAGS: --std=gnu99 -pedantic -Wall
|
||||||
|
// #cgo LDFLAGS: -lX11 -lXext
|
||||||
|
// #include "dpms.h"
|
||||||
|
// #include <stdlib.h>
|
||||||
|
// #include <stdbool.h>
|
||||||
|
import "C"
|
||||||
import (
|
import (
|
||||||
"os/exec"
|
"unsafe"
|
||||||
"regexp"
|
"errors"
|
||||||
"strings"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// Xset monitors/controls the display using xset
|
// Xset monitors/controls the display using the DPMS X extension
|
||||||
type Xset struct {}
|
type Xset struct {}
|
||||||
|
|
||||||
// Suspend the display
|
// Suspend the display
|
||||||
func (Xset) Suspend() error {
|
func (Xset) Suspend() error {
|
||||||
cmd := exec.Command("xset",
|
xDisplayName := C.CString(tryGetXDisplay())
|
||||||
"dpms",
|
defer C.free(unsafe.Pointer(xDisplayName))
|
||||||
"force",
|
|
||||||
"suspend",
|
err := C.turn_display_off(xDisplayName)
|
||||||
)
|
if err != C.int(0) {
|
||||||
return cmd.Run()
|
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
|
// IsOn determines if the display is on
|
||||||
func (Xset) IsOn() (bool, error) {
|
func (Xset) IsOn() (bool, error) {
|
||||||
cmd := exec.Command("xset", "q")
|
xDisplayName := C.CString(tryGetXDisplay())
|
||||||
outputBytes, err := cmd.CombinedOutput()
|
defer C.free(unsafe.Pointer(xDisplayName))
|
||||||
if err != nil {
|
var isDisplayOn C.bool
|
||||||
return false, err
|
|
||||||
}
|
err := C.is_display_on(xDisplayName, &isDisplayOn)
|
||||||
return determineIfDisplayIsOn(string(outputBytes)), nil
|
if err != C.int(0) {
|
||||||
|
return false, convertErrorIntToGoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func determineIfDisplayIsOn(xsetOutput string) bool {
|
if isDisplayOn {
|
||||||
if ! strings.Contains(xsetOutput, "Monitor") {
|
return true, nil
|
||||||
// after booting xset q is missing this line
|
|
||||||
// lacking better ideas assume display is on
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
return false, nil
|
||||||
re := regexp.MustCompile(`Monitor is (\w+)`)
|
|
||||||
matches := re.FindStringSubmatch(xsetOutput)
|
|
||||||
if len(matches) >= 2 {
|
|
||||||
if matches[1] == "On" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
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);
|
Loading…
Reference in New Issue
Block a user