main.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. #include <atomic>
  2. #include <cstdio>
  3. #include <iomanip>
  4. #include <cerrno>
  5. #include <iostream>
  6. #include "simple.hpp"
  7. #include "plain_button.h"
  8. #include "layout.h"
  9. #include "digits.h"
  10. #include "ecs.hpp"
  11. #include "utils.hpp"
  12. #include "digit_display.h"
  13. #include "timer.h"
  14. // TODO: get rid of these, are here to generate the focus_group and layout of entity iterator range, define specific entity types and explicitly instantiaste them in a separate cpp file
  15. #include "implementation.hpp"
  16. #include "layout.hpp"
  17. // const std::chrono::steady_clock::duration max_duration = 99h + 59min + 59s;
  18. // const std::chrono::steady_clock::duration min_duration{};
  19. struct nothing {};
  20. using movement = motion::movement<std::chrono::steady_clock::duration, nothing, nothing>;
  21. int main(int argc, const char** argv) try
  22. {
  23. using support::ston;
  24. const auto main_color = argc > 2
  25. ? rgb_pixel::from(ston<rgb_pixel::int_type>(argv[2]))
  26. : 0x009dff_rgb; // or 0x3ba3cd_rgb;
  27. const auto second_color = argc > 3
  28. ? rgb_pixel::from(ston<rgb_pixel::int_type>(argv[3]))
  29. : 0x000000_rgb;
  30. const std::chrono::milliseconds frametime (argc > 4 ? ston<unsigned>(argv[4]) : 33);
  31. if(main_color == second_color) // would be cool to have a smarter contrast check
  32. {
  33. std::fputs("foreground and background colors should differ", stderr);
  34. std::fputs("\n", stderr);
  35. return -1;
  36. }
  37. std::string icon_string =
  38. "---------" // "---------"
  39. "+++++++++" // "-+++++++-"
  40. "--+------" // "----+----"
  41. "--+-+---+" // "----+----"
  42. "--+-+-+-+" // "----+----"
  43. "--+-+-+-+" // "----+----"
  44. "--+-+-+-+" // "----+----"
  45. "--+-+++++" // "----+----"
  46. "---------" // "---------"
  47. ;
  48. initializer init;
  49. surface icon_small(reinterpret_cast<surface::byte*>(icon_string.data()), {9,9},
  50. pixel_format(pixel_format::type::index8));
  51. icon_small.format().palette()->set_color('+', main_color);
  52. icon_small.format().palette()->set_color('-', 0x0_rgba);
  53. graphical::software_window win("truwo", {400,400}, graphical::window::flags::resizable);
  54. auto fg_color = win.surface().format().color(main_color);
  55. auto bg_color = win.surface().format().color(second_color);
  56. surface icon({64,64}, pixel_format(pixel_format::type::rgba8888));
  57. blit(convert( icon_small, icon.format()), icon, rect{icon.size()});
  58. win.icon(icon);
  59. auto music = argc > 1
  60. ? argv[1][0] != '\0' ? std::optional<musical::wav>(argv[1]) : std::nullopt
  61. : std::optional<musical::wav>("./truwo.wav");
  62. std::atomic<bool> music_playing = false;
  63. // std::atomic<bool> music_done = false;
  64. auto player = [&music_playing, &music, i = music->buffer().begin()](auto& device, auto buffer) mutable
  65. {
  66. if(!music_playing)
  67. {
  68. std::fill(buffer.begin(), buffer.end(), device.silence());
  69. return;
  70. }
  71. const auto buffer_size = buffer.end() - buffer.begin();
  72. const auto size = std::min<size_t>(buffer_size, music->buffer().end() - i);
  73. std::copy_n(i, size, buffer.begin());
  74. i += size;
  75. if(i == music->buffer().end())
  76. {
  77. i = music->buffer().begin();
  78. music_playing = false;
  79. }
  80. };
  81. using music_device = musical::device_with_callback<decltype(player)>;
  82. std::unique_ptr<music_device> device = nullptr;
  83. bool paused = true;
  84. components<
  85. object_interface<
  86. i_ui_object,
  87. i_graphic,
  88. i_interactive,
  89. i_movable_bounds<int2>
  90. >,
  91. movement,
  92. focus_vector,
  93. bounds_layout_vector
  94. > components {};
  95. std::cerr << "----------------INIT----------------" << '\n';
  96. std::cerr << components.log_sizes() << '\n';
  97. rect button_rect{win.size()/8 + int2{5,5}};
  98. auto make_control_button = [&]() -> auto&
  99. {
  100. auto& button = components.emplace<plain_button>(fg_color, button_rect);
  101. return button;
  102. };
  103. auto& stop_button = make_control_button();
  104. auto& down_button = make_control_button();
  105. std::cerr << "----------------BUTTONS----------------" << '\n';
  106. std::cerr << components.log_sizes() << '\n';
  107. focus_vector button_focus_group{
  108. {stop_button, down_button} };
  109. focus_vector main_focus_group;
  110. auto focus_handler = [&main_focus_group](auto& element)
  111. {
  112. main_focus_group.focus_on(element);
  113. };
  114. stop_button.on_press.push_back(focus_handler);
  115. down_button.on_press.push_back(focus_handler);
  116. // oof
  117. const auto & const_components = components;
  118. auto& movable_bounds = const_components.get<i_movable_bounds<int2>*>();
  119. auto button_range_begin = std::find(movable_bounds.begin(), movable_bounds.end(),
  120. (i_movable_bounds<int2>*)(&stop_button));
  121. auto button_range = support::offset_range
  122. {
  123. movable_bounds,
  124. support::range
  125. {
  126. button_range_begin,
  127. std::next(std::find(movable_bounds.begin(), movable_bounds.end(),
  128. (i_movable_bounds<int2>*)(&down_button)))
  129. } - button_range_begin
  130. };
  131. bounds_layout button_layout (button_range, int2::i(5));
  132. button_layout.update();
  133. entities entities {std::move(components)};
  134. std::cerr << "----------------ENTITIES----------------" << '\n';
  135. std::cerr << entities.log_sizes() << '\n';
  136. auto make_timer_ui = [&entities, &fg_color, &focus_handler](auto last_total_duration)
  137. {
  138. return entities.make([&](auto& components)
  139. {
  140. int2 digit_size{40,100};
  141. auto digit_spacing = int2::i(5);
  142. auto make_time_display = [&]() -> auto&
  143. {
  144. // oof, template -_-
  145. auto& display = components.template emplace<digit_display<>>(digit_size, digit_spacing, fg_color);
  146. return display;
  147. };
  148. auto& hours_display = make_time_display();
  149. auto& minutes_display = make_time_display();
  150. auto& seconds_display = make_time_display();
  151. rect separator_rect{{13,100}};
  152. components.template emplace<bounds_layout_vector>(
  153. std::vector<i_movable_bounds<int2>*>{
  154. &hours_display,
  155. &components.template emplace<digit_bitmap>(digit[10], fg_color, separator_rect),
  156. &minutes_display,
  157. &components.template emplace<digit_bitmap>(digit[10], fg_color, separator_rect),
  158. &seconds_display
  159. },
  160. int2::i(5)
  161. ).update();
  162. components.template push( focus_vector{{hours_display, minutes_display, seconds_display}} );
  163. hours_display.on_press.push_back(focus_handler);
  164. minutes_display.on_press.push_back(focus_handler);
  165. seconds_display.on_press.push_back(focus_handler);
  166. components.template emplace<movement>(movement{last_total_duration});
  167. std::cerr << "----------------INMAKE----------------" << '\n';
  168. std::cerr << components.log_sizes() << '\n';
  169. return std::tie(
  170. hours_display,
  171. minutes_display,
  172. seconds_display
  173. );
  174. });
  175. };
  176. std::vector<decltype(make_timer_ui(0ms))> timer_displays;
  177. auto timer_layout = bounds_layout{ simple::support::range{
  178. entities.get_component_iterator<bounds_layout_vector>(timer_displays.begin()),
  179. entities.get_component_iterator<bounds_layout_vector>(timer_displays.end()),
  180. }, int2::j(15)};
  181. bounds_layout_vector main_layout({&button_layout, &timer_layout}, int2::j(15));
  182. main_layout.update();
  183. main_layout += int2(10,10);
  184. auto current_timer = motion::symphony{ simple::support::range{
  185. entities.get_component_iterator<movement>(timer_displays.begin()),
  186. entities.get_component_iterator<movement>(timer_displays.end()),
  187. }};
  188. auto timer_focus_group = focus_group{ simple::support::range{
  189. entities.get_component_iterator<focus_vector>(timer_displays.begin()),
  190. entities.get_component_iterator<focus_vector>(timer_displays.end()),
  191. }};
  192. main_focus_group = focus_vector{{ button_focus_group, timer_focus_group }};
  193. auto reset_current_timer = [&]()
  194. {
  195. music_playing = false;
  196. init.graphics.screensaver.release_one();
  197. device = nullptr;
  198. current_timer.reset();
  199. paused = true;
  200. };
  201. stop_button.on_click.push_back([&](auto&)
  202. {
  203. if(music_playing && current_timer.done())
  204. {
  205. reset_current_timer();
  206. }
  207. else
  208. {
  209. paused = true;
  210. }
  211. });
  212. down_button.on_click.push_back([&](auto&)
  213. {
  214. paused = false;
  215. });
  216. timer stop_button_hold(1s);
  217. stop_button.on_press.push_back([&stop_button_hold](auto&)
  218. {
  219. stop_button_hold = timer(stop_button_hold.duration(), true);
  220. });
  221. stop_button.on_release.push_back([&stop_button_hold](auto&)
  222. {
  223. stop_button_hold.pause();
  224. });
  225. bool done = false;
  226. std::chrono::steady_clock::time_point current_time;
  227. while(!done)
  228. {
  229. const auto new_time = std::chrono::steady_clock::now();
  230. const auto time_delta = new_time - current_time;
  231. current_time = new_time;
  232. using namespace interactive;
  233. while(auto event = next_event())
  234. {
  235. std::visit(support::overload{
  236. [&done](quit_request) { done = true; },
  237. [&win](window_size_changed)
  238. {
  239. win.update_surface();
  240. },
  241. [&main_focus_group](const mouse_down&)
  242. {
  243. main_focus_group.drop_focus();
  244. },
  245. [&entities, &main_focus_group, &timer_focus_group, &timer_layout, &timer_displays, &make_timer_ui, &main_layout, &current_timer](const key_pressed& e)
  246. {
  247. if(e.data.keycode == keycode::tab)
  248. {
  249. auto direction =
  250. pressed(scancode::rshift) ||
  251. pressed(scancode::lshift)
  252. ? i_focusable::prev
  253. : i_focusable::next
  254. ;
  255. if(!main_focus_group.focus(direction))
  256. {
  257. main_focus_group.drop_focus();
  258. main_focus_group.focus(direction);
  259. }
  260. }
  261. else if(e.data.keycode == keycode::n)
  262. {
  263. auto last_total_duration = empty(timer_displays) ? 0ms
  264. : entities.find<movement>(timer_displays.back().id).begin()->total;
  265. auto& entity = timer_displays.emplace_back(make_timer_ui(last_total_duration));
  266. std::cerr << "----------------NEWTIMER----------------" << '\n';
  267. std::cerr << "timer displays: " << timer_displays.size() << '\n';
  268. std::cerr << entities.log_sizes() << '\n';
  269. auto [h,m,s] = entity.components;
  270. current_timer = motion::symphony{ simple::support::range{
  271. entities.get_component_iterator<movement>(timer_displays.begin()),
  272. entities.get_component_iterator<movement>(timer_displays.end()),
  273. }};
  274. main_focus_group.drop_focus();
  275. timer_focus_group = focus_group{ simple::support::range{
  276. entities.get_component_iterator<focus_vector>(timer_displays.begin()),
  277. entities.get_component_iterator<focus_vector>(timer_displays.end()),
  278. }};
  279. timer_layout = bounds_layout{ simple::support::range{
  280. entities.get_component_iterator<bounds_layout_vector>(timer_displays.begin()),
  281. entities.get_component_iterator<bounds_layout_vector>(timer_displays.end()),
  282. }, int2::j(15)};
  283. timer_layout.update();
  284. main_layout.update();
  285. h.on_input.push_back([&entities, entity_id = entity.id](auto&&, int old_value, int new_value)
  286. {
  287. using namespace std::chrono;
  288. auto timer = entities.find<movement>(entity_id).begin();
  289. auto offset = hours(new_value) - hours(old_value);
  290. timer->reset();
  291. timer->total = timer->total - timer->elapsed + offset;
  292. });
  293. m.on_input.push_back([&entities, entity_id = entity.id](auto&&, int old_value, int new_value)
  294. {
  295. using namespace std::chrono;
  296. auto timer = entities.find<movement>(entity_id).begin();
  297. auto new_minutes = minutes(new_value);
  298. if(new_minutes >= hours(1))
  299. new_minutes = hours(1) - minutes(1);
  300. auto offset = new_minutes - minutes(old_value);
  301. timer->reset();
  302. timer->total = timer->total - timer->elapsed + offset;
  303. });
  304. s.on_input.push_back([&entities, entity_id = entity.id](auto&&, int old_value, int new_value)
  305. {
  306. using namespace std::chrono;
  307. auto timer = entities.find<movement>(entity_id).begin();
  308. auto new_seconds = seconds(new_value);
  309. if(new_seconds >= minutes(1))
  310. new_seconds = minutes(1) - seconds(1);
  311. auto offset = new_seconds - seconds(old_value);
  312. timer->reset();
  313. timer->total = timer->total - timer->elapsed + offset;
  314. });
  315. }
  316. },
  317. [](auto) { }
  318. }, *event);
  319. for(auto&& interactive : entities.get_all<i_interactive*>())
  320. interactive->update(*event);
  321. }
  322. // TODO:
  323. // use proper iterator that return ranges, and steps based on entity_size instead of the base entity iterator
  324. for
  325. (
  326. auto [hours, minutes, seconds, timer, end] = std::tuple
  327. {
  328. entities.get_component_iterator<i_graphic*>(timer_displays.begin()),
  329. entities.get_component_iterator<i_graphic*, 1>(timer_displays.begin()),
  330. entities.get_component_iterator<i_graphic*, 2>(timer_displays.begin()),
  331. entities.get_component_iterator<movement>(timer_displays.begin()),
  332. entities.get_component_iterator<movement>(timer_displays.end())
  333. };
  334. timer != end;
  335. ++hours, ++minutes, ++seconds, ++timer
  336. )
  337. {
  338. auto duration = timer->total - timer->elapsed;
  339. static_cast<digit_display<>*>(*hours)->set(extract_duration<std::chrono::hours>(duration).count());
  340. static_cast<digit_display<>*>(*minutes)->set(extract_duration<std::chrono::minutes>(duration).count());
  341. static_cast<digit_display<>*>(*seconds)->set(extract_duration<std::chrono::seconds>(duration).count());
  342. }
  343. fill(win.surface(), bg_color);
  344. for(auto&& graphic : entities.get_all<i_graphic*>())
  345. graphic->draw(win.surface());
  346. win.update();
  347. down_button.enable(!music_playing && paused);
  348. // TODO:
  349. // use proper iterator that return ranges, and steps based on entity_size instead of the base entity iterator
  350. for
  351. (
  352. auto [hours, minutes, seconds, end] = std::tuple
  353. {
  354. entities.get_component_iterator<i_interactive*>(timer_displays.begin()),
  355. entities.get_component_iterator<i_interactive*>(timer_displays.begin()),
  356. entities.get_component_iterator<i_interactive*>(timer_displays.begin()),
  357. entities.get_component_iterator<i_interactive*>(timer_displays.end()),
  358. };
  359. hours != end;
  360. ++hours, ++minutes, ++seconds
  361. )
  362. {
  363. static_cast<ui_element*>(*hours)->enable(!music_playing && paused);
  364. static_cast<ui_element*>(*minutes)->enable(!music_playing && paused);
  365. static_cast<ui_element*>(*seconds)->enable(!music_playing && paused);
  366. }
  367. if(stop_button_hold.check())
  368. reset_current_timer();
  369. if(!music_playing)
  370. {
  371. if(timer_displays.size() != 0 && current_timer.done())
  372. {
  373. reset_current_timer();
  374. }
  375. }
  376. if(!paused)
  377. {
  378. auto result = current_timer.advance(time_delta);
  379. for(auto&& timer : result.updated)
  380. {
  381. if(timer.done())
  382. {
  383. if(!music_playing)
  384. {
  385. music_playing = true;
  386. main_focus_group.focus_on(stop_button);
  387. if(music)
  388. {
  389. device = std::make_unique<music_device>(
  390. musical::basic_device_parameters{music->obtained()},
  391. player
  392. );
  393. device->play();
  394. }
  395. }
  396. }
  397. }
  398. if(result.done)
  399. {
  400. paused = true;
  401. // TODO:
  402. }
  403. }
  404. const auto next_frame_time = current_time + frametime;
  405. // const auto next_frame_time = paused
  406. // ? current_time + frametime
  407. // : min(current_timer.target_time_point(), current_time + frametime);
  408. std::this_thread::sleep_until(next_frame_time);
  409. }
  410. return 0;
  411. }
  412. catch(...)
  413. {
  414. if(errno)
  415. std::perror("ERROR");
  416. const char* sdl_error = SDL_GetError();
  417. if(*sdl_error)
  418. {
  419. std::fputs(sdl_error, stderr);
  420. std::fputs("\n", stderr);
  421. }
  422. throw;
  423. }