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
|
||||
|
||||
// #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);
|
Loading…
Reference in New Issue
Block a user