Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First working prototype #3

Merged
merged 8 commits into from
Apr 20, 2024
35 changes: 23 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ project(
LANGUAGES CXX)
include(CTest)

set(CMAKE_C_COMPILER "/usr/bin/gcc")
set(CMAKE_CPP_COMPILER "/usr/bin/c++")

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
Expand All @@ -22,24 +19,38 @@ pkg_check_modules(EASYLOGGINGPP REQUIRED easyloggingpp)
set(FILE_WATCHER_SRC ${SRC_DIR}/FileWatcher.cpp)
set(COLORD_HANDLER_SRC ${SRC_DIR}/ColordHandler.cpp)

add_library(Easyloggigpp)
target_sources(Easyloggigpp
PUBLIC ${EASYLOGGINGPP_INCLUDE_DIRS}/easylogging++.cc)
target_include_directories(Easyloggigpp PUBLIC ${EASYLOGGINGPP_INCLUDE_DIRS})

add_library(file_watcher)
target_sources(file_watcher PRIVATE ${FILE_WATCHER_SRC})
target_include_directories(file_watcher PUBLIC ${INCLUDE_DIR}
${EASYLOGGINGPP_INCLUDE_DIRS})
target_include_directories(file_watcher PUBLIC ${INCLUDE_DIR})
target_link_libraries(file_watcher Easyloggigpp)

add_library(colord_handler)
target_sources(colord_handler PRIVATE ${COLORD_HANDLER_SRC})
target_include_directories(
colord_handler PUBLIC ${INCLUDE_DIR} ${COLORD_INCLUDE_DIRS}
${LCMS2_INCLUDE_DIRS}${EASYLOGGINGPP_INCLUDE_DIRS})
target_link_libraries(colord_handler ${COLORD_LIBRARIES} ${LCMS2_LIBRARIES})
${LCMS2_INCLUDE_DIRS})
target_link_libraries(colord_handler ${COLORD_LIBRARIES} ${LCMS2_LIBRARIES}
Easyloggigpp)

add_executable(test_file_watcher)
target_sources(test_file_watcher PRIVATE tests/test_file_watcher.cpp)
target_link_libraries(test_file_watcher file_watcher)
target_link_libraries(test_file_watcher file_watcher Easyloggigpp)
target_include_directories(test_file_watcher PUBLIC ${INCLUDE_DIR})
add_test(NAME test_file_watcher COMMAND test_file_watcher)

add_executable(poc_test tests/test.cpp)
target_link_libraries(poc_test ${COLORD_LIBRARIES} ${LCMS2_LIBRARIES})
target_include_directories(poc_test PUBLIC ${COLORD_INCLUDE_DIRS}
${LCMS2_INCLUDE_DIRS})
add_executable(colord_brightness)
target_sources(colord_brightness PRIVATE ${SRC_DIR}/colord_brightness.cpp)
target_include_directories(
colord_brightness PUBLIC ${INCLUDE_DIR} ${COLORD_INCLUDE_DIRS}
${LCMS2_INCLUDE_DIRS})
target_link_libraries(colord_brightness file_watcher colord_handler
${COLORD_LIBRARIES} ${LCMS2_LIBRARIES} Easyloggigpp)

# add_executable(poc_test tests/test.cpp) target_link_libraries(poc_test
# ${COLORD_LIBRARIES} ${LCMS2_LIBRARIES}) target_include_directories(poc_test
# PUBLIC ${COLORD_INCLUDE_DIRS} ${LCMS2_INCLUDE_DIRS})
26 changes: 18 additions & 8 deletions include/ColordHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <colord.h>
#include <filesystem>
#include <lcms2.h>
#include <optional>

/*! \class ColordHandler
* \brief wrapper for setting the brightness colord and managing the file
Expand All @@ -20,19 +21,28 @@ class ColordHandler {
* \param path_for_icc path used for creating the mem_fd used for the icc
* profiles
*
* \throws std::runtime_error if the colord_server is not running
* \throws std::runtime_error::system_error if the fd couldn't get created
* \throws std::runtime_error if the colord_server is not running
* \throws std::runtime_error::system_error if the fd couldn't get created
*/
ColordHandler(std::filesystem::path path_for_icc) noexcept(false);
bool setDefaultProfile();
bool setIccFromCmsProfile(cmsHPROFILE profile);
// bool setDefaultProfile(std::filesystem::path edid_file_path, uint
// display_device_id = 0);
bool setIccFromCmsProfile(cmsHPROFILE profile, uint display_device_id = 0);
bool cancelCurrentAction();
virtual ~ColordHandler();

protected:
CdDevice getDisplayDevice();
int mem_fd_; /*!< filedescriptor for the icc file */
CdClient cd_client_;
GCancellable cancle_request_;
std::optional<CdDevice *> getDisplayDevice(uint dev_num);
bool makeProfileFromIccDefault(CdIcc *icc_file, uint display_device_id);
std::optional<CdIcc> createIccFromEdid(std::filesystem::path edid_file_path);
bool resetMemFd();

int _mem_fd; /*!< file descriptor for the icc file */
std::filesystem::path _mem_fd_path;
std::filesystem::path _icc_path;
std::shared_ptr<CdClient> _cd_client;
std::shared_ptr<GCancellable>
_cancel_request; /*!< for future cancellation, currently unused*/
};

#endif /* end of include guard: COLORDHANDLER_H */
16 changes: 14 additions & 2 deletions include/FileWatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ enum class file_watch_error {
*/
class FileWatcher {
public:
/*! \brief Constructor initialises the inotify-file-descriptor and signal
* handling
*
* initialises shared_ptr and inotify-context and adds a signal handler for
* SIGINT used to stop the thread, when its started later with
* FileWatcher::startWatching()
*
* \param file file to watch for changes
* \throws std::system_error if the inotify-file-descriptor couldn't get
* initialsied
* \throws std::runtime_error if the path given does not exist
*/
FileWatcher(std::filesystem::path file) noexcept(false);
file_watch_error startWatching();
file_watch_error startWatching(std::filesystem::path file);
Expand All @@ -60,15 +72,15 @@ class FileWatcher {
std::shared_ptr<std::atomic_bool> _watching;
std::shared_ptr<std::atomic_bool> _updated;
std::shared_ptr<std::string> _changed_file_content;
std::shared_ptr<std::mutex> _cv_mut; /*<! mutex for the lock in the condition_variable */
std::shared_ptr<std::mutex>
_cv_mut; /*<! mutex for the lock in the condition_variable */
std::shared_ptr<std::condition_variable>
_notify_waiter_cv; /*!< for notification of the wait_and_get() - caller */
int _inotify_fd; /*!< file descriptor for the inotify instance used to detect
file changes */
int _watch_fd; /*!< file descriptor for the watch instance for
_file_to_watch*/
std::thread _watching_thread;

};

#endif /* end of include guard: FILEWATCHER_H */
212 changes: 203 additions & 9 deletions src/ColordHandler.cpp
Original file line number Diff line number Diff line change
@@ -1,33 +1,227 @@
#include "ColordHandler.h"
#include <cstdio>
#include <easylogging++.h>
#include <filesystem>
#include <lcms2.h>
#include <memory>
#include <optional>
#include <stdexcept>
#include <sys/mman.h>
#include <system_error>

/*! TODO: is it possible with only one fd??
* \todo is it possible with only one fd??
*/
bool ColordHandler::resetMemFd() {
auto file = fopen(_mem_fd_path.c_str(), "w");
if (!file) {
// make new fd
_mem_fd = memfd_create(_icc_path.c_str(), 0);
std::stringstream sa;
sa << "/proc/" << getpid() << "/fd/" << _mem_fd;
_mem_fd_path = std::filesystem::path(sa.str());
LOG(DEBUG) << "New Filedescriptor path: " << _mem_fd_path;
}
// fclose(file);
return _mem_fd < 0;
}

ColordHandler::ColordHandler(std::filesystem::path path_for_icc)
: cancle_request_(*g_cancellable_new()), cd_client_(*cd_client_new()) {
: _cancel_request(g_cancellable_new()), _cd_client(cd_client_new()),
_icc_path(path_for_icc) {

// connect client
if (cd_client_get_has_server(&cd_client_)) {
std::unique_ptr<GError *> error;
if (!cd_client_connect_sync(&cd_client_, &cancle_request_, error.get())) {
if (cd_client_get_has_server(_cd_client.get())) {
GError *error = NULL;
if (!cd_client_connect_sync(_cd_client.get(), _cancel_request.get(),
&error)) {
// client not connected, can be fixed
LOG(WARNING) << "Couldn't connect to Colord-Server on init!";
LOG(WARNING) << "Couldn't connect colord client on init! Gerror: "
<< error->message;
}
} else {
// Colord-Server not running, object would be useless
throw std::runtime_error("Colord-Server is not running!");
}

// open memfd, set close on exit (e.g.: closes fd, if process crashes)
int fd_or_failed = memfd_create(path_for_icc.c_str(), MFD_CLOEXEC);
if (fd_or_failed < 0) {
_mem_fd = memfd_create(path_for_icc.c_str(), 0);
if (_mem_fd < 0) {
// file Couldn't get created, object Couldn't write the profile
throw std::system_error(errno, std::system_category());
;
} else {
mem_fd_ = fd_or_failed;
std::stringstream sa;
sa << "/proc/" << getpid() << "/fd/" << _mem_fd;
_mem_fd_path = std::filesystem::path(sa.str());
LOG(DEBUG) << "Filedescriptor path: " << _mem_fd_path;
}
}

std::optional<CdDevice *> ColordHandler::getDisplayDevice(uint dev_num) {
GError *error = NULL;
GPtrArray *devices = cd_client_get_devices_by_kind_sync(
_cd_client.get(), CD_DEVICE_KIND_DISPLAY, _cancel_request.get(), &error);
gpointer dev = devices->pdata[dev_num];
if (dev)
return static_cast<CdDevice *>(dev);
LOG(ERROR) << "No Display device found! Gerror: " << error->message;
return std::nullopt;
}

/*! TODO: better error propagation ??
* \todo better error propagation ??
*/
bool ColordHandler::setIccFromCmsProfile(cmsHPROFILE profile,
uint display_device_id) {
LOG_IF(resetMemFd(), WARNING) << "Couldn't clear file deskriptor content.";

if (!cmsMD5computeID(profile))
LOG(WARNING) << "Couldn't recompute hash for lcms2 color profile!";

if (!cmsSaveProfileToFile(profile, _mem_fd_path.c_str())) {
LOG(ERROR) << "Lcms2-profile Couldn't get saved into mem_fd!";
return false;
}

CdIcc *icc_file;
{
CdIcc *icc = cd_icc_new();
GError *error = NULL;
if (!cd_icc_load_fd(icc, _mem_fd, CD_ICC_LOAD_FLAGS_ALL, &error)) {
LOG(ERROR) << "CdIcc profile couldn't get loaded from mem_fd! Gerror: "
<< error->message;
return false;
}
cd_icc_set_filename(icc, _mem_fd_path.c_str());
icc_file = icc;
}

// LOG(DEBUG) << "Icc-content: \n" << cd_icc_to_string(icc_file);
return makeProfileFromIccDefault(icc_file, display_device_id);
}

bool ColordHandler::makeProfileFromIccDefault(CdIcc *icc_file,
uint display_device_id) {
CdProfile *icc_profile;
{
GError *error = NULL;
CdProfile *tmp_profile = cd_client_create_profile_for_icc_sync(
_cd_client.get(), icc_file, CdObjectScope::CD_OBJECT_SCOPE_TEMP,
_cancel_request.get(), &error);
if (!tmp_profile) {
LOG(ERROR) << "CdClient couldn't create a Profile from icc file, '"
<< tmp_profile << "'! Gerror: " << error->message;
return false;
} else {
icc_profile = tmp_profile;
}
}

if (!cd_client_get_connected(_cd_client.get())) {
GError *error = NULL;
if (!cd_client_connect_sync(_cd_client.get(), _cancel_request.get(),
&error)) {
// client not connected
LOG(ERROR)
<< "Couldn't connect Colord client on setting a profile! Gerror: "
<< error->message;
return false;
}
}

std::optional<CdDevice *> cd_display = getDisplayDevice(display_device_id);
if (!cd_display.has_value())
return false;
CdDevice *display = cd_display.value();

{
GError *error = NULL;
if (!cd_device_connect_sync(display, _cancel_request.get(), &error)) {
LOG(ERROR) << "Couldn't connect to CdDevice! Gerror: " << error->message;
return false;
}
}

{
GError *error = NULL;
if (!cd_device_add_profile_sync(display, CD_DEVICE_RELATION_SOFT,
icc_profile, _cancel_request.get(),
&error)) {
LOG(ERROR) << "Couldn't add Profile to device! Gerror: "
<< error->message;
return false;
}
}

GError *error = NULL;
auto set_profile = cd_device_make_profile_default_sync(
display, icc_profile, _cancel_request.get(), &error);
LOG_IF(!set_profile, ERROR)
<< "Couldn't make profile default for device! Gerror: " << error->message;
return set_profile;
}

std::string print_color(const CdColorYxy *color) {
std::stringstream ss;
if (color != nullptr) {
ss << " Y: " << color->Y << " x:" << color->x << " y:" << color->y;
} else {
ss << "NULLPTR";
}
return ss.str();
}

std::optional<CdIcc>
ColordHandler::createIccFromEdid(std::filesystem::path edid_file_path) {
CdEdid *monitor = cd_edid_new();
std::ifstream edid_file("/sys/class/drm/card1-eDP-1/edid",
std::ios::in | std::ios::binary);

char bytes[128];
edid_file.read(&bytes[0], 128);
{
GError *error = NULL;
gboolean parsed =
cd_edid_parse(monitor, g_bytes_new(bytes, sizeof(bytes)), &error);
if (!parsed) {
LOG(ERROR) << "Edid couldn't ger parsed! Gerror: " << error->message;
return std::nullopt;
}
}
const CdColorYxy *m_red = cd_edid_get_red(monitor);
const CdColorYxy *m_blue = cd_edid_get_blue(monitor);
const CdColorYxy *m_green = cd_edid_get_green(monitor);
const CdColorYxy *m_white = cd_edid_get_white(monitor);
gdouble m_gamma = cd_edid_get_gamma(monitor);

LOG(INFO) << "GAMMA: " << m_gamma;
LOG(INFO) << "RED: " << print_color(m_red);
LOG(INFO) << "BLUE: " << print_color(m_blue);
LOG(INFO) << "GREEN: " << print_color(m_green);
LOG(INFO) << "WHITE: " << print_color(m_white);

CdIcc icc = *cd_icc_new();
{
GError *error = NULL;
gboolean created = cd_icc_create_from_edid(&icc, m_gamma, m_red, m_green,
m_blue, m_white, &error);
if (!created) {
LOG(ERROR) << "Couldn't create icc file form edid values! Gerror: "
<< error->message;
}
}

return icc;
}

bool ColordHandler::cancelCurrentAction() {
g_cancellable_cancel(_cancel_request.get());
return g_cancellable_is_cancelled(_cancel_request.get());
}

ColordHandler::~ColordHandler() {
if (!g_cancellable_is_cancelled(_cancel_request.get())) {
cancelCurrentAction();
}
close(_mem_fd);
}
Loading
Loading