#include #include #include #include #include #include #include "seqmatcher.h" #include "got.h" #include "gui.h" #include "xutil.h" namespace Config { constexpr const char *const TRIGGER_KEY = ""; constexpr const char *const TRIGGER_KEY_DESCR = "F18 (XF86Launch9)"; constexpr const KeySym TRIGGER_KEY_X = XKB_KEY_XF86Launch9; const std::string USAGE_TEXT = std::string{} + " " + TRIGGER_KEY + " H -- Get brief help in a notification\n" + " " + TRIGGER_KEY + " I -- Check into a sheet\n" + " " + TRIGGER_KEY + " E -- Edit the note for the currently running activity\n" + " " + TRIGGER_KEY + " O -- Check out of the current activity\n" + " " + TRIGGER_KEY + " -- Switch and check into the 'misc' sheet\n" + " " + TRIGGER_KEY + " " + TRIGGER_KEY + " -- Show notification with current status\n" + " " + TRIGGER_KEY + " Q -- Quit this program (killing it also works of course)\n"; const std::string USAGE_SUMMARY = std::string{TRIGGER_KEY} + " + (H)elp (I)ncheck (O)utcheck (E)dit (Q)uit; " + TRIGGER_KEY + " " + TRIGGER_KEY + " = status; " + TRIGGER_KEY + " = into misc"; } int main(int argc, char **argv) { if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) { std::cout << "GUI for GOT (https://github.com/lieuwex/got), which is a rewrite of Timetrap.\n" "This thing runs as a daemon; it gets activated with the " << Config::TRIGGER_KEY_DESCR << " key on\n" "your keyboard. The following sequences trigger actions:\n" << Config::USAGE_TEXT << std::flush; return 0; } if (!std::filesystem::exists(got::getDBpath())) { std::cerr << "The GOT database (" << got::getDBpath() << ") doesn't exist!" << std::endl; std::cerr << "Please run 'got' at least once before using this tool." << std::endl; return 1; } auto dpy_pair = x::XOpenDisplayRAII(nullptr); Display *dpy = dpy_pair.first; bool quitRequested = false; SeqMatcher matcher{dpy}; matcher.addSequence({XK_H}, []() { gui::showNotification(Config::USAGE_SUMMARY); }); matcher.addSequence({XK_E}, []() { if (got::getRunning()) { if (auto descr = gui::promptText("Edit currently running entry's text:")) { got::editRunning(*descr); } } else { gui::showNotification("Cannot edit if not checked in"); } }); matcher.addSequence({XK_I}, []() { std::vector sheets = got::getSheets(); auto choice = gui::chooseList("Check in", "Sheet", sheets); if (choice) { auto current = got::getRunning(); if (current) { got::checkOut(); gui::showNotification("Checked out of sheet '" + current->first + "'"); } got::checkIn(*choice); gui::showNotification("Checked in to sheet '" + *choice + "'"); if (auto descr = gui::promptText("Set text for this '" + *choice + "' entry:")) { got::editRunning(*descr); } } }); matcher.addSequence({XK_O}, []() { auto current = got::getRunning(); if (current) { got::checkOut(); gui::showNotification("Checked out of sheet '" + current->first + "'"); } else { gui::showNotification("Cannot check out if not checked in"); } }); matcher.addSequence({XK_space}, []() { if (got::getRunning()) got::checkOut(); got::checkIn("misc"); gui::showNotification("Switched to 'misc'"); if (auto descr = gui::promptText("Set text for this 'misc' entry:")) { got::editRunning(*descr); } }); matcher.addSequence({Config::TRIGGER_KEY_X}, []() { auto current = got::getRunning(); if (!current) { gui::showNotification("Currently checked out"); } else { gui::showNotification( "Checked into '" + current->first + "'" + (current->second.empty() ? "" : " (" + current->second + ")") ); } }); matcher.addSequence({XK_Q}, [&quitRequested]() { gui::showNotification("GOT GUI is quitting"); quitRequested = true; }); x::globalKeyWatch( dpy, Config::TRIGGER_KEY_X, [dpy, &matcher, &quitRequested](const XKeyEvent&) -> bool { matcher.reset(); SeqMatcher::Callback cb; x::globalKeyboardGrab(dpy, [&matcher, &cb](const XKeyEvent &ev) -> bool { auto opt_cb = matcher.observe(ev); if (opt_cb) { cb = *opt_cb; return true; } else { return false; } }); if (cb) cb(); return quitRequested; } ); }