summaryrefslogtreecommitdiffstats
path: root/src/detection/brightness/brightness_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/brightness/brightness_linux.c')
-rw-r--r--src/detection/brightness/brightness_linux.c176
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;
+}