Linux xorg screensaver

Screensaver Xorg 內建螢幕保護程式

  • 常見 commnad

  • xset s on, 開啟螢幕保護程式

  • xset x off, 關閉螢幕保護程式

  • xset s reset, 重設螢幕保護程式, 重設 clock, 直接喚醒進入睡眠的螢幕保護程式

設定螢幕保護

  • xset s {seconds} {interval seconds}, 設定螢幕保護時間
    • xset s 900 900, 閒置15分鐘進入休眠
  • xset s blank, 進入休眠 黑畫面

串接 API

#pragma once

#include <functional>
#include <memory>

enum STATE : int {
	E_SCREEN_SAVER_UNKNOW = -1,
	E_SCREEN_SAVER_WAKE = 0,
	E_SCREEN_SAVER_SLEEP = 1,
	E_SCREEN_SAVER_DISABLE = 3,
};

class ScreenSaver {
public:
	ScreenSaver();
	~ScreenSaver();

	// enable default screensaver idle 10min into screensaver
	void enable();
	// enable  screensaver idle seconds into screensaver
	void enable(int seconds);
	// disable screensaver
	void disable();

	STATE getState();

	void wake();
	void nap();

	void registerWakeNotifyFn(std::function<void(void)> fn);
	void registerSleepNotifyFn(std::function<void(void)> fn);

	// monitor screensaver state, trigger notify callback after state changed
	void monitor();

private:
	struct ScreenSaverImpl;
	std::unique_ptr<ScreenSaverImpl> m_impl;
};
#include "screensaver.h"

#include <error.h>
#include <string.h>

#include <vector>

extern "C" {
#include <X11/X.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/scrnsaver.h>
}

struct ScreenSaver::ScreenSaverImpl {
	Display *m_dpy;
	Window m_root;
	XScreenSaverInfo *m_info;
	int m_saverEvent = 0;

	std::vector<std::function<void(void)>> m_wakeNotifyFnVec;
	std::vector<std::function<void(void)>> m_sleepNotifyFnVec;

	enum TIMEOUT : int {
		SERVER_DEFAULT = -1,
		DEFAULT_TIMEOUT = -600,
	};
	enum MASK : int {
		ALL = -1,
		TIMEOUT = 1,
		INTERVAL = 2,
		PREFER_BLANK = 3,
		ALLOW_EXP = 4,
	};

	ScreenSaverImpl()
		: m_dpy{XOpenDisplay(getenv("DISPLAY"))},
		  m_root{RootWindow(m_dpy, DefaultScreen(m_dpy))},
		  m_info{XScreenSaverAllocInfo()} {
		if (!subscribeScreenSaver_()) {
			fprintf(stderr, "could not subsrube screensaver:%s", strerror(errno));
		}
	}

	~ScreenSaverImpl() { XFree(m_info); }

	static void setSaver_(Display *dpy, MASK mask, int value) {
		int timeout, interval, prefer_blank, allow_exp;

		XGetScreenSaver(dpy, &timeout, &interval, &prefer_blank, &allow_exp);
		if (mask == TIMEOUT) timeout = value;
		if (mask == INTERVAL) interval = value;
		if (mask == PREFER_BLANK) prefer_blank = value;
		if (mask == ALLOW_EXP) allow_exp = value;
		if (mask == ALL) {
			timeout = SERVER_DEFAULT;
			interval = SERVER_DEFAULT;
			prefer_blank = DefaultBlanking;
			allow_exp = DefaultExposures;
		}
		XSetScreenSaver(dpy, timeout, interval, prefer_blank, allow_exp);
		if (mask == ALL && value == DEFAULT_TIMEOUT) {
			XGetScreenSaver(dpy, &timeout, &interval, &prefer_blank, &allow_exp);
			if (!timeout) XSetScreenSaver(dpy, -DEFAULT_TIMEOUT, interval, prefer_blank, allow_exp);
		}
		return;
	}

	void disable() {
		setSaver_(m_dpy, TIMEOUT, 0);
		setSaver_(m_dpy, INTERVAL, 0);
	}

	void resetSaver_() {
		setSaver_(m_dpy, PREFER_BLANK, DontPreferBlanking);
		disable();
	}

	void reset() { resetSaver_(); }

	void enable() { setSaver_(m_dpy, ALL, DEFAULT_TIMEOUT); }
	void enable(int seconds) { setSaver(seconds); }

	void setSaver(int timeout) {
		setSaver_(m_dpy, ALL, 0);
		setSaver_(m_dpy, TIMEOUT, timeout);
	}

	void wake() { XResetScreenSaver(m_dpy); }

	void nap() { XActivateScreenSaver(m_dpy); }

	int getState() {
		try {
			XInitThreads();
			XScreenSaverQueryInfo(m_dpy, m_root, m_info);
			return m_info->state;
		} catch (const std::exception &e) {
			fprintf(stderr, "could not query: %s", e.what());
			return E_SCREEN_SAVER_UNKNOW;
		}
	}

	bool subscribeScreenSaver_() {
		int error = 0, event = 0;
		if (!XScreenSaverQueryExtension(m_dpy, &event, &error)) {
			fprintf(stderr, "could not query screensaver extension");
			return false;
		}
		m_saverEvent = event;

		XScreenSaverSelectInput(m_dpy, DefaultRootWindow(m_dpy), ScreenSaverNotifyMask);
		return true;
	}

	void notifyStateChanged_(int state) {
		if (state == STATE::E_SCREEN_SAVER_WAKE) {
			for (const auto &fn : m_wakeNotifyFnVec) {
				fn();
			}
		} else if (state == STATE::E_SCREEN_SAVER_SLEEP) {
			for (const auto &fn : m_sleepNotifyFnVec) {
				fn();
			}
		}
	}

	void monitor() {
		int state = getState();

		for (;;) {
			XEvent ev;
			u_long mask;

			XNextEvent(m_dpy, &ev);

			if (ev.type == m_saverEvent) {
				XScreenSaverNotifyEvent *se = (XScreenSaverNotifyEvent *)&ev;
				printf("state: %d\n", se->state);
				if (se->state != state) {
					printf("screensaver state changed\n");
					state = se->state;
					notifyStateChanged_(state);
				}
			}

			printf("next loop\n");
		}
	}

	void registerWakeNotifyFn(std::function<void(void)> fn) { m_wakeNotifyFnVec.push_back(fn); }
	void registerSleepNotifyFn(std::function<void(void)> fn) { m_sleepNotifyFnVec.push_back(fn); }
};

ScreenSaver::ScreenSaver() : m_impl{std::make_unique<ScreenSaverImpl>()} {}
ScreenSaver::~ScreenSaver() {}

STATE ScreenSaver::getState() { return static_cast<STATE>(m_impl->getState()); }
void ScreenSaver::enable() { m_impl->enable(); }
void ScreenSaver::enable(int seconds) { m_impl->enable(seconds); }
void ScreenSaver::disable() { m_impl->disable(); }
void ScreenSaver::wake() { m_impl->wake(); }
void ScreenSaver::nap() { m_impl->nap(); }
void ScreenSaver::monitor() { m_impl->monitor(); }
void ScreenSaver::registerWakeNotifyFn(std::function<void(void)> fn) {
	m_impl->registerWakeNotifyFn(fn);
}
void ScreenSaver::registerSleepNotifyFn(std::function<void(void)> fn) {
	m_impl->registerSleepNotifyFn(fn);
}

example

#include <stdio.h>

#include "screensaver.h"

static void wake_() { printf("wake\n"); }

static void sleep_() { printf("sleep\n"); }

int main(int argc, char const *argv[]) {
	auto screenSaver = std::make_unique<ScreenSaver>();
	screenSaver->registerSleepNotifyFn(sleep_);
	screenSaver->registerWakeNotifyFn(wake_);
	screenSaver->monitor();
	return 0;
}