There's now a bezos_ctl and bezos_cli that can control the presentation window. The bezos_ctl is compile as a windows app so that it won't flash a cmd.exe window when run from launchers like Stream Deck. The bezos_cli is compiled as a console app so you can use it from the command line.

master
Zed A. Shaw 3 weeks ago
parent 717f90eae4
commit 26ed4b74ce
  1. 6
      Makefile
  2. 45
      meson.build
  3. 4
      sample/about-bezos.md
  4. 71
      src/bezos_ctl.cpp
  5. 91
      src/main.cpp

@ -27,16 +27,16 @@ debug_build:
run: build run: build
ifeq '$(OS)' 'Windows_NT' ifeq '$(OS)' 'Windows_NT'
powershell "cp ./builddir/bezos.exe ." powershell "cp ./builddir/bezos.exe ."
./bezos ${SAMPLE} ./bezos -d ${SAMPLE}
else else
./builddir/bezos ${SAMPLE} ./builddir/bezos -d ${SAMPLE}
endif endif
debug: build debug: build
gdb --nx -x .gdbinit --ex run --args builddir/bezos gdb --nx -x .gdbinit --ex run --args builddir/bezos
debug_run: build debug_run: build
gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/bezos ${SAMPLE} gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/bezos -d ${SAMPLE}
clean: clean:
meson compile --clean -C builddir meson compile --clean -C builddir

@ -3,7 +3,7 @@
project('bezos-loves-slides', 'cpp', project('bezos-loves-slides', 'cpp',
version: '0.2.0', version: '0.2.0',
default_options: [ default_options: [
'cpp_std=c++20', 'cpp_std=c++23',
'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1',
]) ])
@ -87,6 +87,8 @@ dependencies += [
] ]
inc_dirs = include_directories('src')
sources = [ sources = [
'src/dbc.cpp', 'src/dbc.cpp',
'src/control_ui.cpp', 'src/control_ui.cpp',
@ -99,16 +101,51 @@ tests = [
'tests/parsing.cpp' 'tests/parsing.cpp'
] ]
executable('bezos', sources + ['src/main.cpp'],
bezos_lib = static_library('bezos',
sources,
cpp_args: cpp_args,
include_directories: inc_dirs,
override_options: exe_defaults,
dependencies: dependencies)
bezos_dep = declare_dependency(
link_with: bezos_lib,
include_directories: inc_dirs)
dependencies += [ bezos_dep ]
executable('bezos', ['src/main.cpp'],
cpp_args: cpp_args, cpp_args: cpp_args,
link_args: link_args, link_args: link_args,
include_directories: include_directories('src'), include_directories: inc_dirs,
win_subsystem: 'console',
override_options: exe_defaults, override_options: exe_defaults,
dependencies: dependencies) dependencies: dependencies)
executable('runtests', sources + tests, # bezos_ctrl is a version of bezos_cli compiled to be a windows
# app instead of console. Ironically you have to do this so that
# stream deck and other launchers don't open a cmd.exe to run it.
executable('bezos_ctrl', ['src/bezos_ctl.cpp'],
cpp_args: cpp_args,
link_args: link_args,
include_directories: inc_dirs,
override_options: exe_defaults,
win_subsystem: 'windows',
dependencies: dependencies)
executable('bezos_cli', ['src/bezos_ctl.cpp'],
cpp_args: cpp_args, cpp_args: cpp_args,
link_args: link_args, link_args: link_args,
include_directories: include_directories('src'), include_directories: include_directories('src'),
win_subsystem: 'console',
override_options: exe_defaults,
dependencies: dependencies)
executable('runtests', tests,
cpp_args: cpp_args,
link_args: link_args,
include_directories: inc_dirs,
override_options: exe_defaults, override_options: exe_defaults,
win_subsystem: 'console',
dependencies: dependencies + [catch2]) dependencies: dependencies + [catch2])

@ -79,8 +79,8 @@ Bare content like this
{ {
"bg_color": [10, 10, 25, 255], "bg_color": [10, 10, 25, 255],
"font_color": [255, 146, 0, 255], "font_color": [255, 146, 0, 255],
"title_color": [255, 255, 0, 255], "title_color": [255, 200, 0, 255],
"font_size": 80, "font_size": 120,
"title_size": 120, "title_size": 120,
"font_padding": 20 "font_padding": 20
} }

@ -0,0 +1,71 @@
#include <fmt/xchar.h>
#include "dbc.hpp"
#include <SFML/Network/UdpSocket.hpp>
#include <memory>
#include <unistd.h>
using namespace std::chrono_literals;
const int DEFAULT_PORT=9898;
struct Options {
bool help{false};
bool focus{false};
bool error{false};
unsigned short port=DEFAULT_PORT;
};
void request_focus(Options& options) {
fmt::println("Moving control window front: port={}", options.port);
sf::UdpSocket sock;
uint32_t cmd = 2;
auto localhost = sf::IpAddress::getLocalAddress();
auto result = sock.send(&cmd, sizeof cmd, *localhost, options.port);
}
void print_usage() {
fmt::println("USAGE: bezos [-p {}] [-hf] -d deck.md", DEFAULT_PORT);
}
Options parse_options(int argc, char* argv[]) {
int opt = 0;
Options result;
while((opt = getopt(argc, argv, "fhp:")) != -1) {
switch(opt) {
case 'f':
result.focus=true;
break;
case 'h':
print_usage();
return {.help=true};
break;
case 'p':
result.port = std::stoi(optarg);
break;
default:
print_usage();
return {.error=true};
}
}
if(!result.focus) {
print_usage();
}
return result;
}
int main(int argc, char *argv[]) {
auto options = parse_options(argc, argv);
if(options.focus) {
request_focus(options);
return 0;
} else if(options.help) {
return 0;
} else if(options.error) {
return 1;
}
}

@ -9,10 +9,20 @@
#include <filesystem> #include <filesystem>
#include <chrono> #include <chrono>
#include <SFML/System/Clock.hpp> #include <SFML/System/Clock.hpp>
#include <SFML/Network/UdpSocket.hpp>
#include <memory> #include <memory>
#include <unistd.h>
using namespace std::chrono_literals; using namespace std::chrono_literals;
struct Options {
std::string deck_file{};
bool deck_given{false};
bool help{false};
bool error{false};
unsigned short port=9898;
};
std::shared_ptr<SlidesUI> load_slides(const std::string& input_md, std::shared_ptr<gui::Backend> backend) { std::shared_ptr<SlidesUI> load_slides(const std::string& input_md, std::shared_ptr<gui::Backend> backend) {
try { try {
auto deck = parse_slides(input_md, [&](nlohmann::json& config) { auto deck = parse_slides(input_md, [&](nlohmann::json& config) {
@ -59,8 +69,72 @@ struct ChangeDetector {
} }
}; };
void print_usage() {
fmt::println("USAGE: bezos [-p PORT] [-hf] -d deck.md");
}
std::optional<sf::UdpSocket> listen_control(Options& opts) {
sf::UdpSocket socket;
auto status = socket.bind(opts.port);
if(status == sf::Socket::Status::Error) {
fmt::println("Failed to bind port");
return std::nullopt;
}
socket.setBlocking(false);
fmt::println("Focus listener on port {}", opts.port);
return socket;
}
Options parse_options(int argc, char* argv[]) {
int opt = 0;
Options result;
while((opt = getopt(argc, argv, "hp:d:")) != -1) {
switch(opt) {
case 'h':
print_usage();
return {.help=true};
break;
case 'p':
result.port = std::stoi(optarg);
break;
case 'd':
result.deck_given=true;
result.deck_file = optarg;
break;
default:
print_usage();
return {.error=true};
}
}
if(!result.deck_given) {
print_usage();
result.error = true;
}
return result;
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
dbc::check(argc >= 2, "USAGE: bezos my_fucking_slides.md"); auto options = parse_options(argc, argv);
if(options.help) {
return 0;
} else if(options.error) {
return 1;
}
auto control = listen_control(options);
if(control == std::nullopt) {
fmt::println("Error binding UDP port {}", options.port);
}
auto& modes = sf::VideoMode::getFullscreenModes(); auto& modes = sf::VideoMode::getFullscreenModes();
auto screen_mode = std::find_if(modes.begin(), modes.end(), [=](const auto& a) -> bool { auto screen_mode = std::find_if(modes.begin(), modes.end(), [=](const auto& a) -> bool {
@ -77,7 +151,7 @@ int main(int argc, char *argv[]) {
auto backend = std::make_shared<gui::Backend>(); auto backend = std::make_shared<gui::Backend>();
auto slides = load_slides(argv[1], backend); auto slides = load_slides(options.deck_file, backend);
if(!slides) { if(!slides) {
fmt::println("ERROR in your .md file"); fmt::println("ERROR in your .md file");
@ -90,10 +164,19 @@ int main(int argc, char *argv[]) {
dbc::check(control_ui.$status != nullptr, "bad ptr"); dbc::check(control_ui.$status != nullptr, "bad ptr");
ChangeDetector slides_reloader{argv[1]}; ChangeDetector slides_reloader{options.deck_file};
ChangeDetector layout_reloader{slides->$deck->config["layouts"]}; ChangeDetector layout_reloader{slides->$deck->config["layouts"]};
while(controller.isOpen()) { while(controller.isOpen()) {
uint32_t cmd = 0;
std::size_t received = 0;
std::optional<sf::IpAddress> sender;
if(control->receive(&cmd, sizeof cmd, received, sender, options.port) == sf::Socket::Status::Done) {
fmt::println("sender {} sent {}", sender->toString(), cmd);
sf::Mouse::setPosition({WINDOW_WIDTH - 100, WINDOW_HEIGHT - 100}, presenter);
}
while(const auto event = presenter.pollEvent()) { while(const auto event = presenter.pollEvent()) {
if(event) slides->handle_events(presenter, *event); if(event) slides->handle_events(presenter, *event);
} }
@ -112,7 +195,7 @@ int main(int argc, char *argv[]) {
// save the current slide // save the current slide
auto current_slide = slides->$deck->current; auto current_slide = slides->$deck->current;
// load the new one // load the new one
auto new_slides = load_slides(argv[1], backend); auto new_slides = load_slides(options.deck_file, backend);
if(new_slides) { if(new_slides) {
new_slides->set_slide(current_slide); new_slides->set_slide(current_slide);

Loading…
Cancel
Save