- idle tracking - initial implementation
This commit is contained in:
parent
18ac04ec18
commit
d2d899280d
@ -4,7 +4,7 @@
|
|||||||
// App version
|
// App version
|
||||||
#define QBREAK_VERSION_MAJOR 0
|
#define QBREAK_VERSION_MAJOR 0
|
||||||
#define QBREAK_VERSION_MINOR 1
|
#define QBREAK_VERSION_MINOR 1
|
||||||
#define QBREAK_VERSION_SUFFIX 0
|
#define QBREAK_VERSION_SUFFIX 1
|
||||||
|
|
||||||
// How often UI is updated - interval in seconds
|
// How often UI is updated - interval in seconds
|
||||||
#define INTERVAL_UPDATE_UI (60)
|
#define INTERVAL_UPDATE_UI (60)
|
||||||
|
|||||||
37
app/idle_tracking.cpp
Normal file
37
app/idle_tracking.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include "idle_tracking.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#if defined(TARGET_LINUX)
|
||||||
|
|
||||||
|
// Thanks to https://stackoverflow.com/questions/222606/detecting-keyboard-mouse-activity-in-linux
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/extensions/scrnsaver.h>
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
idle_time = (mit_info->idle) / 1000 / 60;
|
||||||
|
XFree(mit_info);
|
||||||
|
XCloseDisplay(display);
|
||||||
|
|
||||||
|
return idle_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
7
app/idle_tracking.h
Normal file
7
app/idle_tracking.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef __IDLE_TRACKING_H
|
||||||
|
#define __IDLE_TRACKING_H
|
||||||
|
|
||||||
|
// Returns the idle time in minutes
|
||||||
|
extern int get_idle_time();
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -6,6 +6,7 @@
|
|||||||
#include "aboutdlg.h"
|
#include "aboutdlg.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "audio_support.h"
|
#include "audio_support.h"
|
||||||
|
#include "idle_tracking.h"
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
@ -78,18 +79,19 @@ void MainWindow::init()
|
|||||||
// No postpone attempts yet
|
// No postpone attempts yet
|
||||||
mPostponeCount = 0;
|
mPostponeCount = 0;
|
||||||
|
|
||||||
// Timer
|
// Timer to start break
|
||||||
mTimer = new QTimer(this);
|
mTimer = new QTimer(this);
|
||||||
mTimer->setTimerType(Qt::TimerType::CoarseTimer);
|
mTimer->setTimerType(Qt::TimerType::CoarseTimer);
|
||||||
mTimer->setSingleShot(true);
|
mTimer->setSingleShot(true);
|
||||||
connect(mTimer, SIGNAL(timeout()), this, SLOT(onLongBreakStart()));
|
connect(mTimer, SIGNAL(timeout()), this, SLOT(onLongBreakStart()));
|
||||||
|
|
||||||
|
// Timer to run notification about upcoming break
|
||||||
mNotifyTimer = new QTimer(this);
|
mNotifyTimer = new QTimer(this);
|
||||||
mNotifyTimer->setTimerType(Qt::TimerType::CoarseTimer);
|
mNotifyTimer->setTimerType(Qt::TimerType::CoarseTimer);
|
||||||
mNotifyTimer->setSingleShot(true);
|
mNotifyTimer->setSingleShot(true);
|
||||||
connect(mNotifyTimer, SIGNAL(timeout()), this, SLOT(onLongBreakNotify()));
|
connect(mNotifyTimer, SIGNAL(timeout()), this, SLOT(onLongBreakNotify()));
|
||||||
|
|
||||||
|
// Just update UI once per minute
|
||||||
mUpdateUITimer = new QTimer(this);
|
mUpdateUITimer = new QTimer(this);
|
||||||
mUpdateUITimer->setTimerType(Qt::TimerType::CoarseTimer);
|
mUpdateUITimer->setTimerType(Qt::TimerType::CoarseTimer);
|
||||||
mUpdateUITimer->setSingleShot(false);
|
mUpdateUITimer->setSingleShot(false);
|
||||||
@ -97,6 +99,7 @@ void MainWindow::init()
|
|||||||
connect(mUpdateUITimer, SIGNAL(timeout()), this, SLOT(onUpdateUI()));
|
connect(mUpdateUITimer, SIGNAL(timeout()), this, SLOT(onUpdateUI()));
|
||||||
mUpdateUITimer->start();
|
mUpdateUITimer->start();
|
||||||
|
|
||||||
|
// Timer to draw progress bar during the break
|
||||||
mProgressTimer = new QTimer(this);
|
mProgressTimer = new QTimer(this);
|
||||||
mProgressTimer->setInterval(std::chrono::milliseconds(INTERVAL_UPDATE_PROGRESS));
|
mProgressTimer->setInterval(std::chrono::milliseconds(INTERVAL_UPDATE_PROGRESS));
|
||||||
mProgressTimer->setSingleShot(false);
|
mProgressTimer->setSingleShot(false);
|
||||||
@ -269,6 +272,25 @@ void MainWindow::onUpdateUI()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui->mSkipButton->setVisible(mPostponeCount > 0);
|
ui->mSkipButton->setVisible(mPostponeCount > 0);
|
||||||
|
|
||||||
|
if (mAppConfig.idle_timeout != 0)
|
||||||
|
{
|
||||||
|
int idle_minutes = get_idle_time();
|
||||||
|
if (idle_minutes >= mAppConfig.idle_timeout)
|
||||||
|
{
|
||||||
|
// Idle mode is active. Increase the timer interval
|
||||||
|
mIdleStart = std::chrono::steady_clock::now() - std::chrono::minutes(idle_minutes);
|
||||||
|
|
||||||
|
int remaining_minutes = mTimer->remainingTime() / 1000 / 60;
|
||||||
|
|
||||||
|
// Change the time
|
||||||
|
mTimer->stop();
|
||||||
|
mTimer->start(std::chrono::minutes(remaining_minutes + idle_minutes));
|
||||||
|
|
||||||
|
qDebug() << "Idle detected for " << idle_minutes << " minutes. "
|
||||||
|
<< "Remaining " << mTimer->remainingTime() / 1000 / 60 << " until the next break.";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onLongBreakNotify()
|
void MainWindow::onLongBreakNotify()
|
||||||
|
|||||||
@ -39,6 +39,7 @@ private:
|
|||||||
std::chrono::steady_clock::time_point mBreakStartTime;
|
std::chrono::steady_clock::time_point mBreakStartTime;
|
||||||
app_settings::config mAppConfig;
|
app_settings::config mAppConfig;
|
||||||
int mPostponeCount;
|
int mPostponeCount;
|
||||||
|
std::chrono::steady_clock::time_point mIdleStart;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void loadConfig();
|
void loadConfig();
|
||||||
|
|||||||
@ -16,7 +16,8 @@ SOURCES += \
|
|||||||
settings.cpp \
|
settings.cpp \
|
||||||
settingsdialog.cpp \
|
settingsdialog.cpp \
|
||||||
runguard.cpp \
|
runguard.cpp \
|
||||||
audio_support.cpp
|
audio_support.cpp \
|
||||||
|
idle_tracking.cpp
|
||||||
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
@ -27,7 +28,8 @@ HEADERS += \
|
|||||||
settings.h \
|
settings.h \
|
||||||
settingsdialog.h \
|
settingsdialog.h \
|
||||||
runguard.h \
|
runguard.h \
|
||||||
audio_support.h
|
audio_support.h \
|
||||||
|
idle_tracking.h
|
||||||
|
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
@ -45,3 +47,7 @@ unix:!macx: DEFINES += TARGET_LINUX
|
|||||||
qnx: target.path = /tmp/$${TARGET}/bin
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
!isEmpty(target.path): INSTALLS += target
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
|
unix:LIBS += -L/usr/X11R6/lib/ -lX11 -lXext -lXss
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ const QString Key_PreferredMonitor = "Preferred_Monitor";
|
|||||||
const QString Key_Audio_Name = "Audio_Name";
|
const QString Key_Audio_Name = "Audio_Name";
|
||||||
const QString Key_Audio_Path = "Audio_Path";
|
const QString Key_Audio_Path = "Audio_Path";
|
||||||
const QString Key_Script = "Script";
|
const QString Key_Script = "Script";
|
||||||
|
const QString Key_Idle_Timeout = "Idle_Timeout";
|
||||||
|
|
||||||
void app_settings::save(const config &cfg)
|
void app_settings::save(const config &cfg)
|
||||||
{
|
{
|
||||||
@ -28,6 +29,7 @@ void app_settings::save(const config &cfg)
|
|||||||
s.setValue(Key_Audio_Name, cfg.play_audio.name);
|
s.setValue(Key_Audio_Name, cfg.play_audio.name);
|
||||||
s.setValue(Key_Audio_Path, cfg.play_audio.path);
|
s.setValue(Key_Audio_Path, cfg.play_audio.path);
|
||||||
s.setValue(Key_Script, cfg.script_on_break_finish);
|
s.setValue(Key_Script, cfg.script_on_break_finish);
|
||||||
|
s.setValue(Key_Idle_Timeout, cfg.idle_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
app_settings::config app_settings::load()
|
app_settings::config app_settings::load()
|
||||||
@ -45,6 +47,7 @@ app_settings::config app_settings::load()
|
|||||||
r.play_audio.name = s.value(Key_Audio_Name, Audio_Empty).toString();
|
r.play_audio.name = s.value(Key_Audio_Name, Audio_Empty).toString();
|
||||||
r.play_audio.path = s.value(Key_Audio_Path, QString()).toString();
|
r.play_audio.path = s.value(Key_Audio_Path, QString()).toString();
|
||||||
r.script_on_break_finish = s.value(Key_Script, QString()).toString();
|
r.script_on_break_finish = s.value(Key_Script, QString()).toString();
|
||||||
|
r.idle_timeout = s.value(Key_Idle_Timeout, Default_Idle_Timeout).toInt();
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,9 @@ const bool Default_Verbose = false;
|
|||||||
// Default autostart
|
// Default autostart
|
||||||
const bool Default_Autostart = true;
|
const bool Default_Autostart = true;
|
||||||
|
|
||||||
|
// Default idle timeout
|
||||||
|
const int Default_Idle_Timeout = 0;
|
||||||
|
|
||||||
const QString Default_Monitor = "";
|
const QString Default_Monitor = "";
|
||||||
const QString Primary_Monitor = "[Primary]";
|
const QString Primary_Monitor = "[Primary]";
|
||||||
|
|
||||||
@ -67,6 +70,9 @@ public:
|
|||||||
// This value can be path to audio file or empty or [embedded] string
|
// This value can be path to audio file or empty or [embedded] string
|
||||||
selected_audio play_audio;
|
selected_audio play_audio;
|
||||||
QString script_on_break_finish;
|
QString script_on_break_finish;
|
||||||
|
|
||||||
|
// Zero means "idle is not tracked"
|
||||||
|
int idle_timeout = Default_Idle_Timeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void save(const config& cfg);
|
static void save(const config& cfg);
|
||||||
|
|||||||
@ -69,6 +69,9 @@ void SettingsDialog::init()
|
|||||||
ui->mScriptEdit->setText(c.script_on_break_finish);
|
ui->mScriptEdit->setText(c.script_on_break_finish);
|
||||||
connect(ui->mAudioComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onAudioIndexChanged(int)));
|
connect(ui->mAudioComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onAudioIndexChanged(int)));
|
||||||
|
|
||||||
|
// Idle timeout
|
||||||
|
ui->mIdleTimeoutSpinbox->setValue(c.idle_timeout);
|
||||||
|
|
||||||
mSkipAudioChangeEvent = false;
|
mSkipAudioChangeEvent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +90,7 @@ void SettingsDialog::accept()
|
|||||||
if (c.play_audio.name == Audio_Custom)
|
if (c.play_audio.name == Audio_Custom)
|
||||||
c.play_audio.path = mCustomAudioPath;
|
c.play_audio.path = mCustomAudioPath;
|
||||||
|
|
||||||
|
c.idle_timeout = ui->mIdleTimeoutSpinbox->value();
|
||||||
app_settings::save(c);
|
app_settings::save(c);
|
||||||
|
|
||||||
emit accepted();
|
emit accepted();
|
||||||
|
|||||||
@ -138,6 +138,20 @@
|
|||||||
<item row="9" column="1">
|
<item row="9" column="1">
|
||||||
<widget class="QLineEdit" name="mScriptEdit"/>
|
<widget class="QLineEdit" name="mScriptEdit"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="10" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>Idle timeout in minutes (zero means no idle tracking)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="1">
|
||||||
|
<widget class="QSpinBox" name="mIdleTimeoutSpinbox">
|
||||||
|
<property name="suffix">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user