screensaver/main.cpp
2022-09-13 09:39:52 +02:00

196 lines
6.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// C++
#include <iostream>
#include <cstdlib>
#include <string>
#include <stdexcept>
#include <cmath>
#include <thread>
#include <fstream>
#include <algorithm>
// POSIX
#include <getopt.h>
#include <unistd.h>
// libraries
#include <cairo/cairo.h>
#include <libscgui.hpp>
#include <libscerror.hpp>
#include <libscscreensaver.hpp>
#include <libscstring.hpp>
#include <libscnumerics.hpp>
#include <sc/plugin.hpp>
const int WIDTH {1600};
const int HEIGHT {900};
sc::gui::Window* main_window {nullptr};
ScreensaverPlugin* main_saver {nullptr};
std::unique_ptr<sc::gui::Image> main_image {nullptr};
std::unique_ptr<cairo_t, void(*)(cairo_t*)> main_context {nullptr, cairo_destroy};
std::unique_ptr<cairo_surface_t, void(*)(cairo_surface_t*)> main_surface {nullptr, cairo_surface_destroy};
void print_help() {
std::cout << "usage: screensaver [-h|-l|--version]\n";
std::cout << " -h, --help show this help text and exit\n";
std::cout << " -l, --list show available screensaver modules and exit\n";
std::cout << " -r, --random choose random module from available ones\n";
std::cout << " --version show version number and exit\n";
}
void print_version() {
std::cout << "screensaver version 1.0\n";
}
void list_plugins() {
for (const std::string& name : sc::plugin<ScreensaverPlugin>::names()) {
std::cout << name << '\n';
}
}
void create_image(int w, int h) {
// create an SDL drawing surface and connect it to cairo
main_image.reset(new sc::gui::Image {w, h});
SDL_Surface* s = main_image->surface();
cairo_surface_t* cs {cairo_image_surface_create_for_data(static_cast<unsigned char*>(s->pixels),
CAIRO_FORMAT_RGB24, s->w, s->h, s->pitch)};
main_surface.reset(cs);
cairo_t* cr {cairo_create(cs)};
main_context.reset(cr);
}
void draw() {
main_saver->update();
sc::gui::ImageLock lock {*main_image};
main_saver->render();
main_window->dirty(true);
main_window->show_image(*main_image);
}
bool handle_window_resize(const SDL_Event& event, bool quit) {
if (event.window.data1 != main_image->width() || event.window.data2 != main_image->height()) {
std::cerr << "window resized to " << event.window.data1 << "×" << event.window.data2 << " pixels\n";
create_image(event.window.data1, event.window.data2);
cairo_rectangle_t r {0, 0, static_cast<double>(event.window.data1), static_cast<double>(event.window.data2)};
if (main_saver) {
main_saver->setup(main_context.get(), r);
}
}
return quit;
}
int main(int argc, const char * argv[]) {
try {
// gather plugins
#if DEBUG
std::string plugin_dir {sc::dirname(sc::tool_path(argv[0])) + "/plugins"};
#else
std::string plugin_dir {"/usr/local/share/screensaver/plugins"};
#endif
sc::plugin<ScreensaverPlugin>::scan_plugins(plugin_dir, "saver");
std::string saver_name;
bool random_saver {false};
int opt_char, opt_val;
struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{"list", no_argument, nullptr, 'l'},
{"random", no_argument, nullptr, 'r'},
{"version", no_argument, &opt_val, 1},
{nullptr, 0, nullptr, 0}
};
while ((opt_char = getopt_long(argc, const_cast<char* const *>(argv), "hlr", long_options, nullptr)) != -1) {
std::string arg {optarg ? optarg : ""};
switch (opt_char) {
case 0: {
// handle long-only options here
switch (opt_val) {
case 1:
print_version();
return EXIT_SUCCESS;
}
break;
}
case 'h':
print_help();
return EXIT_SUCCESS;
case 'l':
list_plugins();
return EXIT_SUCCESS;
case 'r':
random_saver = true;
break;
case '?':
throw std::runtime_error("unrecognized option");
}
}
std::vector<std::string> names {sc::plugin<ScreensaverPlugin>::names()};
if (optind == argc) {
// here when no file args
if (random_saver) {
saver_name = sc::random::choice(names);
} else {
saver_name = "Default";
}
}
for (int i = optind; i < argc; ++i) {
try {
// process file argv[i]
saver_name = argv[i];
if (random_saver) {
std::cerr << "screensaver: warning: -r option overridden by file arg\n";
}
auto it = std::find(names.begin(), names.end(), saver_name);
if (it == names.end()) {
saver_name = "";
throw std::out_of_range(saver_name);
}
break; // only first one used
} catch (const std::runtime_error& ex) {
std::cerr << "screensaver: " << ex.what() << '\n';
} catch (const std::out_of_range& ex) {
std::cerr << "screensaver: no such screensaver module: " << argv[i] << '\n';
}
}
// RAII instances
sc::gui::SDLWrapper sdl2;
sc::gui::SDLImageWrapper sdl_image;
// main window
sc::gui::Window& window {sc::gui::Window::new_window("Living Art", SDL_WINDOW_FULLSCREEN_DESKTOP)};
main_window = &window;
window.add_event_handler(handle_window_resize, SDL_WINDOWEVENT_RESIZED);
window.add_event_handler(handle_window_resize, SDL_WINDOWEVENT_SIZE_CHANGED);
SDL_ShowCursor(SDL_DISABLE);
// setup sdl surface and connect to cairo
create_image(WIDTH, HEIGHT);
// setup screen saver module
if (saver_name.size() == 0) {
saver_name = "Default";
std::cerr << "screensaver: using standard 'Default' module\n";
}
std::unique_ptr<ScreensaverPlugin> saver {sc::plugin<ScreensaverPlugin>::get(saver_name)()};
main_saver = saver.get();
main_saver->setup(main_context.get(), {0, 0, WIDTH, HEIGHT});
std::string config_path {plugin_dir + "/" + saver_name + ".json"};
std::ifstream config_file {config_path};
if (config_file) {
std::cerr << "screensaver: reading config file " << config_path << '\n';
nlohmann::json j;
config_file >> j;
main_saver->config(std::move(j));
}
sc::gui::app().fps(main_saver->fps());
sc::gui::app().add_run_loop_action(draw);
sc::gui::app().run();
} catch (const std::exception& ex) {
std::cerr << "screensaver: " << ex.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}