diff options
| author | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
|---|---|---|
| committer | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
| commit | 76424950e373d3b04ac3dd13019151bfba3e8423 (patch) | |
| tree | 4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/brightness/brightness_linux.c | |
Add the files
Diffstat (limited to 'src/detection/brightness/brightness_linux.c')
| -rw-r--r-- | src/detection/brightness/brightness_linux.c | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/src/detection/brightness/brightness_linux.c b/src/detection/brightness/brightness_linux.c new file mode 100644 index 0000000..e7125f8 --- /dev/null +++ b/src/detection/brightness/brightness_linux.c @@ -0,0 +1,176 @@ +#include "brightness.h" +#include "common/io.h" +#include "common/edidHelper.h" +#include "common/strutil.h" + +#include <dirent.h> +#include <limits.h> + +static const char* detectWithBacklight(FFlist* result) { + // https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-class-backlight + const char* backlightDirPath = "/sys/class/backlight/"; + + DIR* dirp = opendir(backlightDirPath); + if (dirp == NULL) { + return "Failed to open `/sys/class/backlight/`"; + } + + FF_STRBUF_AUTO_DESTROY backlightDir = ffStrbufCreateA(64); + ffStrbufAppendS(&backlightDir, backlightDirPath); + + uint32_t backlightDirLength = backlightDir.length; + + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + + struct dirent* entry; + while ((entry = readdir(dirp)) != NULL) { + if (entry->d_name[0] == '.') { + continue; + } + + ffStrbufAppendS(&backlightDir, entry->d_name); + ffStrbufAppendS(&backlightDir, "/brightness"); + if (ffReadFileBuffer(backlightDir.chars, &buffer)) { + double actualBrightness = ffStrbufToDouble(&buffer, 0); + ffStrbufSubstrBefore(&backlightDir, backlightDirLength); + ffStrbufAppendS(&backlightDir, entry->d_name); + ffStrbufAppendS(&backlightDir, "/max_brightness"); + if (ffReadFileBuffer(backlightDir.chars, &buffer)) { + FFBrightnessResult* brightness = FF_LIST_ADD(FFBrightnessResult, *result); + ffStrbufSubstrBeforeLastC(&backlightDir, '/'); + ffStrbufAppendS(&backlightDir, "/device"); + ffStrbufInitA(&brightness->name, PATH_MAX); + if (realpath(backlightDir.chars, brightness->name.chars)) { + ffStrbufRecalculateLength(&brightness->name); + // if we managed to get edid, use it + ffStrbufAppendS(&brightness->name, "/edid"); + uint8_t edidData[128]; + if (ffReadFileData(brightness->name.chars, ARRAY_SIZE(edidData), edidData) == ARRAY_SIZE(edidData)) { + ffStrbufClear(&brightness->name); + ffEdidGetName(edidData, &brightness->name); + } else { + ffStrbufSubstrBeforeLastC(&brightness->name, '/'); // remove "/edid" + ffStrbufSubstrAfterLastC(&brightness->name, '/'); // try getting DRM connector name + if (ffCharIsDigit(brightness->name.chars[0])) { + // PCI address or some unknown path, give up + ffStrbufSetS(&brightness->name, entry->d_name); + } else { + if (ffStrbufStartsWithS(&brightness->name, "card") && ffCharIsDigit(brightness->name.chars[4])) { + ffStrbufSubstrAfterFirstC(&brightness->name, '-'); + } + } + } + } else { + ffStrbufInitS(&brightness->name, entry->d_name); + } + brightness->max = ffStrbufToDouble(&buffer, 0); + brightness->min = 0; + brightness->current = actualBrightness; + brightness->builtin = true; + } + } + ffStrbufSubstrBefore(&backlightDir, backlightDirLength); + } + + closedir(dirp); + + return NULL; +} + +#ifdef FF_HAVE_DDCUTIL + #include "detection/displayserver/displayserver.h" + #include "common/library.h" + #include "common/mallocHelper.h" + + #include <ddcutil_macros.h> + #include <ddcutil_c_api.h> + + // Try to be compatible with ddcutil 2.0 + #if DDCUTIL_VMAJOR >= 2 +double ddca_set_default_sleep_multiplier(double multiplier); // ddcutil 1.4 + #else +DDCA_Status ddca_init(const char* libopts, int syslog_level, int opts); + #endif + +static const char* detectWithDdcci(FF_A_UNUSED FFBrightnessOptions* options, FFlist* result) { + FF_LIBRARY_LOAD_MESSAGE(libddcutil, "libddcutil" FF_LIBRARY_EXTENSION, 5); + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_get_display_info_list2) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_open_display2) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_get_any_vcp_value_using_explicit_type) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_free_any_vcp_value) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_close_display) + + #ifndef FF_DISABLE_DLOPEN + FF_LIBRARY_LOAD_SYMBOL_LAZY(libddcutil, ddca_init) + if (ffddca_init) { + FF_SUPPRESS_IO(); + // Ref: https://github.com/rockowitz/ddcutil/issues/344 + if (ffddca_init(NULL, -1 /*DDCA_SYSLOG_NOT_SET*/, 1 /*DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE*/) < 0) { + return "ddca_init() failed"; + } + } else { + FF_LIBRARY_LOAD_SYMBOL_LAZY(libddcutil, ddca_set_default_sleep_multiplier); + if (ffddca_set_default_sleep_multiplier) { + ffddca_set_default_sleep_multiplier(options->ddcciSleep / 40.0); + } + + libddcutil = NULL; // Don't dlclose libddcutil. See https://github.com/rockowitz/ddcutil/issues/330 + } + #else + #if DDCUTIL_VMAJOR >= 2 + if (ddca_init(NULL, -1 /*DDCA_SYSLOG_NOT_SET*/, 1 /*DDCA_INIT_OPTIONS_DISABLE_CONFIG_FILE*/) < 0) { + return "ddca_init() failed"; + } + #else + ddca_set_default_sleep_multiplier(options->ddcciSleep / 40.0); + #endif + #endif + + FF_AUTO_FREE DDCA_Display_Info_List* infoList = NULL; + if (ffddca_get_display_info_list2(false, &infoList) < 0) { + return "ddca_get_display_info_list2(false, &infoList) failed"; + } + + if (infoList->ct == 0) { + return "No DDC/CI compatible displays found"; + } + + for (int index = 0; index < infoList->ct; ++index) { + const DDCA_Display_Info* display = &infoList->info[index]; + + DDCA_Display_Handle handle; + if (ffddca_open_display2(display->dref, false, &handle) >= 0) { + DDCA_Any_Vcp_Value* vcpValue = NULL; + if (ffddca_get_any_vcp_value_using_explicit_type(handle, 0x10 /*brightness*/, DDCA_NON_TABLE_VCP_VALUE, &vcpValue) >= 0) { + assert(vcpValue->value_type == DDCA_NON_TABLE_VCP_VALUE); + int current = VALREC_CUR_VAL(vcpValue), max = VALREC_MAX_VAL(vcpValue); + ffddca_free_any_vcp_value(vcpValue); + + FFBrightnessResult* brightness = FF_LIST_ADD(FFBrightnessResult, *result); + brightness->max = max; + brightness->min = 0; + brightness->current = current; + ffStrbufInitS(&brightness->name, display->model_name); + } + ffddca_close_display(handle); + } + } + + return NULL; +} +#endif + +const char* ffDetectBrightness(FF_A_UNUSED FFBrightnessOptions* options, FFlist* result) { + detectWithBacklight(result); + +#ifdef FF_HAVE_DDCUTIL + if (options->ddcciSleep != FF_BRIGHTNESS_DDCCI_SLEEP_SKIP) { + const FFDisplayServerResult* displayServer = ffConnectDisplayServer(); + if (result->length < displayServer->displays.length) { + detectWithDdcci(options, result); + } + } +#endif + + return NULL; +} |