Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,4 @@
};
};
}

12 changes: 12 additions & 0 deletions package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
fontconfig,
xxd,
mesa,
libglvnd,
xorg,
wayland,
libxkbcommon,
Expand Down Expand Up @@ -61,6 +62,7 @@ stdenv.mkDerivation rec {

buildInputs = [
glfw
libglvnd
freetype
libpng
fontconfig
Expand Down Expand Up @@ -97,6 +99,16 @@ stdenv.mkDerivation rec {

dontWrapGApps = false;

postFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
if [ -f "$out/bin/.clrsync_gui-wrapped" ]; then
wrapProgram "$out/bin/.clrsync_gui-wrapped" \
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ mesa.out libglvnd ]}" \
--set-default GBM_BACKENDS_PATH "${mesa}/lib/gbm" \
--set-default __EGL_VENDOR_LIBRARY_FILENAMES__ "${mesa}/share/glvnd/egl_vendor.d/50_mesa.json" \
--prefix LIBGL_DRIVERS_PATH : "${mesa}/lib/dri"
fi
'';

meta = with lib; {
description = "Color scheme manager with GUI and CLI";
homepage = "https://github.com/obsqrbtz/clrsync";
Expand Down
59 changes: 59 additions & 0 deletions src/cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "core/io/toml_file.hpp"
#include "core/palette/hellwal_generator.hpp"
#include "core/palette/matugen_generator.hpp"
#include "core/palette/pywal16_generator.hpp"
#include "core/palette/palette_file.hpp"
#include "core/palette/palette_manager.hpp"
#include "core/theme/theme_renderer.hpp"
Expand Down Expand Up @@ -139,6 +140,21 @@ void setup_argument_parser(argparse::ArgumentParser &program)
.default_value(std::string("0.0"))
.help("hellwal: gray scale factor (float)")
.metavar("FLOAT");

program.add_argument("--pywal16-background")
.help("pywal16: custom background color (hex)")
.metavar("COLOR");
program.add_argument("--pywal16-foreground")
.help("pywal16: custom foreground color (hex)")
.metavar("COLOR");
program.add_argument("--pywal16-backend")
.help("pywal16: color extraction backend")
.metavar("BACKEND");
program.add_argument("--pywal16-saturate")
.default_value(std::string("-1.0"))
.help("pywal16: color saturation from 0.0 to 1.0")
.metavar("FLOAT");
program.add_argument("--pywal16-light").help("pywal16: generate a light colorscheme").flag();
}

int main(int argc, char *argv[])
Expand Down Expand Up @@ -325,6 +341,49 @@ int main(int argc, char *argv[])
pal = gen.generate_from_image(image_path, opts);
}
}
else if (generator_name == "pywal16")
{
clrsync::core::pywal16_generator gen;
if (!gen.supports_current_system())
{
std::cerr << "Error: pywal16 is not supported on "
<< clrsync::core::generator::system_name(
clrsync::core::generator::current_system())
<< ". Supported systems: " << gen.supported_systems_description()
<< std::endl;
return 1;
}

if (program.is_used("--generate-color"))
{
std::cerr << "Error: --generate-color is only supported with --generator matugen"
<< std::endl;
return 1;
}

clrsync::core::pywal16_generator::options opts{};
if (program.is_used("--pywal16-background"))
opts.background = program.get<std::string>("--pywal16-background");
if (program.is_used("--pywal16-foreground"))
opts.foreground = program.get<std::string>("--pywal16-foreground");
if (program.is_used("--pywal16-backend"))
opts.backend = program.get<std::string>("--pywal16-backend");
if (program.is_used("--pywal16-light"))
opts.light = true;

try
{
std::string s = program.get<std::string>("--pywal16-saturate");
const float saturate = std::stof(s);
if (saturate >= 0.0f && saturate <= 1.0f)
opts.saturate = saturate;
}
catch (...)
{
}

pal = gen.generate_from_image(image_path, opts);
}
else
{
std::cerr << "Unknown generator: " << generator_name << std::endl;
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(CORE_SOURCES
palette/color.cpp
palette/hellwal_generator.cpp
palette/matugen_generator.cpp
palette/pywal16_generator.cpp
io/toml_file.cpp
config/config.cpp
common/process.cpp
Expand Down
94 changes: 89 additions & 5 deletions src/core/common/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ std::wstring build_windows_command_line(const std::vector<std::string> &args)
return command_line;
}

std::string run_windows_process(const std::vector<std::string> &args)
std::string run_windows_process(const std::vector<std::string> &args, int *exit_code)
{
SECURITY_ATTRIBUTES sa{};
sa.nLength = sizeof(sa);
Expand Down Expand Up @@ -159,6 +159,8 @@ std::string run_windows_process(const std::vector<std::string> &args)
if (!CreateProcessW(nullptr, mutable_command_line.data(), nullptr, nullptr, TRUE,
CREATE_NO_WINDOW, nullptr, nullptr, &startup, &process_info))
{
if (exit_code)
*exit_code = -1;
return {};
}

Expand All @@ -177,20 +179,34 @@ std::string run_windows_process(const std::vector<std::string> &args)
}

WaitForSingleObject(process_handle.get(), INFINITE);
if (exit_code)
{
DWORD code = STILL_ACTIVE;
if (GetExitCodeProcess(process_handle.get(), &code))
*exit_code = static_cast<int>(code);
else
*exit_code = -1;
}
return output;
}
#else
std::string run_posix_process(const std::vector<std::string> &args)
std::string run_posix_process(const std::vector<std::string> &args, int *exit_code)
{
int pipe_fds[2];
if (pipe(pipe_fds) != 0)
{
if (exit_code)
*exit_code = -1;
return {};
}

pid_t pid = fork();
if (pid < 0)
{
close(pipe_fds[0]);
close(pipe_fds[1]);
if (exit_code)
*exit_code = -1;
return {};
}

Expand Down Expand Up @@ -225,22 +241,90 @@ std::string run_posix_process(const std::vector<std::string> &args)
close(pipe_fds[0]);
int status = 0;
(void)waitpid(pid, &status, 0);
if (exit_code)
{
if (WIFEXITED(status))
*exit_code = WEXITSTATUS(status);
else if (WIFSIGNALED(status))
*exit_code = 128 + WTERMSIG(status);
else
*exit_code = -1;
}
return output;
}
#endif
} // namespace

namespace
{
bool is_ansi_bracket_param_char(unsigned char c)
{
return (c >= '0' && c <= '9') || c == ';';
}

void skip_ansi_brackets(const std::string &text, size_t &index)
{
while (index < text.size() &&
is_ansi_bracket_param_char(static_cast<unsigned char>(text[index])))
++index;
if (index < text.size() && text[index] >= 0x40 && text[index] <= 0x7E)
++index;
}
} // namespace

namespace clrsync::core
{
std::string run_process_capture_output(const std::vector<std::string> &args)
std::string strip_ansi_escapes(const std::string &text)
{
std::string result;
result.reserve(text.size());
for (size_t i = 0; i < text.size();)
{
const unsigned char c = static_cast<unsigned char>(text[i]);
if (c == 0x1B || c == 0x9B)
{
if (c == 0x1B)
++i;
if (i < text.size() && text[i] == '[')
{
++i;
skip_ansi_brackets(text, i);
}
continue;
}
if (i + 1 < text.size() && text[i] == '?' && text[i + 1] == '[')
{
i += 2;
skip_ansi_brackets(text, i);
continue;
}
result.push_back(text[i]);
++i;
}
return result;
}

std::string process_failure_message(const std::string &output, const char *fallback_message)
{
const std::string cleaned = strip_ansi_escapes(output);
if (!cleaned.empty())
return cleaned;
return fallback_message ? fallback_message : "command failed";
}

std::string run_process_capture_output(const std::vector<std::string> &args, int *exit_code)
{
if (args.empty())
{
if (exit_code)
*exit_code = -1;
return {};
}

#ifdef _WIN32
return run_windows_process(args);
return run_windows_process(args, exit_code);
#else
return run_posix_process(args);
return run_posix_process(args, exit_code);
#endif
}
} // namespace clrsync::core
6 changes: 5 additions & 1 deletion src/core/common/process.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@

namespace clrsync::core
{
std::string run_process_capture_output(const std::vector<std::string> &args);
std::string run_process_capture_output(const std::vector<std::string> &args,
int *exit_code = nullptr);
std::string strip_ansi_escapes(const std::string &text);
std::string process_failure_message(const std::string &output,
const char *fallback_message = "command failed");
} // namespace clrsync::core

#endif // CLRSYNC_CORE_COMMON_PROCESS_HPP
2 changes: 1 addition & 1 deletion src/core/common/version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace clrsync::core
{

const std::string GIT_SEMVER = "1.2.1+git.g815f162";
const std::string GIT_SEMVER = "1.2.2+git.g84cff2a";

const std::string version_string();
} // namespace clrsync::core
Expand Down
36 changes: 36 additions & 0 deletions src/core/palette/generator_mappings.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef CLRSYNC_CORE_PALETTE_GENERATOR_MAPPINGS_HPP
#define CLRSYNC_CORE_PALETTE_GENERATOR_MAPPINGS_HPP

#include "core/palette/palette.hpp"

#include <cstddef>
#include <functional>

namespace clrsync::core
{

struct index_color_mapping
{
const char *palette_key;
int source_index;
};

struct key_color_mapping
{
const char *palette_key;
const char *source_key;
};

inline void apply_index_mappings(
palette &pal,
const index_color_mapping *mappings,
std::size_t count,
const std::function<const color &(int index)> &get_by_index)
{
for (std::size_t i = 0; i < count; ++i)
pal.set_color(mappings[i].palette_key, get_by_index(mappings[i].source_index));
}

} // namespace clrsync::core

#endif
Loading
Loading