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
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ add_subdirectory(src/core)
add_subdirectory(src/cli)
add_subdirectory(src/gui)

if(WIN32)
set(CLRSYNC_WINDOWS_RC ${CMAKE_SOURCE_DIR}/assets/icons/clrsync.rc)
target_sources(clrsync_cli PRIVATE ${CLRSYNC_WINDOWS_RC})
target_sources(clrsync_gui PRIVATE ${CLRSYNC_WINDOWS_RC})
endif()

include(Install)
include(Packaging)

Expand Down
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Nix Flake](https://img.shields.io/badge/Nix-Flake-blue.svg)](https://nixos.wiki/wiki/Flakes)
<p align="center">
<img src="assets/icons/web/logo-256.png" alt="clrsync logo" width="128" height="128">
</p>

# clrsync
<h1 align="center">clrsync</h1>

A theme management tool for synchronizing color schemes across multiple applications. clrsync allows to define color palettes once and apply them consistently to all configurable applications.
<p align="center">
A theme management tool for synchronizing color schemes across multiple applications. clrsync allows to define color palettes once and apply them consistently to all configurable applications.
</p>

<p align="center">
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
<a href="https://nixos.wiki/wiki/Flakes"><img src="https://img.shields.io/badge/Nix-Flake-blue.svg" alt="Nix Flake"></a>
</p>

![Preview](assets/screenshot.png)

Expand Down
Binary file added assets/icons/clrsync-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/clrsync.icns
Binary file not shown.
Binary file added assets/icons/clrsync.ico
Binary file not shown.
2 changes: 2 additions & 0 deletions assets/icons/clrsync.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Application icon (resource ID 1 = primary icon in Explorer and taskbar)
1 ICON "clrsync.ico"
12 changes: 12 additions & 0 deletions assets/icons/clrsync.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/favicon.ico
Binary file not shown.
Binary file added assets/icons/png/clrsync-1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/png/clrsync-128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/png/clrsync-16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/png/clrsync-24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/png/clrsync-256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/png/clrsync-32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/png/clrsync-48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/png/clrsync-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/png/clrsync-64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/web/logo-128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/web/logo-256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icons/web/logo-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 21 additions & 1 deletion src/cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,23 @@ int main(int argc, char *argv[])
clrsync::core::palette pal;
if (generator_name == "hellwal")
{
clrsync::core::hellwal_generator gen;
if (!gen.supports_current_system())
{
std::cerr << "Error: hellwal 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::hellwal_generator gen;
clrsync::core::hellwal_generator::options opts{};

if (program.is_used("--hellwal-neon"))
Expand Down Expand Up @@ -246,6 +256,16 @@ int main(int argc, char *argv[])
else if (generator_name == "matugen")
{
clrsync::core::matugen_generator gen;
if (!gen.supports_current_system())
{
std::cerr << "Error: matugen is not supported on "
<< clrsync::core::generator::system_name(
clrsync::core::generator::current_system())
<< ". Supported systems: " << gen.supported_systems_description()
<< std::endl;
return 1;
}

clrsync::core::matugen_generator::options opts{};

try
Expand Down
5 changes: 3 additions & 2 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ set(CORE_SOURCES
palette/matugen_generator.cpp
io/toml_file.cpp
config/config.cpp
common/utils.cpp
common/version.cpp
common/process.cpp
common/utils.cpp
common/version.cpp
theme/theme_template.cpp
)

Expand Down
246 changes: 246 additions & 0 deletions src/core/common/process.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
#include "process.hpp"

#include <array>
#include <cstdio>
#include <vector>

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif

namespace
{
#ifdef _WIN32
struct handle_guard
{
HANDLE handle = nullptr;

explicit handle_guard(HANDLE value) : handle(value) {}

handle_guard(const handle_guard &) = delete;
handle_guard &operator=(const handle_guard &) = delete;
handle_guard(handle_guard &&other) noexcept : handle(other.handle) { other.handle = nullptr; }
handle_guard &operator=(handle_guard &&other) noexcept
{
if (this != &other)
{
reset(other.release());
}
return *this;
}

~handle_guard()
{
if (handle)
CloseHandle(handle);
}

HANDLE get() const { return handle; }
HANDLE release()
{
HANDLE value = handle;
handle = nullptr;
return value;
}
void reset(HANDLE value = nullptr)
{
if (handle)
CloseHandle(handle);
handle = value;
}
};

std::wstring utf8_to_wide(const std::string &value)
{
if (value.empty())
return {};

const int size = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, nullptr, 0);
if (size <= 0)
return {};

std::wstring result(static_cast<size_t>(size), L'\0');
MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, result.data(), size);
result.pop_back();
return result;
}

std::wstring quote_windows_arg(const std::wstring &arg)
{
if (arg.empty())
return L"\"\"";

if (arg.find_first_of(L" \t\n\v\"") == std::wstring::npos)
return arg;

std::wstring quoted;
quoted.reserve(arg.size() + 2);
quoted.push_back(L'"');

size_t backslashes = 0;
for (wchar_t ch : arg)
{
if (ch == L'\\')
{
backslashes++;
continue;
}

if (ch == L'"')
{
quoted.append(backslashes * 2 + 1, L'\\');
quoted.push_back(L'"');
backslashes = 0;
continue;
}

if (backslashes > 0)
{
quoted.append(backslashes, L'\\');
backslashes = 0;
}

quoted.push_back(ch);
}

if (backslashes > 0)
quoted.append(backslashes * 2, L'\\');

quoted.push_back(L'"');
return quoted;
}

std::wstring build_windows_command_line(const std::vector<std::string> &args)
{
std::wstring command_line;
for (size_t i = 0; i < args.size(); i++)
{
if (i > 0)
command_line.push_back(L' ');
command_line += quote_windows_arg(utf8_to_wide(args[i]));
}
return command_line;
}

std::string run_windows_process(const std::vector<std::string> &args)
{
SECURITY_ATTRIBUTES sa{};
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;

HANDLE read_pipe_raw = nullptr;
HANDLE write_pipe_raw = nullptr;
if (!CreatePipe(&read_pipe_raw, &write_pipe_raw, &sa, 0))
return {};

handle_guard read_pipe(read_pipe_raw);
handle_guard write_pipe(write_pipe_raw);

if (!SetHandleInformation(read_pipe.get(), HANDLE_FLAG_INHERIT, 0))
return {};

STARTUPINFOW startup{};
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESTDHANDLES;
startup.hStdOutput = write_pipe.get();
startup.hStdError = write_pipe.get();
startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);

PROCESS_INFORMATION process_info{};
std::wstring command_line = build_windows_command_line(args);
std::vector<wchar_t> mutable_command_line(command_line.begin(), command_line.end());
mutable_command_line.push_back(L'\0');

if (!CreateProcessW(nullptr, mutable_command_line.data(), nullptr, nullptr, TRUE,
CREATE_NO_WINDOW, nullptr, nullptr, &startup, &process_info))
{
return {};
}

handle_guard process_handle(process_info.hProcess);
handle_guard thread_handle(process_info.hThread);
write_pipe.reset();

std::string output;
std::array<char, 4096> buffer{};
DWORD bytes_read = 0;
while (ReadFile(read_pipe.get(), buffer.data(), static_cast<DWORD>(buffer.size()),
&bytes_read, nullptr) &&
bytes_read > 0)
{
output.append(buffer.data(), buffer.data() + bytes_read);
}

WaitForSingleObject(process_handle.get(), INFINITE);
return output;
}
#else
std::string run_posix_process(const std::vector<std::string> &args)
{
int pipe_fds[2];
if (pipe(pipe_fds) != 0)
return {};

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

if (pid == 0)
{
dup2(pipe_fds[1], STDOUT_FILENO);
dup2(pipe_fds[1], STDERR_FILENO);

close(pipe_fds[0]);
close(pipe_fds[1]);

std::vector<char *> argv;
argv.reserve(args.size() + 1);
for (const auto &arg : args)
argv.push_back(const_cast<char *>(arg.c_str()));
argv.push_back(nullptr);

execvp(argv[0], argv.data());
_exit(127);
}

close(pipe_fds[1]);

std::string output;
std::array<char, 4096> buffer{};
ssize_t bytes_read = 0;
while ((bytes_read = read(pipe_fds[0], buffer.data(), buffer.size())) > 0)
{
output.append(buffer.data(), static_cast<size_t>(bytes_read));
}

close(pipe_fds[0]);
int status = 0;
(void)waitpid(pid, &status, 0);
return output;
}
#endif
} // namespace

namespace clrsync::core
{
std::string run_process_capture_output(const std::vector<std::string> &args)
{
if (args.empty())
return {};

#ifdef _WIN32
return run_windows_process(args);
#else
return run_posix_process(args);
#endif
}
} // namespace clrsync::core
12 changes: 12 additions & 0 deletions src/core/common/process.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef CLRSYNC_CORE_COMMON_PROCESS_HPP
#define CLRSYNC_CORE_COMMON_PROCESS_HPP

#include <string>
#include <vector>

namespace clrsync::core
{
std::string run_process_capture_output(const std::vector<std::string> &args);
} // 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.1.2+git.g9d4cb72";
const std::string GIT_SEMVER = "1.2.1+git.g7280102";

const std::string version_string();
} // namespace clrsync::core
Expand Down
Loading
Loading