@ -0,0 +1 @@ | |||
build/* |
@ -0,0 +1,34 @@ | |||
CXX = clang++ | |||
CXXFLAGS = --std=c++17 -O3 -Iinclude | |||
USE_THREADS = -pthread | |||
USE_FILESYSTEM = -lc++fs | |||
USE_SDL = -lSDL2 | |||
all: build | |||
build: pomodoro unix | |||
unix: yes | |||
clean: clean_pomodoro | |||
dirs: | |||
@mkdir -p ./build/bin | |||
pomodoro: dirs | |||
$(CXX) $(CXXFLAGS) $(USE_SDL) $(USE_THREADS) src/pomodoro/pomodoro.cpp -o ./build/bin/pomodoro_view | |||
clean_pomodoro: | |||
-@rm ./build/bin/pomodoro_view | |||
yes: dirs | |||
$(CXX) $(CXXFLAGS) src/yes/yes.cpp -o ./build/bin/yes | |||
yes_clean: | |||
-@rm ./build/bin/yes | |||
astyle: | |||
astyle --style=bsd --align-reference=type --align-pointer=type --break-blocks --indent-namespaces --indent=tab --add-brackets \ | |||
include/rigid_paradise/*.h \ | |||
src/pomodoro/pomodoro.cpp \ | |||
src/yes/yes.cpp \ |
@ -0,0 +1,3 @@ | |||
pomodoro_sound ~/.rigidparadise/sound.wav | |||
pomodoro.tasks ~/.rigidparadise/pomodoro.tasks | |||
pomodoro.log ~/.rigidparadise/pomodoro.log |
@ -0,0 +1,47 @@ | |||
#pragma once | |||
#include <fstream> | |||
#include <iostream> | |||
#include <string> | |||
#include <cctype> | |||
#include <algorithm> | |||
namespace config | |||
{ | |||
inline std::string get_value(const std::string& source) | |||
{ | |||
std::string ret = source; | |||
try | |||
{ | |||
std::ifstream config_file("/home/archivist/.rigidparadise/rp.cfg"); | |||
if(!config_file.good()) | |||
{ | |||
std::cerr << "Unable to read configuration"<<std::endl; | |||
} | |||
while(config_file.good()) | |||
{ | |||
std::string str; | |||
getline (config_file, str); | |||
if(str.size()>1 && str[0] != '#') | |||
{ | |||
auto end = std::find_if(str.begin(), str.end(), isspace); | |||
size_t pos = end-str.begin(); | |||
std::string_view name{str.data(), pos}; | |||
if(name == source) | |||
{ | |||
ret = std::string{end+1, str.end()}; | |||
} | |||
} | |||
} | |||
} | |||
catch(...) | |||
{} | |||
return ret; | |||
} | |||
} |
@ -0,0 +1,68 @@ | |||
#pragma once | |||
#include <string> | |||
#include <sstream> | |||
#include <chrono> | |||
#include <iostream> | |||
namespace display | |||
{ | |||
inline std::string escape_csv(const std::string& value) | |||
{ | |||
std::stringstream ret; | |||
ret<<"\""; | |||
for(auto v : value) | |||
{ | |||
switch(v) | |||
{ | |||
case '\n': | |||
ret << "\\n"; | |||
break; | |||
case '\t': | |||
ret << "\\t"; | |||
break; | |||
case '"': | |||
ret << "\\\""; | |||
break; | |||
default: | |||
ret << v; | |||
break; | |||
} | |||
} | |||
ret<<"\""; | |||
return ret.str(); | |||
} | |||
inline std::string to_string(std::chrono::seconds v) | |||
{ | |||
std::stringstream ret; | |||
ret << v.count() << " second" << (v.count()!=1?"s":""); | |||
return ret.str(); | |||
} | |||
inline std::string to_string(std::chrono::minutes v) | |||
{ | |||
std::stringstream ret; | |||
ret << v.count() << " minute" << (v.count()!=1?"s":""); | |||
return ret.str(); | |||
} | |||
inline void header(std::string app_name) | |||
{ | |||
std::cout << "\033[2J" << "\033[1;1H"; | |||
std::cout << "Rigid Paradise's "<<app_name<<std::endl; | |||
} | |||
template<typename T> | |||
void pair_set(const T& value) | |||
{ | |||
for(auto action : value) | |||
{ | |||
std::cout << " [" << action.first << "]: " << action.second<<std::endl; | |||
} | |||
} | |||
} |
@ -0,0 +1,40 @@ | |||
#pragma once | |||
#include <utility> | |||
#include <set> | |||
#include <fstream> | |||
#include <rigid_paradise/config.h> | |||
namespace pomodoro | |||
{ | |||
using pomotask = std::pair<int, std::string>; | |||
std::set<pomotask> get_pomodoro_tasks() | |||
{ | |||
std::set<std::pair<int, std::string>> ret; | |||
std::ifstream tasks(config::get_value("pomodoro.tasks")); | |||
int counter = 1; | |||
while(tasks.good()) | |||
{ | |||
std::string str; | |||
getline (tasks, str); | |||
if(str.size()>1 && str[0] != '#') | |||
{ | |||
ret.insert(std::make_pair(counter++, str)); | |||
} | |||
} | |||
return ret; | |||
} | |||
} | |||
#define pomodoro_actions_unwrap {\ | |||
std::make_pair<char, std::string>('P',"Productive work"),\ | |||
std::make_pair<char, std::string>('B',"Parallel work"),\ | |||
std::make_pair<char, std::string>('C',"Non productive work"),\ | |||
std::make_pair<char, std::string>('D',"Productive discution or chat"),\ | |||
std::make_pair<char, std::string>('E',"Non productive chat"),\ | |||
std::make_pair<char, std::string>('T',"Learning"),\ | |||
std::make_pair<char, std::string>('S',"Improductive activity"),\ | |||
std::make_pair<char, std::string>('Z',"Break")\ | |||
} |
@ -0,0 +1,2 @@ | |||
#pragma once | |||
#include "rigid_paradise/systems/unix/stdio.h" |
@ -0,0 +1,31 @@ | |||
#pragma once | |||
#include <unistd.h> | |||
#include <fcntl.h> | |||
#include <stdint.h> | |||
namespace os{ | |||
constexpr auto write = [](int32_t fd, const char* ptr, int64_t size){ | |||
do | |||
{ | |||
auto check = ::write(fd, ptr, size); | |||
if(check<0) | |||
{ | |||
return check; | |||
} | |||
size-=check; | |||
ptr+=check; | |||
}while(size!=0); | |||
return size; | |||
}; | |||
constexpr auto stdout_write = [](const char* ptr, size_t size){ | |||
return write(0, ptr, size); | |||
}; | |||
constexpr auto stderr_write = [](const char* ptr, size_t size){ | |||
return write(2, ptr, size); | |||
}; | |||
} |
@ -0,0 +1,255 @@ | |||
#include <SDL2/SDL.h> | |||
#ifdef SV_EXP | |||
#include <experimental/string_view> | |||
namespace std | |||
{ | |||
using string_view = std::experimental::string_view; | |||
} | |||
#else | |||
#include <string_view> | |||
#endif | |||
#include <vector> | |||
#include <set> | |||
#include <chrono> | |||
#include <thread> | |||
#include <atomic> | |||
#include <string> | |||
#include <utility> | |||
#include <iostream> | |||
#include <sstream> | |||
#include <cassert> | |||
#include <random> | |||
#include <fstream> | |||
#include <algorithm> | |||
#include "rigid_paradise/display.h" | |||
#include "rigid_paradise/config.h" | |||
#include "rigid_paradise/pomodoro.h" | |||
using namespace std::chrono_literals; | |||
auto wait_interval = 12min; | |||
//auto wait_interval = 12s; | |||
std::vector<std::string_view> audioDeviceNames; | |||
std::vector<SDL_AudioDeviceID> audioDeviceIDs; | |||
Uint8* audio_pos; // global pointer to the audio buffer to be played | |||
Uint8* audio_pos_orig; // global pointer to the audio buffer to be played | |||
Uint32 audio_len; // remaining length of the sample we have to play | |||
Uint32 audio_len_orig; // remaining length of the sample we have to play | |||
std::atomic<bool> must_play = false; | |||
std::minstd_rand bad_rng; | |||
std::set<std::pair<char, std::string>> pomodoro_actions = pomodoro_actions_unwrap; | |||
namespace sound_mng | |||
{ | |||
void my_audio_callback(void* userdata, Uint8* stream, int len) | |||
{ | |||
if (audio_len ==0) | |||
{ | |||
return; | |||
} | |||
len = ( len > audio_len ? audio_len : len ); | |||
//SDL_memcpy (stream, audio_pos, len); // simply copy from one buffer into the other | |||
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);// mix from one buffer into another | |||
audio_pos += len; | |||
audio_len -= len; | |||
} | |||
constexpr SDL_AudioSpec base_spec = | |||
{ | |||
.freq = 48000, | |||
.format = AUDIO_F32, | |||
.channels = 2, | |||
.silence = 0, | |||
.samples = 4096, | |||
.callback = my_audio_callback, | |||
.userdata = nullptr | |||
}; | |||
void loadAudioDevices() | |||
{ | |||
int i, count = SDL_GetNumAudioDevices(0); | |||
for (i = 0; i < count; ++i) | |||
{ | |||
const char* name = SDL_GetAudioDeviceName(i, 0); | |||
audioDeviceNames.push_back(name); | |||
SDL_AudioSpec ignored; | |||
audioDeviceIDs.push_back(SDL_OpenAudioDevice( | |||
name, | |||
0, | |||
&base_spec, | |||
&ignored, | |||
0 | |||
)); | |||
} | |||
} | |||
void player() | |||
{ | |||
while ( true ) | |||
{ | |||
while(must_play) | |||
{ | |||
SDL_PauseAudio(0); | |||
std::this_thread::sleep_for(1s); | |||
audio_pos = audio_pos_orig; | |||
audio_len = audio_len_orig; | |||
SDL_PauseAudio(1); | |||
std::this_thread::sleep_for(3s); | |||
} | |||
std::this_thread::sleep_for(1s); | |||
} | |||
return; | |||
} | |||
} | |||
int main(); | |||
int WinMain() | |||
{ | |||
return main(); | |||
} | |||
int main() | |||
{ | |||
assert(SDL_Init(SDL_INIT_AUDIO) >= 0); | |||
sound_mng::loadAudioDevices(); | |||
// sound data | |||
static Uint32 wav_length; // length | |||
static Uint8* wav_buffer; // buffer containing our audio file | |||
static SDL_AudioSpec wav_spec; // the specs of our sound | |||
// Load the WAV | |||
assert( SDL_LoadWAV(config::get_value("pomodoro_sound").c_str(), &wav_spec, &wav_buffer, &wav_length) != NULL ); | |||
// set the callback function | |||
wav_spec.callback = sound_mng::my_audio_callback; | |||
wav_spec.userdata = NULL; | |||
// set our global static variables | |||
audio_pos = wav_buffer; // copy sound buffer | |||
audio_pos_orig = wav_buffer; // copy sound buffer | |||
audio_len = wav_length; // copy file length | |||
audio_len_orig = wav_length; // copy file length | |||
/* Open the device */ | |||
if ( SDL_OpenAudio(&wav_spec, NULL) < 0 ) | |||
{ | |||
fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError()); | |||
exit(-1); | |||
} | |||
std::thread my_player{sound_mng::player}; | |||
/********************* MAIN LOOP *************************/ | |||
bool quit = false; | |||
while(!quit) | |||
{ | |||
display::header("Pomodoro"); | |||
std::cout << "> Interval time is: "<< display::to_string(wait_interval) <<std::endl; | |||
std::cout << "> Tasklist excerpt:\n"; | |||
std::array<pomodoro::pomotask,5> sample; | |||
auto list = pomodoro::get_pomodoro_tasks(); | |||
auto smp_end = std::sample(list.begin(), list.end(), sample.begin(),5, bad_rng); | |||
for(auto it = sample.begin(); it != smp_end; it++) | |||
{ | |||
std::cout << "> ["<<it->first<<"]: "<<it->second<<"\n"; | |||
} | |||
std::cout<<std::endl; | |||
std::this_thread::sleep_for(wait_interval); | |||
must_play.store(true); | |||
{ | |||
display::header("Pomodoro"); | |||
std::cout << "> Select your current action:" << std::endl; | |||
display::pair_set(pomodoro_actions); | |||
char action; | |||
bool valid=false; | |||
do | |||
{ | |||
std::string str; | |||
getline (std::cin, str); | |||
auto is_matching = [&](auto action) | |||
{ | |||
return str[0]==action.first; | |||
}; | |||
if( | |||
std::any_of( | |||
pomodoro_actions.begin(), | |||
pomodoro_actions.end(), | |||
is_matching | |||
) | |||
) | |||
{ | |||
action=str[0]; | |||
valid=true; | |||
} | |||
} | |||
while(!valid); | |||
auto task_list = pomodoro::get_pomodoro_tasks(); | |||
std::cout<<"> Select your current assigned task:"<<std::endl; | |||
display::pair_set(task_list); | |||
int task_nb; | |||
std::string task_label; | |||
valid=false; | |||
do | |||
{ | |||
try | |||
{ | |||
std::cin >> task_nb; | |||
} | |||
catch(...) | |||
{ | |||
task_nb = 0; | |||
} | |||
std::for_each(task_list.begin(), task_list.end(), [&](auto action) | |||
{ | |||
if(task_nb==action.first) | |||
{ | |||
valid=true; | |||
task_label = display::escape_csv(action.second); | |||
} | |||
}); | |||
} | |||
while(!valid); | |||
std::ofstream(config::get_value("pomodoro.log"), std::ofstream::app)<<std::chrono::seconds(std::time(NULL)).count()<<"\t"<<action<<"\t"<<task_label<<std::endl; | |||
} | |||
must_play.store(false); | |||
} | |||
// shut everything down | |||
SDL_CloseAudio(); | |||
SDL_FreeWAV(wav_buffer); | |||
} |
@ -0,0 +1,31 @@ | |||
#ifdef SV_EXP | |||
#include <experimental/string_view> | |||
namespace std | |||
{ | |||
using string_view = std::experimental::string_view; | |||
} | |||
#else | |||
#include <string_view> | |||
#endif | |||
#include <vector> | |||
#include <set> | |||
#include <chrono> | |||
#include <thread> | |||
#include <atomic> | |||
#include <string> | |||
#include <utility> | |||
#include <iostream> | |||
#include <sstream> | |||
#include <cassert> | |||
#include <random> | |||
#include <fstream> | |||
#include <algorithm> | |||
#include "rigid_paradise/display.h" | |||
#include "rigid_paradise/config.h" | |||
#include "rigid_paradise/pomodoro.h" | |||
using namespace std::chrono_literals; | |||
std::set<std::pair<char, std::string>> pomodoro_actions = pomodoro_actions_unwrap; | |||
@ -0,0 +1,67 @@ | |||
#include <vector> | |||
#include <algorithm> | |||
#include <string> | |||
#include <sstream> | |||
#include <rigid_paradise/systems/stdio.h> | |||
#include <string.h> | |||
#ifndef EXIT_FAILURE | |||
#define EXIT_FAILURE -1 | |||
#endif | |||
const char* help = | |||
"RigidParadise Yes, no version number\n\ | |||
Archivist - 2019\n\ | |||
usage: yes [STRING]*\n\ | |||
Repeatedly output a line with all specified STRING(s), or 'y'.\n\ | |||
yes --help\n\ | |||
yes --version\n\ | |||
Display this text.\n"; | |||
const size_t help_len = strlen(help); | |||
int main(int argc, char** argv) | |||
{ | |||
std::string to_write = "y\n"; | |||
std::vector<char> str; | |||
constexpr size_t target_size = 1 << 15; | |||
str.reserve(target_size); | |||
if(argc>1) | |||
{ | |||
if(argv[1][0]=='-' && argv[1][1]=='-') | |||
{ | |||
os::stdout_write(help, help_len); | |||
return 0; | |||
} | |||
std::stringstream generator; | |||
std::for_each(argv+1, argv+argc, [&](auto cstring) | |||
{ | |||
generator << cstring << ' '; | |||
}); | |||
to_write = generator.str(); | |||
to_write[to_write.size()-1]='\n'; | |||
} | |||
do | |||
{ | |||
for(auto a : to_write) | |||
{ | |||
str.push_back(a); | |||
} | |||
} | |||
while(str.size()<(target_size)); | |||
do | |||
{ | |||
auto check = os::stdout_write(str.data() , str.size()); | |||
if(check<0) | |||
{ | |||
return EXIT_FAILURE; | |||
} | |||
} | |||
while(1); | |||
return 0; | |||
} |