Skip to content

Commit

Permalink
pause when specific programs are running
Browse files Browse the repository at this point in the history
Closes #9
  • Loading branch information
AllanChain committed Nov 28, 2024
1 parent cd97abc commit c5549fe
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 12 deletions.
15 changes: 14 additions & 1 deletion src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "idle-time.h"
#include "pref-window.h"
#include "preferences.h"
#include "program-monitor.h"

SaneBreakApp::SaneBreakApp() : QObject() {
prefWindow = new PreferenceWindow();
Expand All @@ -36,6 +37,7 @@ SaneBreakApp::SaneBreakApp() : QObject() {
oneshotIdleTimer->setMinIdleTime(1000);
sleepMonitor = new SleepMonitor();
batteryWatcher = BatteryStatus::createWatcher();
runningProgramsMonitor = new RunningProgramsMonitor();
createMenu();
icon = new QSystemTrayIcon(this);
icon->setContextMenu(menu);
Expand All @@ -51,11 +53,20 @@ SaneBreakApp::SaneBreakApp() : QObject() {
connect(sleepMonitor, &SleepMonitor::sleepEnd, this, &SaneBreakApp::onSleepEnd);
connect(batteryWatcher, &BatteryStatus::onBattery, this, &SaneBreakApp::onBattery);
connect(batteryWatcher, &BatteryStatus::onPower, this, &SaneBreakApp::onPower);
connect(runningProgramsMonitor, &RunningProgramsMonitor::programStarted, this,
[this]() { pauseBreak(PauseReason::APP_OPEN); });
connect(runningProgramsMonitor, &RunningProgramsMonitor::programStopped, this,
[this]() { resumeBreak(PauseReason::APP_OPEN); });
connect(SanePreferences::pauseOnBattery, &SettingWithSignal::changed, this,
&SaneBreakApp::onBatterySettingChange);
connect(SanePreferences::smallEvery, &SettingWithSignal::changed, this,
&SaneBreakApp::resetSecondsToNextBreak);
connect(
SanePreferences::programsToMonitor, &SettingWithSignal::changed, this, [this]() {
runningProgramsMonitor->setPrograms(SanePreferences::programsToMonitor->get());
});
}

SaneBreakApp::~SaneBreakApp() {}

void SaneBreakApp::start() {
Expand All @@ -64,6 +75,8 @@ void SaneBreakApp::start() {
countDownTimer->start();
idleTimer->startWatching();
batteryWatcher->startWatching();
runningProgramsMonitor->setPrograms(SanePreferences::programsToMonitor->get());
runningProgramsMonitor->startMonitoring();
}

void SaneBreakApp::tick() {
Expand Down Expand Up @@ -251,7 +264,7 @@ void SaneBreakApp::onSleepEnd() {
breakCycleCount = 1;
breakManager->close(); // stop current break if necessary
resetSecondsToNextBreak();
// Bue we update menu and icon (in case <1min) only if not paused
// But we update menu and icon (in case <1min) only if not paused
if (pauseReasons == 0) {
updateMenu();
updateIcon();
Expand Down
2 changes: 2 additions & 0 deletions src/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "battery-status.h"
#include "idle-time.h"
#include "pref-window.h"
#include "program-monitor.h"
#include "window-manager.h"

enum PauseReason {
Expand Down Expand Up @@ -68,6 +69,7 @@ class SaneBreakApp : public QObject {
void resetSecondsToNextBreak();
void updateIcon();
uint pauseReasons = 0;
RunningProgramsMonitor *runningProgramsMonitor;
};

#endif // SANE_TRAY_H
2 changes: 2 additions & 0 deletions src/pref-window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ void PreferenceWindow::loadSettings() {
ui->resetBreakSlider->setValue(SanePreferences::resetAfterPause->get() / 60);
ui->resetCycleSlider->setValue(SanePreferences::resetCycleAfterPause->get() / 60);
ui->pauseOnBatteryCheck->setChecked(SanePreferences::pauseOnBattery->get());
ui->programList->setPlainText(SanePreferences::programsToMonitor->get().join("\n"));
}

void PreferenceWindow::saveSettings() {
Expand All @@ -155,6 +156,7 @@ void PreferenceWindow::saveSettings() {
SanePreferences::resetAfterPause->set(ui->resetBreakSlider->value() * 60);
SanePreferences::resetCycleAfterPause->set(ui->resetCycleSlider->value() * 60);
SanePreferences::pauseOnBattery->set(ui->pauseOnBatteryCheck->isChecked());
SanePreferences::programsToMonitor->set(ui->programList->toPlainText().split("\n"));
}

void PreferenceWindow::closeEvent(QCloseEvent *event) {
Expand Down
23 changes: 12 additions & 11 deletions src/pref-window.ui
Original file line number Diff line number Diff line change
Expand Up @@ -714,17 +714,18 @@ QPushButton[flat=&quot;true&quot;]:checked {
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Pause breaks when any of the following program is running:</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="programList">
<property name="placeholderText">
<string>(Write one program name per line)</string>
</property>
</spacer>
</widget>
</item>
</layout>
</widget>
Expand Down Expand Up @@ -803,8 +804,8 @@ QPushButton[flat=&quot;true&quot;]:checked {
<rect>
<x>0</x>
<y>0</y>
<width>66</width>
<height>41</height>
<width>456</width>
<height>154</height>
</rect>
</property>
<property name="sizePolicy">
Expand Down
2 changes: 2 additions & 0 deletions src/preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ Setting<int>* SanePreferences::resetCycleAfterPause =
new Setting<int>("pause/reset-cycle-after", 300);
Setting<bool>* SanePreferences::pauseOnBattery =
new Setting<bool>("pause/on-battery", false);
Setting<QStringList>* SanePreferences::programsToMonitor =
new Setting<QStringList>("pause/programs-to-monitor", QStringList());
1 change: 1 addition & 0 deletions src/preferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class SanePreferences : public QObject {
static Setting<int> *resetAfterPause;
static Setting<int> *resetCycleAfterPause;
static Setting<bool> *pauseOnBattery;
static Setting<QStringList> *programsToMonitor;
};

#endif // SANE_PREFERENCES_H
71 changes: 71 additions & 0 deletions src/program-monitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Sane Break is a polite and sane break reminder preventing mindless skips.
// Copyright (C) 2024 Allan Chain
// SPDX-License-Identifier: GPL-3.0-or-later

#include "program-monitor.h"

#include <qglobal.h>

#include <QProcess>
#include <utility>

RunningProgramsMonitor::RunningProgramsMonitor(QObject *parent)
: QObject(parent), monitorTimer(new QTimer(this)) {
connect(monitorTimer, &QTimer::timeout, this, &RunningProgramsMonitor::tick);
}

void RunningProgramsMonitor::startMonitoring() { monitorTimer->start(5000); }
void RunningProgramsMonitor::stopMonitoring() { monitorTimer->stop(); }

void RunningProgramsMonitor::setPrograms(const QStringList &programs) {
programsToMonitor = programs.filter(validProgramFilter);
if (programsToMonitor.isEmpty() && previouslySeen) {
// Clean up: if all programs are removed from the list, stop pausing
emit programStopped();
}
tick();
}

void RunningProgramsMonitor::tick() {
if (programsToMonitor.isEmpty()) return;
QStringList runningPrograms;

QProcess process;
#ifdef Q_OS_LINUX
process.start("ps", QStringList() << "-e" << "-o" << "comm=");
#elif defined Q_OS_MACOS
process.start("ps", QStringList() << "-ax" << "-o" << "comm=");
#elif defined Q_OS_WIN
process.start("tasklist", QStringList() << "/fo" << "csv" << "/nh");
#endif

process.waitForFinished();
QString output = process.readAllStandardOutput();
if (output.isEmpty()) return;

#ifdef Q_OS_WIN
QStringList lines = output.split('\n', Qt::SkipEmptyParts);
for (const QString &line : lines) {
QStringList parts = line.split(',', Qt::SkipEmptyParts);
if (parts.size() > 0) {
QString program = parts[0].trimmed();
runningPrograms.append(program);
}
}
#else
runningPrograms = output.split('\n', Qt::SkipEmptyParts);
#endif

bool currentlySeen = false;
for (const QString &program : std::as_const(runningPrograms)) {
for (const QString &entry : std::as_const(programsToMonitor)) {
if (program.contains(entry)) {
currentlySeen = true;
break;
}
}
}
if (!previouslySeen && currentlySeen) emit programStarted();
if (previouslySeen && !currentlySeen) emit programStopped();
previouslySeen = currentlySeen;
}
36 changes: 36 additions & 0 deletions src/program-monitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Sane Break is a polite and sane break reminder preventing mindless skips.
// Copyright (C) 2024 Allan Chain
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef RUNNING_PROGRAMS_MONITOR_H
#define RUNNING_PROGRAMS_MONITOR_H

#include <QObject>
#include <QRegularExpression>
#include <QStringList>
#include <QTimer>

class RunningProgramsMonitor : public QObject {
Q_OBJECT

public:
RunningProgramsMonitor(QObject *parent = nullptr);
void startMonitoring();
void stopMonitoring();
void setPrograms(const QStringList &programs);

signals:
void programStarted();
void programStopped();

private slots:
void tick();

private:
const QRegularExpression validProgramFilter = QRegularExpression(".");
QTimer *monitorTimer;
QStringList programsToMonitor;
bool previouslySeen = false;
};

#endif // RUNNING_PROGRAMS_MONITOR_H

0 comments on commit c5549fe

Please sign in to comment.