2022-05-12 19:48:18 +03:00
|
|
|
#include "idle_tracking.h"
|
|
|
|
|
#include "settings.h"
|
2022-05-15 09:24:44 +03:00
|
|
|
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
|
2022-05-12 19:48:18 +03:00
|
|
|
#if defined(TARGET_LINUX)
|
|
|
|
|
|
2022-05-19 20:52:59 +03:00
|
|
|
#include <QObject>
|
|
|
|
|
#include <QDBusConnection>
|
|
|
|
|
#include <QDBusReply>
|
|
|
|
|
#include <QDBusInterface>
|
|
|
|
|
|
2022-05-12 19:48:18 +03:00
|
|
|
// Thanks to https://stackoverflow.com/questions/222606/detecting-keyboard-mouse-activity-in-linux
|
|
|
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#include <stdio.h>
|
2022-05-19 20:52:59 +03:00
|
|
|
#include <stdlib.h>
|
2022-05-12 19:48:18 +03:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
|
#include <X11/Xutil.h>
|
2022-05-15 19:39:05 +03:00
|
|
|
|
|
|
|
|
// This requires sudo apt install libxss-dev
|
2022-05-13 08:25:15 +03:00
|
|
|
#include <X11/extensions/scrnsaver.h> // This can require libxss-dev to be installed
|
2022-05-15 09:24:44 +03:00
|
|
|
#include <dlfcn.h>
|
2022-05-19 20:52:59 +03:00
|
|
|
// #include <qmetatype.h>
|
|
|
|
|
// #include <QDBusConnection>
|
2022-05-12 19:48:18 +03:00
|
|
|
|
2022-05-19 20:52:59 +03:00
|
|
|
/*
|
|
|
|
|
// Prototype from stackoverflow
|
2022-05-12 19:48:18 +03:00
|
|
|
int get_idle_time()
|
|
|
|
|
{
|
|
|
|
|
time_t idle_time;
|
|
|
|
|
static XScreenSaverInfo *mit_info;
|
|
|
|
|
Display *display;
|
|
|
|
|
int screen;
|
|
|
|
|
|
|
|
|
|
mit_info = XScreenSaverAllocInfo();
|
|
|
|
|
if ((display = XOpenDisplay(NULL)) == NULL) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
screen = DefaultScreen(display);
|
|
|
|
|
XScreenSaverQueryInfo(display, RootWindow(display, screen), mit_info);
|
2022-05-14 22:53:13 +03:00
|
|
|
idle_time = (mit_info->idle);
|
2022-05-12 19:48:18 +03:00
|
|
|
XFree(mit_info);
|
|
|
|
|
XCloseDisplay(display);
|
|
|
|
|
|
|
|
|
|
return idle_time;
|
|
|
|
|
}
|
2022-05-19 20:52:59 +03:00
|
|
|
*/
|
2022-05-12 19:48:18 +03:00
|
|
|
|
2022-05-15 09:24:44 +03:00
|
|
|
int get_idle_time_x11()
|
|
|
|
|
{
|
|
|
|
|
void* lib_xss = dlopen("libXss.so", RTLD_LAZY);
|
|
|
|
|
if (!lib_xss)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
void* lib_x11 = dlopen("libX11.so", RTLD_LAZY);
|
|
|
|
|
if (!lib_x11)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
typedef XScreenSaverInfo* (*xss_alloc_info)(void);
|
|
|
|
|
xss_alloc_info alloc_info = (xss_alloc_info)dlsym(lib_xss, "XScreenSaverAllocInfo");
|
|
|
|
|
|
|
|
|
|
typedef Display* (*x11_open_display)(void*);
|
|
|
|
|
x11_open_display open_display = (x11_open_display)dlsym(lib_x11, "XOpenDisplay");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef Status (*xss_query_info)( Display* /* display */,
|
|
|
|
|
Drawable /* drawable */,
|
|
|
|
|
XScreenSaverInfo* /* info */);
|
|
|
|
|
xss_query_info query_info = (xss_query_info)dlsym(lib_xss, "XScreenSaverQueryInfo");
|
|
|
|
|
|
|
|
|
|
typedef int (*x11_free)(void*);
|
|
|
|
|
x11_free free_mem = (x11_free)dlsym(lib_x11, "XFree");
|
|
|
|
|
|
|
|
|
|
typedef int (*x11_close_display)(Display* display);
|
|
|
|
|
x11_close_display close_display = (x11_close_display)dlsym(lib_x11, "XCloseDisplay");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
time_t idle_time;
|
|
|
|
|
static XScreenSaverInfo *mit_info;
|
|
|
|
|
Display *display;
|
|
|
|
|
int screen;
|
|
|
|
|
|
|
|
|
|
mit_info = alloc_info();
|
|
|
|
|
if ((display = open_display(NULL)) == NULL) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
screen = DefaultScreen(display);
|
|
|
|
|
query_info(display, RootWindow(display, screen), mit_info);
|
|
|
|
|
idle_time = (mit_info->idle);
|
|
|
|
|
free_mem(mit_info);
|
|
|
|
|
close_display(display);
|
|
|
|
|
|
|
|
|
|
dlclose(lib_xss);
|
|
|
|
|
dlclose(lib_x11);
|
|
|
|
|
return idle_time;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-19 20:52:59 +03:00
|
|
|
int get_idle_time_gnome()
|
|
|
|
|
{
|
|
|
|
|
auto bus = QDBusConnection::sessionBus();
|
|
|
|
|
if (!bus.isConnected())
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
QDBusInterface interface( "org.gnome.Mutter.IdleMonitor",
|
|
|
|
|
"/org/gnome/Mutter/IdleMonitor/Core",
|
|
|
|
|
"org.gnome.Mutter.IdleMonitor");
|
|
|
|
|
|
|
|
|
|
QDBusReply<int> reply = interface.call("GetIdletime");
|
|
|
|
|
|
|
|
|
|
return reply.isValid() ? reply.value() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(USE_WAYLAND)
|
2022-05-20 10:20:41 +03:00
|
|
|
#include <wayland-client-protocol-unstable.hpp>
|
|
|
|
|
|
|
|
|
|
class kde_idle_detector
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
wayland::seat_t seat;
|
|
|
|
|
wayland::display_t d;
|
|
|
|
|
wayland::org_kde_kwin_idle_t idle;
|
|
|
|
|
wayland::org_kde_kwin_idle_timeout_t idle_timer;
|
|
|
|
|
|
|
|
|
|
uint64_t idle_start = 0;
|
|
|
|
|
uint64_t idle_finish = 0;
|
|
|
|
|
bool active = false;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
kde_idle_detector()
|
|
|
|
|
{}
|
|
|
|
|
~kde_idle_detector()
|
2022-05-20 14:40:01 +03:00
|
|
|
{
|
|
|
|
|
stop();
|
|
|
|
|
}
|
2022-05-20 10:20:41 +03:00
|
|
|
|
|
|
|
|
// Idle timeout is in msec
|
|
|
|
|
void start(int idle_timeout)
|
|
|
|
|
{
|
|
|
|
|
if (active)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto registry = d.get_registry();
|
|
|
|
|
registry.on_global() = [&] (uint32_t name, const std::string& interface, uint32_t version)
|
|
|
|
|
{
|
|
|
|
|
if (interface == wayland::seat_t::interface_name)
|
2022-05-20 14:40:01 +03:00
|
|
|
registry.bind(name, this->seat, version);
|
|
|
|
|
else
|
2022-05-20 10:20:41 +03:00
|
|
|
if (interface == wayland::org_kde_kwin_idle_t::interface_name)
|
2022-05-20 14:40:01 +03:00
|
|
|
registry.bind(name, this->idle, version);
|
2022-05-20 10:20:41 +03:00
|
|
|
};
|
|
|
|
|
d.roundtrip();
|
|
|
|
|
|
2022-05-20 14:40:01 +03:00
|
|
|
|
2022-05-20 10:20:41 +03:00
|
|
|
bool has_keyboard = false, has_pointer = false;
|
|
|
|
|
seat.on_capabilities() = [&] (const wayland::seat_capability& capability)
|
|
|
|
|
{
|
|
|
|
|
has_keyboard = capability & wayland::seat_capability::keyboard;
|
|
|
|
|
has_pointer = capability & wayland::seat_capability::pointer;
|
|
|
|
|
};
|
|
|
|
|
d.roundtrip();
|
|
|
|
|
|
|
|
|
|
idle_timer = idle.get_idle_timeout(seat, idle_timeout);
|
|
|
|
|
idle_timer.on_idle() = [&]()
|
|
|
|
|
{
|
|
|
|
|
idle_start = ::time(nullptr);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
idle_timer.on_resumed() = [&]()
|
|
|
|
|
{
|
|
|
|
|
idle_finish = ::time(nullptr);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
active = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stop()
|
|
|
|
|
{
|
|
|
|
|
if (!active)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
active = false;
|
|
|
|
|
idle_timer.release();
|
|
|
|
|
seat.release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return idle time in microseconds
|
|
|
|
|
int get_idle_time() const
|
|
|
|
|
{
|
|
|
|
|
if (idle_start > idle_finish)
|
|
|
|
|
{
|
|
|
|
|
return (::time(nullptr) - idle_start) * 1000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
kde_idle_detector kde_idle;
|
2022-05-19 20:56:10 +03:00
|
|
|
int get_idle_time_kde_wayland()
|
2022-05-15 09:24:44 +03:00
|
|
|
{
|
2022-05-20 10:20:41 +03:00
|
|
|
// Ensure idle detector runs
|
2022-05-20 14:40:01 +03:00
|
|
|
kde_idle.start(1);
|
2022-05-15 09:24:44 +03:00
|
|
|
|
2022-05-20 10:20:41 +03:00
|
|
|
return kde_idle.get_idle_time();
|
2022-05-15 09:24:44 +03:00
|
|
|
}
|
2022-05-20 10:20:41 +03:00
|
|
|
|
2022-05-19 20:52:59 +03:00
|
|
|
#endif
|
2022-05-15 09:24:44 +03:00
|
|
|
|
|
|
|
|
int get_idle_time_dynamically()
|
|
|
|
|
{
|
|
|
|
|
const char* wl_display = std::getenv("WAYLAND_DISPLAY");
|
2022-05-20 17:59:26 +03:00
|
|
|
// const char* x11_display = std::getenv("DISPLAY");
|
|
|
|
|
|
|
|
|
|
#if defined(USE_WAYLAND)
|
|
|
|
|
if (wl_display)
|
2022-05-19 20:52:59 +03:00
|
|
|
{
|
2022-05-20 14:40:01 +03:00
|
|
|
const char* desktop_name = std::getenv("XDG_SESSION_DESKTOP");
|
2022-05-19 20:52:59 +03:00
|
|
|
if (!desktop_name)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (strcmp(desktop_name, "KDE") == 0)
|
2022-05-19 20:56:10 +03:00
|
|
|
return get_idle_time_kde_wayland();
|
2022-05-19 20:52:59 +03:00
|
|
|
else
|
|
|
|
|
if (strcmp(desktop_name, "GNOME") == 0)
|
|
|
|
|
return get_idle_time_gnome();
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2022-05-15 09:24:44 +03:00
|
|
|
else
|
|
|
|
|
return get_idle_time_x11();
|
2022-05-19 20:52:59 +03:00
|
|
|
#else
|
|
|
|
|
// Restrict to X11
|
2022-05-20 17:59:26 +03:00
|
|
|
if (wl_display)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2022-05-19 20:52:59 +03:00
|
|
|
return get_idle_time_x11();
|
|
|
|
|
#endif
|
2022-05-15 09:24:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(TARGET_WINDOWS)
|
2022-05-12 19:48:18 +03:00
|
|
|
|
2022-05-15 09:24:44 +03:00
|
|
|
// To handle Windows case later
|
|
|
|
|
// https://stackoverflow.com/questions/8820615/how-to-check-in-c-if-the-system-is-active
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
#define WINDOWS_LEAN_AND_MEAN
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
// do something after 10 minutes of user inactivity
|
|
|
|
|
static const unsigned int idle_milliseconds = 60*10*1000;
|
|
|
|
|
// wait at least an hour between two runs
|
|
|
|
|
static const unsigned int interval = 60*60*1000;
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
LASTINPUTINFO last_input;
|
|
|
|
|
BOOL screensaver_active;
|
|
|
|
|
|
|
|
|
|
// main loop to check if user has been idle long enough
|
|
|
|
|
for (;;) {
|
|
|
|
|
if ( !GetLastInputInfo(&last_input)
|
|
|
|
|
|| !SystemParametersInfo(SPI_GETSCREENSAVERACTIVE, 0,
|
|
|
|
|
&screensaver_active, 0))
|
|
|
|
|
{
|
|
|
|
|
std::cerr << "WinAPI failed!" << std::endl;
|
|
|
|
|
return ERROR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (last_input.dwTime < idle_milliseconds && !screensaver_active) {
|
|
|
|
|
// user hasn't been idle for long enough
|
|
|
|
|
// AND no screensaver is running
|
|
|
|
|
Sleep(1000);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// user has been idle at least 10 minutes
|
|
|
|
|
do_something();
|
|
|
|
|
// done. Wait before doing the next loop.
|
|
|
|
|
Sleep(interval);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
2022-05-12 19:48:18 +03:00
|
|
|
#endif
|