config.def.hpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #ifndef _CONFIG_H_
  2. #define _CONFIG_H_
  3. #include <csignal>
  4. #include <cmath>
  5. #include <chrono>
  6. #include <ctime>
  7. #include <thread>
  8. #include "ui/application.hpp"
  9. #include "ui/boxes.hpp"
  10. #include "ui/icon.hpp"
  11. #include "ui/label.hpp"
  12. #include "ui/scales.hpp"
  13. // this file is an example configuration for the knobs dashboard/launcher
  14. // it is meant to (non-exhaustively) illustrate how to quickly create dashboards
  15. // for your own needs
  16. // button callback helper: start a program and dismiss the dashboard
  17. // useful for program launchers
  18. #define launch(command) ([](){\
  19. std::thread t1(std::system, (command));\
  20. t1.detach();\
  21. the_window->dismiss();\
  22. })
  23. // button callback helper: execute a program, but keep the dashboard visible
  24. // useful for scales/sliders and tooling buttons
  25. #define spawn(command) ([](){\
  26. std::thread t1(std::system, (command));\
  27. t1.detach();\
  28. })
  29. // dashboard window dimensions, these must be set before creating the window
  30. static int const WINDOW_WIDTH = 600;
  31. static int const WINDOW_HEIGHT = 400;
  32. // global dashboard window pointer for signal handlers
  33. static ui::Window * the_window = nullptr;
  34. // in this config we use a signal handler to summon/dismiss the dashboard
  35. // a signal handler normally shouldn't be this complex (toggle uses a mutex),
  36. // but so far no crashes/locks/races were detected during normal use
  37. static void on_SIGUSR1(int)
  38. {
  39. if(the_window != nullptr) {
  40. the_window->toggle();
  41. }
  42. }
  43. // here is an example of how to turn a label into a clock which updates on
  44. // minute boundaries; can be adapted to tick on second boundaries if one is
  45. // so inclined; this function shouldn't terminate
  46. static void handle_clock(ui::LabelWidget * clock_label, char const * const clock_format)
  47. {
  48. while(true) {
  49. static char buf[64] = {0};
  50. time_t t_now = time(0);
  51. struct tm const * now = localtime(&t_now);
  52. strftime(buf, sizeof(buf), clock_format, now);
  53. clock_label->set_markup(buf);
  54. struct tm next_minute = *now;
  55. next_minute.tm_min += 1;
  56. next_minute.tm_sec = 0;
  57. time_t t_next = mktime(&next_minute);
  58. std::this_thread::sleep_for(std::chrono::seconds(t_next - t_now));
  59. }
  60. }
  61. // scale callback function: note the value parameter which is the value of
  62. // the scale right after the last update; this one sets the value for the
  63. // Master control of the default ALSA sound card
  64. static void adjust_volume(double value)
  65. {
  66. std::string command;
  67. command.reserve(64);
  68. command.append("amixer sset Master -- ")
  69. .append(std::to_string(std::lround(value))).append("%");
  70. std::system(command.data());
  71. }
  72. // the setup function is where we organize the layout of our dashboard and
  73. // wire controls to their handlers, it runs once during app initialization
  74. static void setup(ui::Window * window)
  75. {
  76. // if you need a global pointer to the window,
  77. // set that first to avoid segfaults
  78. the_window = window;
  79. // a label that will be our dashboard clock
  80. // it will be passed to the handler thread later
  81. // caveat: labels are pointers, not refs, because gtk's copy/move
  82. // constructors are deleted for some reason...
  83. auto clock = ui::Label("clock");
  84. // labels can take arbitrary pango markup, providing extreme flexibility:
  85. // if you can write a C/C++ function to get your text, you can have it
  86. // update a label with whatever information at whatever interval you want
  87. // window can only have one child widget, so it should probably be a box
  88. window->set_child(
  89. ui::BoundingBox(30).add( // a bounding box lets us control margins
  90. ui::VBox(30) // add a vertical layout box, 30px spacing between children
  91. .add(*clock) // then add widgets, or other boxes
  92. .add(
  93. ui::VBox(10)
  94. .add(ui::Button("terminal", "Alacritty", launch("alacritty")))
  95. .add(ui::Button("editor", "vim", launch("alacritty -e vim"))))
  96. .add( // calls to .add() can be chained
  97. ui::HBox(10)
  98. .add(ui::Icon("audio-speakers-symbolic"))
  99. .add(ui::HScale(0, 100, adjust_volume)))
  100. )
  101. );
  102. // setup the signal handler, in this case for USR1
  103. std::signal(SIGUSR1, on_SIGUSR1);
  104. // run the clock handler in a separate thread
  105. // because setup must be allowed to terminate
  106. static char const * const clock_format =
  107. "%a, %b %d\n<span size=\"300%%\">%H:%M</span>";
  108. std::thread t1(handle_clock, clock, clock_format);
  109. t1.detach();
  110. }
  111. #endif