Compare commits

...

1 Commits

Author SHA1 Message Date
0f258561e5 Use Unix sockets instead of GlobalShortcuts portal 2024-07-22 14:41:43 -06:00
12 changed files with 256 additions and 450 deletions

View File

@ -2,7 +2,7 @@
SRC_DIRS := src
INC_DIRS := inc
LIBS := glew glfw3 freetype2 sdbus-c++
LIBS := glew glfw3 freetype2
# Outputs
@ -37,7 +37,7 @@ CXXFLAGS := -std=c++17 $(OPT) -Wall -Wextra -Wpedantic
# Linker flags
LDFLAGS := $(foreach lib, $(LIBS), $(shell pkg-config --libs-only-L $(lib)))
LDLIBS := $(foreach lib, $(LIBS), $(shell pkg-config --libs-only-l $(lib)))
LDLIBS := $(foreach lib, $(LIBS), $(shell pkg-config --libs-only-l $(lib))) -largParser
# Dependency files generated by the compiler

View File

@ -1,3 +1,5 @@
#pragma once
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <unordered_map>

View File

@ -1,3 +1,5 @@
#pragma once
#include <filesystem>
#include <cstdint>
#include <string>

16
inc/ipc.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "timer.h"
namespace IPC {
void listen_timer(bool* running, Timer* timer, StopWatch* stopwatch);
void addTime(int64_t minutes);
void clearTimer();
void startStopwatch();
void pauseResumeStopwatch();
void clearStopwatch();
void stop();
}

View File

@ -1,3 +1,5 @@
#pragma once
#include <GL/glew.h>

View File

@ -1,2 +1,4 @@
#pragma once
extern const char* const shader_text_frag;
extern const char* const shader_text_vert;

View File

@ -1,40 +0,0 @@
#include <sdbus-c++/sdbus-c++.h>
#include <memory>
#include <string>
#include <vector>
#include <map>
typedef sdbus::Struct<std::string, std::map<std::string, sdbus::Variant>> dbus_shortcut_t;
typedef void(*shortcut_callback_t)(void*);
// typedef std::function<void()> shortcut_callback_t;
struct ShortcutCallback {
void* userData;
shortcut_callback_t callback;
};
class GlobalShortcuts {
public:
GlobalShortcuts() = delete;
GlobalShortcuts(const char* const tokenPrefix);
void addShortcut(const std::string& id, const std::string& description, const std::string& trigger, shortcut_callback_t callback, void* userData);
int createSession();
int bindKeys();
bool alreadyBound();
std::vector<dbus_shortcut_t> listBinds();
void listen();
private:
std::string AddNumToToken();
private:
sdbus::ObjectPath m_SessionPath;
std::string m_TokenPrefix;
std::string m_ConnName;
std::string m_Sender;
std::unique_ptr<sdbus::IProxy> m_xdgProxy;
std::vector<dbus_shortcut_t> m_Shortcuts;
std::map<std::string, ShortcutCallback> m_Callbacks;
};

View File

@ -1,3 +1,5 @@
#pragma once
#include <cstdint>
#include <chrono>
@ -35,6 +37,7 @@ public:
StopWatch();
TimeDuration GetTime();
void Start();
void PauseResume();
void Pause();
void Resume();
void Clear();

173
src/ipc.cpp Normal file
View File

@ -0,0 +1,173 @@
#include <sys/socket.h>
#include <filesystem>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>
#include "timer.h"
#include "ipc.h"
enum COMMAND {
ADD_TIME = 1,
CLEAR_TIMER = 2,
START_STOPWATCH = 3,
PAUSE_RESUME_STOPWATCH = 4,
CLEAR_STOPWATCH = 5,
STOP = 6,
};
namespace IPC {
std::filesystem::path getSockDir()
{
std::filesystem::path sock_dir = getenv("XDG_RUNTIME_DIR");
if (sock_dir.empty()) {
sock_dir = "/tmp";
}
sock_dir /= "timer_overlay";
std::filesystem::create_directories(sock_dir);
return sock_dir;
}
void listen_timer(bool* running, Timer* timer, StopWatch* stopwatch)
{
std::filesystem::path sock_path = getSockDir() / "timer.sock";
unlink(sock_path.c_str());
int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sock == -1) {
// std::cout << "Failed to create socket" << std::endl;
return;
}
sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock_path.c_str(), sizeof(addr.sun_path) - 1);
if (bind(sock, (sockaddr*) &addr, sizeof(addr)) == -1) {
// std::cout << "Failed to bind" << std::endl;
close(sock);
return;
}
char buf[100];
while (*running) {
ssize_t len = recv(sock, buf, 100, 0);
// std::cout << "New packet" << std::endl;
if (len < 1) {
// std::cout << "Bad packet" << std::endl;
continue;
}
switch (buf[0]) {
case ADD_TIME:
// std::cout << "Add time packet: " << *(int64_t*) &buf[1] << " minutes" << std::endl;
if (len != 9) break;
timer->AddMinutes(*(int64_t*) &buf[1]);
break;
case CLEAR_TIMER:
// std::cout << "Clear timer packet" << std::endl;
timer->Clear();
break;
case START_STOPWATCH:
// std::cout << "Start stopwatch packet" << std::endl;
stopwatch->Start();
break;
case PAUSE_RESUME_STOPWATCH:
// std::cout << "Pause/Resume stopwatch packet" << std::endl;
stopwatch->PauseResume();
break;
case CLEAR_STOPWATCH:
// std::cout << "Clear stopwatch packet" << std::endl;
stopwatch->Clear();
break;
case STOP:
// std::cout << "Stop packet" << std::endl;
*running = false;
break;
default:
// std::cout << "Unknown packet" << std::endl;
break;
}
}
close(sock);
unlink(sock_path.c_str());
}
void sendPacket(char* buf, size_t size)
{
std::filesystem::path sock_path = getSockDir() / "timer.sock";
int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sock == -1) {
return;
}
sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock_path.c_str(), sizeof(addr.sun_path) - 1);
if (connect(sock, (sockaddr*) &addr, sizeof(addr)) == -1) {
close(sock);
return;
}
send(sock, buf, size, 0);
close(sock);
}
void addTime(int64_t minutes)
{
// std::cout << "Sending add time packet" << std::endl;
char buf[9] = { ADD_TIME };
memcpy(&buf[1], &minutes, sizeof(minutes));
sendPacket(buf, 9);
}
void clearTimer()
{
// std::cout << "Sending clear timer packet" << std::endl;
char buf[1] = { CLEAR_TIMER };
sendPacket(buf, 1);
}
void startStopwatch()
{
// std::cout << "Sending start stopwatch packet" << std::endl;
char buf[1] = { START_STOPWATCH };
sendPacket(buf, 1);
}
void pauseResumeStopwatch()
{
// std::cout << "Sending pause/resume stopwatch packet" << std::endl;
char buf[1] = { PAUSE_RESUME_STOPWATCH };
sendPacket(buf, 1);
}
void clearStopwatch()
{
// std::cout << "Sending clear stopwatch packet" << std::endl;
char buf[1] = { CLEAR_STOPWATCH };
sendPacket(buf, 1);
}
void stop()
{
// std::cout << "Sending stop packet" << std::endl;
char buf[1] = { STOP };
sendPacket(buf, 1);
}
} // namespace IPC

View File

@ -7,7 +7,8 @@
#include <glm/common.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <wayland-client-protocol.h>
#include <thread>
#include <argparser.h>
#include <unistd.h>
#include <iostream>
@ -16,17 +17,48 @@
#include "shader_utils.h"
#include "shadersrc.h"
#include "character_utils.h"
#include "shortcuts.h"
#include "timer.h"
#include "config.h"
#include "timer.h"
#include "ipc.h"
int daemon();
void error_callback(int error, const char* description)
{
std::cerr << "Error " << error << ": " << description << std::endl;
}
int main()
int main(int argc, char* argv[])
{
Arguments::Bool start_daemon("daemon");
Arguments::Int add_time("add", 0);
Arguments::Bool clear_timer("clear-timer");
Arguments::Bool start_stopwatch("start-stopwatch");
Arguments::Bool pause("pause");
Arguments::Bool clear_stopwatch("clear-stopwatch");
Arguments::Bool stop("stop");
Arguments::parse(argc, argv);
if (start_daemon.found) {
return daemon();
} else if (add_time.found) {
IPC::addTime(add_time.value);
} else if (clear_timer.found) {
IPC::clearTimer();
} else if (start_stopwatch.found) {
IPC::startStopwatch();
} else if (pause.found) {
IPC::pauseResumeStopwatch();
} else if (clear_stopwatch.found) {
IPC::clearStopwatch();
} else if (stop.found) {
IPC::stop();
}
}
int daemon()
{
Config cfg = readConfig(getConfigPath("timer_overlay"));
@ -106,60 +138,14 @@ int main()
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
bool running = true;
Timer timer;
StopWatch stopwatch;
GlobalShortcuts shortcuts("timer_overlay");
if (shortcuts.createSession() != 0) {
std::cout << "Failed to create shortcuts session" << std::endl;
glfwTerminate();
}
shortcuts.addShortcut("time1", "Adds 1 minute to the timer", "ALT+F5", [](void* timer_ptr) {
((Timer*)timer_ptr)->AddMinutes(1);
}, &timer);
shortcuts.addShortcut("time5", "Adds 5 minutes to the timer", "ALT+F6", [](void* timer_ptr) {
((Timer*)timer_ptr)->AddMinutes(5);
}, &timer);
shortcuts.addShortcut("time15", "Adds 15 minutes to the timer", "ALT+F7", [](void* timer_ptr) {
((Timer*)timer_ptr)->AddMinutes(15);
}, &timer);
shortcuts.addShortcut("time60", "Adds 1 hour to the timer", "ALT+F8", [](void* timer_ptr) {
((Timer*)timer_ptr)->AddMinutes(60);
}, &timer);
shortcuts.addShortcut("timeclear", "Clears the timer", "ALT+F9", [](void* timer_ptr) {
((Timer*)timer_ptr)->Clear();
}, &timer);
shortcuts.addShortcut("swstart", "Starts the stopwatch", "CTRL+F5", [](void* stopwatch_ptr) {
((StopWatch*)stopwatch_ptr)->Start();
}, &stopwatch);
shortcuts.addShortcut("swclear", "Clears the stopwatch", "CTRL+F6", [](void* stopwatch_ptr) {
((StopWatch*)stopwatch_ptr)->Clear();
}, &stopwatch);
shortcuts.addShortcut("swpause", "Pauses the stopwatch", "CTRL+F7", [](void* stopwatch_ptr) {
((StopWatch*)stopwatch_ptr)->Pause();
}, &stopwatch);
shortcuts.addShortcut("swresume", "Resumes the stopwatch", "CTRL+F8", [](void* stopwatch_ptr) {
((StopWatch*)stopwatch_ptr)->Resume();
}, &stopwatch);
if (!shortcuts.alreadyBound()) {
std::cout << "Requsting to bind keys" << std::endl;
if (shortcuts.bindKeys() != 0) {
std::cerr << "Failed to bind keys" << std::endl;
return -1;
}
}
shortcuts.listen();
std::thread ipc_thread(IPC::listen_timer, &running, &timer, &stopwatch);
int64_t last_frame_seconds = LONG_MAX;
while (!glfwWindowShouldClose(window)) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GLFW_TRUE);
}
while (running) {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
@ -189,5 +175,8 @@ int main()
glfwPollEvents();
}
ipc_thread.join();
glfwTerminate();
return 0;
}

View File

@ -1,354 +0,0 @@
#include <cstdlib>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/sdbus-c++.h>
#include <iostream>
#include <cstdint>
#include <memory>
#include <string>
#include <unistd.h>
#include <utility>
#include <random>
#include <vector>
#include <map>
#include "shortcuts.h"
const char* xdgName = "org.freedesktop.portal.Desktop";
const char* xdgPath = "/org/freedesktop/portal/desktop";
const char* shortcutsInterface = "org.freedesktop.portal.GlobalShortcuts";
const char* requstInterface = "org.freedesktop.portal.Request";
GlobalShortcuts::GlobalShortcuts(const char* const tokenPrefix)
: m_TokenPrefix(tokenPrefix), m_Shortcuts() { };
std::string GlobalShortcuts::AddNumToToken()
{
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> dist(1000,9999);
return m_TokenPrefix + std::to_string(dist(rng));
}
void GlobalShortcuts::addShortcut(const std::string& id, const std::string& description, const std::string& trigger, shortcut_callback_t callback, void* userData)
{
std::map<std::string, sdbus::Variant> smap;
smap["description"] = sdbus::Variant(description);
smap["preferred_trigger"] = sdbus::Variant(trigger);
m_Shortcuts.emplace_back(id, smap);
m_Callbacks[id] = ShortcutCallback{ userData, callback };
}
int GlobalShortcuts::createSession()
{
bool requestInProgress = false;
int result = 1;
auto conn = sdbus::createSessionBusConnection();
m_ConnName = conn->getUniqueName();
m_Sender = m_ConnName.substr(1);
m_xdgProxy = sdbus::createProxy(std::move(conn), xdgName, xdgPath);
std::map<std::string, sdbus::Variant> create_session_args;
std::string token = AddNumToToken();
create_session_args["handle_token"] = sdbus::Variant(token);
create_session_args["session_handle_token"] = sdbus::Variant(m_TokenPrefix);
for (size_t i = 1; i < m_Sender.length(); i++) {
if (m_Sender[i] == '.') {
m_Sender[i] = '_';
}
}
std::string expectedRequestPath = "/org/freedesktop/portal/desktop/request/" + m_Sender + "/" + token;
auto requestProxy = sdbus::createProxy(m_xdgProxy->getConnection(), xdgName, expectedRequestPath);
requestProxy->uponSignal("Response").onInterface(requstInterface)
.call([this, &requestInProgress, &result](uint32_t result_code, std::map<std::string, sdbus::Variant> res_map)
{
if (result_code == 0) {
this->m_SessionPath = res_map.at("session_handle").get<std::string>();
} else {
std::cerr << "Failed to create GlobalShortcuts session" << std::endl;
}
result = result_code;
requestInProgress = false;
});
requestProxy->finishRegistration();
sdbus::ObjectPath requestPath;
requestInProgress = true;
m_xdgProxy->callMethod("CreateSession").onInterface(shortcutsInterface)
.withArguments(create_session_args).storeResultsTo(requestPath);
std::unique_ptr<sdbus::IProxy> requestProxy2;
if (requestPath != expectedRequestPath) {
requestProxy->unregister();
requestProxy2 = sdbus::createProxy(m_xdgProxy->getConnection(), xdgName, requestPath);
requestProxy2->uponSignal("Response").onInterface(requstInterface)
.call([this, &requestInProgress, &result](uint32_t result_code, std::map<std::string, sdbus::Variant> res_map)
{
if (result_code == 0) {
this->m_SessionPath = res_map.at("session_handle").get<sdbus::ObjectPath>();
} else {
std::cerr << "Failed to create GlobalShortcuts session" << std::endl;
}
result = result_code;
requestInProgress = false;
});
requestProxy2->finishRegistration();
}
while (requestInProgress) {
usleep(500);
}
return result;
}
bool GlobalShortcuts::alreadyBound()
{
std::vector<dbus_shortcut_t> binds = listBinds();
if (binds.size() != m_Shortcuts.size()) {
return false;
}
for (dbus_shortcut_t shortcut: m_Shortcuts) {
const std::string shortcut_id = shortcut.get<0>();
bool match = false;
for (dbus_shortcut_t bind: binds) {
const std::string bind_id = bind.get<0>();
if (shortcut_id == bind_id) {
match = true;
break;
}
}
if (!match) {
return false;
}
}
return true;
}
std::vector<dbus_shortcut_t> GlobalShortcuts::listBinds()
{
std::vector<dbus_shortcut_t> binds;
bool requestInProgress = false;
std::map<std::string, sdbus::Variant> list_shortcuts_args;
const std::string token = AddNumToToken();
list_shortcuts_args["handle_token"] = sdbus::Variant(token);
sdbus::ObjectPath expectedRequestPath = "/org/freedesktop/portal/desktop/request/" + m_Sender + "/" + token;
auto requestProxy = sdbus::createProxy(m_xdgProxy->getConnection(), xdgName, expectedRequestPath);
requestProxy->uponSignal("Response").onInterface(requstInterface)
.call([&requestInProgress, &binds](uint32_t result_code, std::map<std::string, sdbus::Variant> res_map)
{
if (result_code == 0) {
binds = res_map.at("shortcuts").get<std::vector<dbus_shortcut_t>>();
// for (dbus_shortcut_t bind: binds) {
// std::cout << "id: " << bind.get<0>();
// std::cout << ", desc: " << bind.get<1>().at("description").get<std::string>();
// std::cout << ", trigger: " << bind.get<1>().at("trigger_description").get<std::string>();
// std::cout << std::endl << std::endl;
// }
} else {
std::cerr << "Failed to list shortcuts" << std::endl;
}
requestInProgress = false;
});
requestProxy->finishRegistration();
sdbus::ObjectPath requestPath;
requestInProgress = true;
m_xdgProxy->callMethod("ListShortcuts").onInterface(shortcutsInterface)
.withArguments(m_SessionPath, list_shortcuts_args).storeResultsTo(requestPath);
std::unique_ptr<sdbus::IProxy> requestProxy2;
if (requestPath != expectedRequestPath) {
requestProxy->unregister();
requestProxy2 = sdbus::createProxy(m_xdgProxy->getConnection(), xdgName, requestPath);
requestProxy2->uponSignal("Response").onInterface(requstInterface)
.call([&requestInProgress, &binds](uint32_t result_code, std::map<std::string, sdbus::Variant> res_map)
{
if (result_code == 0) {
binds = res_map.at("shortcuts").get<std::vector<dbus_shortcut_t>>();
} else {
std::cerr << "Failed to list shortcuts" << std::endl;
}
requestInProgress = false;
});
requestProxy2->finishRegistration();
}
while (requestInProgress) {
usleep(500);
}
return binds;
}
int GlobalShortcuts::bindKeys()
{
bool requestInProgress = false;
int result = 1;
// std::cout << "session: " << this->sessionPath.c_str() << std::endl;
std::map<std::string, sdbus::Variant> bind_shortcuts_args;
const std::string token = AddNumToToken();
bind_shortcuts_args["handle_token"] = sdbus::Variant(token);
sdbus::ObjectPath expectedRequestPath = "/org/freedesktop/portal/desktop/request/" + m_Sender + "/" + token;
auto requestProxy = sdbus::createProxy(m_xdgProxy->getConnection(), xdgName, expectedRequestPath);
requestProxy->uponSignal("Response").onInterface(requstInterface)
.call([&requestInProgress, &result](uint32_t result_code, std::map<std::string, sdbus::Variant> res_map){
(void) res_map;
if (result_code == 0) {
// auto binds = res_map.at("shortcuts").get<std::vector<dbus_shortcut_t>>();
// for (auto bind: binds) {
// std::cout << "id: " << bind.get<0>();
// std::cout << ", desc: " << bind.get<1>().at("description").get<std::string>();
// std::cout << ", trigger: " << bind.get<1>().at("trigger_description").get<std::string>();
// std::cout << std::endl;
// }
} else {
std::cerr << "Failed to bind shortcuts" << std::endl;
}
result = result_code;
requestInProgress = false;
});
requestProxy->finishRegistration();
sdbus::ObjectPath requestPath;
requestInProgress = true;
m_xdgProxy->callMethod("BindShortcuts").onInterface(shortcutsInterface)
.withArguments(m_SessionPath, m_Shortcuts, "", bind_shortcuts_args).storeResultsTo(requestPath);
std::unique_ptr<sdbus::IProxy> requestProxy2;
if (requestPath != expectedRequestPath) {
requestProxy->unregister();
requestProxy2 = sdbus::createProxy(m_xdgProxy->getConnection(), xdgName, requestPath);
requestProxy2->uponSignal("Response").onInterface(requstInterface)
.call([&requestInProgress, &result](uint32_t result_code, std::map<std::string, sdbus::Variant> res_map){
(void) res_map;
if (result_code == 0) {
std::cerr << "Failed to bind shortcuts" << std::endl;
}
result = result_code;
requestInProgress = false;
});
requestProxy2->finishRegistration();
}
while (requestInProgress) {
usleep(500);
}
return result;
}
void GlobalShortcuts::listen()
{
m_xdgProxy->uponSignal("Activated").onInterface(shortcutsInterface)
.call([this](sdbus::ObjectPath session_handle, const std::string& shortcut_id, uint64_t timestamp, std::map<std::string, sdbus::Variant> options)
{
(void) options;
(void) session_handle;
(void) timestamp;
// std::cout << "Shortcut activated!" << std::endl;
// std::cout << "session: " << session_handle << ", id: " << shortcut_id << ", time: " << timestamp << std::endl << std::endl;
ShortcutCallback cb = this->m_Callbacks[shortcut_id];
cb.callback(cb.userData);
});
m_xdgProxy->finishRegistration();
}
// Very basic example
//int main()
//{
// GlobalShortcuts shortcuts;
//
// if (shortcuts.createSession() != 0) {
// std::cout << "Failed to create shortcuts session" << std::endl;
// return -1;
// }
//
// shortcuts.addShortcut("test1", "Prints things", "CTRL+SHIFT+a");
// shortcuts.addShortcut("test2", "Prints things, but like, different", "CTRL+SHIFT+b");
//
// if (shortcuts.listBinds().size() == 0) {
// std::cout << "Requsting to bind keys" << std::endl;
// shortcuts.bindKeys();
// }
//
// shortcuts.listen();
//
// while (true) {};
//}
//
// Code for working with libdbus directly. There isn't a lot of information around on how to use it
// And it is pretty difficult to use
//void connect()
//{
// DBusConnection* dbus_conn = nullptr;
// DBusError dbus_error;
//
// dbus_error_init(&dbus_error);
//
// dbus_conn = dbus_bus_get(DBUS_BUS_SESSION, &dbus_error);
// std::cout << "Connected to DBUS as \"" << dbus_bus_get_unique_name(dbus_conn) << "\"." << std::endl;
//
// // Request connection to GlobalShortcuts portal
// DBusMessage* conn_request_msg = dbus_message_new_method_call("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.GlobalShortcuts", "CreateSession");
// DBusMessage* reply = dbus_connection_send_with_reply_and_block(dbus_conn, conn_request_msg, DBUS_TIMEOUT_USE_DEFAULT, &dbus_error);
//
// const char* conn_request_path = nullptr;
// dbus_message_get_args(reply, &dbus_error, DBUS_TYPE_OBJECT_PATH, conn_request_path, DBUS_TYPE_INVALID);
//
// dbus_message_unref(reply);
// dbus_message_unref(conn_request_msg);
//
// std::string rule = "type='signal',path='";
// rule = rule + conn_request_path + '\'';
// dbus_bus_add_match(dbus_conn, rule.c_str(), &dbus_error);
// while (true) {
// dbus_connection_read_write(dbus_conn, DBUS_TIMEOUT_USE_DEFAULT);
// DBusMessage* msg_in = dbus_connection_pop_message(dbus_conn);
// if (msg_in == nullptr) {
// continue;
// }
//
// if (dbus_message_is_signal(msg_in, "org.freedesktop.portal.Request", "Response")) {
// uint32_t result;
//
// dbus_message_get_args(msg_in, &dbus_error, DBUS_TYPE_UINT32, &result, DBUS_TYPE_ARRAY, , DBUS_TYPE_INVALID);
//
// std::cout << "Global shortcut session creation result: " << result << std::endl;
// }
//
// dbus_message_unref(msg_in);
// }
//
// dbus_connection_unref(dbus_conn);
//}

View File

@ -90,6 +90,17 @@ void StopWatch::Start()
}
}
void StopWatch::PauseResume()
{
if (paused) {
start_time = std::chrono::system_clock::now() - paused_duration;
paused = false;
} else {
paused_duration = std::chrono::system_clock::now() - start_time;
paused = true;
}
}
void StopWatch::Pause()
{
if (!paused) {