light.c 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  1. #include "light.h"
  2. #include <libgen.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #include <getopt.h>
  8. #include <sys/stat.h>
  9. #include <errno.h>
  10. /* Sets default values for the configuration */
  11. static void light_defaults(void)
  12. {
  13. ctx.ctrl = LIGHT_AUTO;
  14. memset(&ctx.ctrl_name, '\0', NAME_MAX + 1);
  15. ctx.cmd = LIGHT_GET;
  16. ctx.val_mode = LIGHT_PERCENT;
  17. ctx.val_raw = 0;
  18. ctx.val_percent = 0.0;
  19. ctx.target = LIGHT_BACKLIGHT;
  20. ctx.field = LIGHT_BRIGHTNESS;
  21. ctx.has_cached_brightness_max = false;
  22. ctx.cached_brightness_max = 0;
  23. light_loglevel = 0;
  24. }
  25. static bool light_check_ops(void)
  26. {
  27. bool valid = true;
  28. light_cmd_t op = ctx.cmd;
  29. /* Nothing to check if we just print info */
  30. if (op == LIGHT_PRINT_HELP || op == LIGHT_PRINT_VERSION || op == LIGHT_LIST_CTRL)
  31. return true;
  32. switch (ctx.field) {
  33. case LIGHT_BRIGHTNESS:
  34. if (op != LIGHT_GET && op != LIGHT_SET &&
  35. op != LIGHT_ADD && op != LIGHT_SUB && op != LIGHT_SAVE && op != LIGHT_RESTORE) {
  36. valid = false;
  37. fprintf(stderr,
  38. "Wrong operation specified for brightness. You can use only -G -S -A or -U\n\n");
  39. }
  40. break;
  41. case LIGHT_MAX_BRIGHTNESS:
  42. if (op != LIGHT_GET) {
  43. valid = false;
  44. fprintf(stderr, "Wrong operation specified for max brightness. You can only use -G\n\n");
  45. }
  46. break;
  47. case LIGHT_MIN_CAP:
  48. if (op != LIGHT_GET && op != LIGHT_SET) {
  49. valid = false;
  50. fprintf(stderr, "Wrong operation specified for min cap. You can only use -G or -S\n\n");
  51. }
  52. default:
  53. break;
  54. }
  55. return valid;
  56. }
  57. static bool light_check_ctrl(char const *controller)
  58. {
  59. if (!controller) {
  60. LIGHT_WARN("Invalid or missing controller name");
  61. return false;
  62. }
  63. if (strlen(controller) > NAME_MAX) {
  64. LIGHT_WARN("Too long controller name, %s", controller);
  65. return false;
  66. }
  67. return true;
  68. }
  69. /* Prints help regardless of verbosity level */
  70. static void light_usage(void)
  71. {
  72. printf("Usage:\n"
  73. " light [OPTIONS] <COMMAND> [VALUE]\n"
  74. "\n"
  75. "Commands:\n"
  76. " -A VAL Add value\n"
  77. " -G Get (read) value, default command\n"
  78. " -H, -h Show this help and exit\n"
  79. " -I Restore brightness\n"
  80. " -L List available controllers\n"
  81. " -O Save brightness\n"
  82. " -S VAL Set (write) value\n"
  83. " -U VAL Subtract value\n"
  84. " -V Show program version and exit\n"
  85. "\n"
  86. "Options:\n"
  87. " -a Automatic controller selection, default\n"
  88. " -b Brightness, default\n"
  89. " -c Act on minimum cap, only possible to read and set\n"
  90. " -k Act on keyboard backlight\n"
  91. " -l Act on screen backlight, default\n"
  92. " -m Maximum brightness, only possible to read\n"
  93. " -p Interpret input, and output, values in percent, default\n"
  94. " -r Interpret input, and outpot, values in raw mode\n"
  95. " -s ARG Specify controller to use, use -L and -Lk to list available\n"
  96. " -v ARG Verbosity level:\n"
  97. " 0: Values only, default\n"
  98. " 1: Values, Errors.\n"
  99. " 2: Values, Errors, Warnings.\n"
  100. " 3: Values, Errors, Warnings, Notices.\n"
  101. "\n");
  102. printf("Copyright (C) %s %s\n", LIGHT_YEAR, LIGHT_AUTHOR);
  103. printf("This is free software, see the source for copying conditions. There is NO\n"
  104. "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\n"
  105. "\n");
  106. }
  107. static bool light_parse_args(int argc, char **argv)
  108. {
  109. bool cmd_set = false;
  110. bool target_set = false;
  111. bool field_set = false;
  112. bool ctrl_set = false;
  113. bool val_set = false;
  114. int loglevel;
  115. int val;
  116. while ((val = getopt(argc, argv, "HhVGSAULIObmclkas:prv:")) != -1) {
  117. switch (val) {
  118. /* -- Operations -- */
  119. case 'H':
  120. case 'h':
  121. ASSERT_CMDSET();
  122. ctx.cmd = LIGHT_PRINT_HELP;
  123. break;
  124. case 'V':
  125. ASSERT_CMDSET();
  126. ctx.cmd = LIGHT_PRINT_VERSION;
  127. break;
  128. case 'G':
  129. ASSERT_CMDSET();
  130. ctx.cmd = LIGHT_GET;
  131. break;
  132. case 'S':
  133. ASSERT_CMDSET();
  134. ctx.cmd = LIGHT_SET;
  135. break;
  136. case 'A':
  137. ASSERT_CMDSET();
  138. ctx.cmd = LIGHT_ADD;
  139. break;
  140. case 'U':
  141. ASSERT_CMDSET();
  142. ctx.cmd = LIGHT_SUB;
  143. break;
  144. case 'L':
  145. ASSERT_CMDSET();
  146. ctx.cmd = LIGHT_LIST_CTRL;
  147. break;
  148. case 'I':
  149. ASSERT_CMDSET();
  150. ctx.cmd = LIGHT_RESTORE;
  151. break;
  152. case 'O':
  153. ASSERT_CMDSET();
  154. ctx.cmd = LIGHT_SAVE;
  155. break;
  156. /* -- Targets -- */
  157. case 'l':
  158. ASSERT_TARGETSET();
  159. ctx.target = LIGHT_BACKLIGHT;
  160. break;
  161. case 'k':
  162. ASSERT_TARGETSET();
  163. ctx.target = LIGHT_KEYBOARD;
  164. break;
  165. /* -- Fields -- */
  166. case 'b':
  167. ASSERT_FIELDSET();
  168. ctx.field = LIGHT_BRIGHTNESS;
  169. break;
  170. case 'm':
  171. ASSERT_FIELDSET();
  172. ctx.field = LIGHT_MAX_BRIGHTNESS;
  173. break;
  174. case 'c':
  175. ASSERT_FIELDSET();
  176. ctx.field = LIGHT_MIN_CAP;
  177. break;
  178. case 'a':
  179. ASSERT_CTRLSET();
  180. ctx.ctrl = LIGHT_AUTO;
  181. break;;
  182. case 's':
  183. ASSERT_CTRLSET();
  184. ctx.ctrl = LIGHT_SPECIFY;
  185. if (!light_check_ctrl(optarg))
  186. return false;
  187. snprintf(ctx.ctrl_name, sizeof(ctx.ctrl_name), "%s", optarg);
  188. break;
  189. case 'p':
  190. ASSERT_VALSET();
  191. ctx.val_mode = LIGHT_PERCENT;
  192. break;
  193. case 'r':
  194. ASSERT_VALSET();
  195. ctx.val_mode = LIGHT_RAW;
  196. break;
  197. /* -- Other -- */
  198. case 'v':
  199. if (sscanf(optarg, "%i", &loglevel) != 1) {
  200. fprintf(stderr, "-v ARG is not recognizable.\n\n");
  201. light_usage();
  202. return false;
  203. }
  204. if (loglevel < 0 || loglevel > 3) {
  205. fprintf(stderr, "-v ARG must be between 0 and 3.\n\n");
  206. light_usage();
  207. return false;
  208. }
  209. light_loglevel = (light_loglevel_t)loglevel;
  210. break;
  211. }
  212. }
  213. if (!light_check_ops()) {
  214. light_usage();
  215. return false;
  216. }
  217. /* If we need a <value> (for writing), make sure we have it! */
  218. if (ctx.cmd == LIGHT_SET ||
  219. ctx.cmd == LIGHT_ADD || ctx.cmd == LIGHT_SUB) {
  220. if (argc - optind != 1) {
  221. fprintf(stderr, "Light needs an argument for <value>.\n\n");
  222. light_usage();
  223. return false;
  224. }
  225. if (ctx.val_mode == LIGHT_PERCENT) {
  226. if (sscanf(argv[optind], "%lf", &ctx.val_percent) != 1) {
  227. fprintf(stderr, "<value> is not specified in a recognizable format.\n\n");
  228. light_usage();
  229. return false;
  230. }
  231. ctx.val_percent = light_percent_clamp(ctx.val_percent);
  232. } else {
  233. if (sscanf(argv[optind], "%lu", &ctx.val_raw) != 1) {
  234. fprintf(stderr, "<value> is not specified in a recognizable format.\n\n");
  235. light_usage();
  236. return false;
  237. }
  238. }
  239. }
  240. return true;
  241. }
  242. static int mkpath(char *dir, mode_t mode)
  243. {
  244. struct stat sb;
  245. if (!dir) {
  246. errno = EINVAL;
  247. return -1;
  248. }
  249. if (!stat(dir, &sb))
  250. return 0;
  251. mkpath(dirname(strdupa(dir)), mode);
  252. return mkdir(dir, mode);
  253. }
  254. bool light_initialize(int argc, char **argv)
  255. {
  256. light_cmd_t mode;
  257. int rc;
  258. /* Classic SUID root mode or new user based cache files */
  259. if (geteuid() == 0)
  260. snprintf(ctx.prefix, sizeof(ctx.prefix), "%s", "/etc/light");
  261. else
  262. snprintf(ctx.prefix, sizeof(ctx.prefix), "%s/.cache/light", getenv("HOME"));
  263. light_defaults();
  264. if (!light_parse_args(argc, argv)) {
  265. LIGHT_ERR("could not parse arguments");
  266. return false;
  267. }
  268. /* Just return true for operation modes that do not need initialization */
  269. mode = ctx.cmd;
  270. if (mode == LIGHT_PRINT_HELP || mode == LIGHT_PRINT_VERSION || mode == LIGHT_LIST_CTRL)
  271. return true;
  272. /* Make sure we have a valid cache directory for all files */
  273. if (mode == LIGHT_SAVE || (mode == LIGHT_SET && ctx.field == LIGHT_MIN_CAP)) {
  274. const char *dirs[5] = {
  275. "/mincap/kbd", "/save/kbd", NULL
  276. };
  277. char path[strlen(ctx.prefix) + 20];
  278. int i;
  279. for (i = 0; dirs[i]; i++) {
  280. snprintf(path, sizeof(path), "%s%s", ctx.prefix, dirs[i]);
  281. rc = mkpath(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  282. if (rc && errno != EEXIST) {
  283. LIGHT_ERR("'%s' does not exist and could not be created,"
  284. " make sure this application is run as root.", path);
  285. return false;
  286. }
  287. }
  288. }
  289. /* Make sure we have a valid controller before we proceed */
  290. if (ctx.ctrl == LIGHT_AUTO) {
  291. LIGHT_NOTE("Automatic mode -- finding best controller");
  292. if (!light_ctrl_probe(ctx.ctrl_name, sizeof(ctx.ctrl_name))) {
  293. LIGHT_ERR("could not find suitable controller");
  294. return false;
  295. }
  296. } else if (!light_ctrl_exist(ctx.ctrl_name)) {
  297. LIGHT_ERR("selected controller '%s' is not valid", ctx.ctrl_name);
  298. return false;
  299. }
  300. return true;
  301. }
  302. /* Print help and version info */
  303. static bool light_info(void)
  304. {
  305. if (ctx.cmd == LIGHT_PRINT_HELP) {
  306. light_usage();
  307. return true;
  308. }
  309. if (ctx.cmd == LIGHT_PRINT_VERSION) {
  310. printf("v%s\n", VERSION);
  311. return true;
  312. }
  313. if (ctx.cmd == LIGHT_LIST_CTRL) {
  314. /* listControllers() can return false, but only if it does not find any controllers. That is not enough for an unsuccessfull run. */
  315. light_ctrl_list();
  316. return true;
  317. }
  318. return false;
  319. }
  320. static bool validate(unsigned long *cur_raw, unsigned long *max_raw, bool *has_cap, unsigned long *min_cap)
  321. {
  322. if (ctx.has_cached_brightness_max) {
  323. *max_raw = ctx.cached_brightness_max;
  324. } else if (!light_ctrl_get_brightness_max(ctx.ctrl_name, max_raw)) {
  325. LIGHT_ERR("could not get max brightness");
  326. return false;
  327. }
  328. /* No need to go further if targetting mincap */
  329. if (ctx.field == LIGHT_MIN_CAP || ctx.field == LIGHT_MAX_BRIGHTNESS) {
  330. /* Init other values to 0 */
  331. *cur_raw = *min_cap = 0;
  332. *has_cap = false;
  333. return true;
  334. }
  335. if (!light_ctrl_get_brightness(ctx.ctrl_name, cur_raw)) {
  336. LIGHT_ERR("could not get brightness");
  337. return false;
  338. }
  339. if (!light_ctrl_get_cap_min(ctx.ctrl_name, has_cap, min_cap)) {
  340. LIGHT_ERR("could not get min brightness");
  341. return false;
  342. }
  343. if (*has_cap && *min_cap > *max_raw) {
  344. LIGHT_WARN("invalid minimum cap (raw) value of '%lu' for controller, ignoring and using 0",
  345. *min_cap);
  346. LIGHT_WARN("minimum cap must be inferior to '%lu'", *max_raw);
  347. min_cap = 0;
  348. }
  349. return true;
  350. }
  351. bool light_execute(void)
  352. {
  353. light_val_mode_t val_mode;
  354. unsigned long cur_raw; /* The current brightness, in raw units */
  355. double cur_percent; /* The current brightness, in percent */
  356. unsigned long max_raw; /* The max brightness, in percent */
  357. unsigned long min_cap; /* The minimum cap, in raw units */
  358. double min_cap_percent; /* The minimum cap, in percent */
  359. bool has_cap; /* If we have a minimum cap */
  360. if (light_info())
  361. return true;
  362. if (!validate(&cur_raw, &max_raw, &has_cap, &min_cap))
  363. return false;
  364. val_mode = ctx.val_mode;
  365. cur_percent = light_percent_clamp(((double)cur_raw) / ((double)max_raw) * 100);
  366. min_cap_percent = light_percent_clamp(((double)min_cap) / ((double)max_raw) * 100);
  367. LIGHT_NOTE("executing light on '%s' controller", ctx.ctrl_name);
  368. /* Handle get operations */
  369. if (ctx.cmd == LIGHT_GET) {
  370. switch (ctx.field) {
  371. case LIGHT_BRIGHTNESS:
  372. (val_mode == LIGHT_RAW) ? printf("%lu\n", cur_raw) : printf("%.2f\n", cur_percent);
  373. break;
  374. case LIGHT_MAX_BRIGHTNESS:
  375. (val_mode == LIGHT_RAW) ? printf("%lu\n", max_raw) : printf("100.00\n"); /* <- I know how stupid it is but it might just make someones life easier */
  376. break;
  377. case LIGHT_MIN_CAP:
  378. (val_mode == LIGHT_RAW) ? printf("%lu\n", min_cap) : printf("%.2f\n", min_cap_percent);
  379. break;
  380. case LIGHT_SAVERESTORE:
  381. break;
  382. }
  383. return true;
  384. }
  385. /* Handle saves and restores */
  386. if (ctx.cmd == LIGHT_SAVE) {
  387. if (!light_ctrl_save_brightness(ctx.ctrl_name, cur_raw)) {
  388. LIGHT_ERR("could not save brightness");
  389. return false;
  390. }
  391. return true;
  392. }
  393. if (ctx.cmd == LIGHT_RESTORE) {
  394. if (!light_ctrl_restore_brightness(ctx.ctrl_name)) {
  395. LIGHT_ERR("could not restore brightness");
  396. return false;
  397. }
  398. return true;
  399. }
  400. /* Handle set/add/sub operations */
  401. if (ctx.cmd == LIGHT_SET ||
  402. ctx.cmd == LIGHT_ADD || ctx.cmd == LIGHT_SUB) {
  403. unsigned long raw;
  404. if (val_mode == LIGHT_RAW)
  405. raw = ctx.val_raw;
  406. else
  407. raw = (unsigned long)((ctx.val_percent * ((double)max_raw)) / 100.0);
  408. if (ctx.field == LIGHT_MIN_CAP) {
  409. /* Handle minimum cap files */
  410. if (!light_ctrl_set_cap_min(ctx.ctrl_name, LIGHT_CLAMP(raw, 0, max_raw))) {
  411. LIGHT_ERR("could not set minimum cap");
  412. return false;
  413. }
  414. /* All good? Return true. */
  415. return true;
  416. } else if (ctx.field == LIGHT_BRIGHTNESS) {
  417. /* Handle brightness writing */
  418. unsigned long writeVal;
  419. switch (ctx.cmd) {
  420. case LIGHT_SET:
  421. writeVal = LIGHT_CLAMP(raw, min_cap, max_raw);
  422. break;
  423. case LIGHT_ADD:
  424. writeVal = LIGHT_CLAMP(cur_raw + raw, min_cap, max_raw);
  425. break;
  426. case LIGHT_SUB:
  427. /* check if we're going below 0, which wouldn't work with unsigned values */
  428. if (cur_raw < raw) {
  429. light_log_clamp_min(min_cap);
  430. writeVal = min_cap;
  431. break;
  432. }
  433. writeVal = LIGHT_CLAMP(cur_raw - raw, min_cap, max_raw);
  434. break;
  435. /* we have already taken other possibilities, so we shouldn't get here */
  436. default:
  437. return false;
  438. }
  439. /* Attempt to write */
  440. if (!light_ctrl_set_brightness(ctx.ctrl_name, writeVal)) {
  441. LIGHT_ERR("could not set brightness");
  442. return false;
  443. }
  444. /* All good? return true. */
  445. return true;
  446. }
  447. }
  448. /* Handle saves and restores */
  449. if (ctx.cmd == LIGHT_SAVE) {
  450. if (!light_ctrl_save_brightness(ctx.ctrl_name, cur_raw)) {
  451. LIGHT_ERR("could not save brightness");
  452. return false;
  453. }
  454. return true;
  455. }
  456. if (ctx.cmd == LIGHT_RESTORE) {
  457. if (!light_ctrl_restore_brightness(ctx.ctrl_name)) {
  458. LIGHT_ERR("could not restore brightness");
  459. return false;
  460. }
  461. return true;
  462. }
  463. fprintf(stderr,
  464. "Controller : %s\n"
  465. "Value : %lu\n"
  466. "Value %% : %.2f %%\n"
  467. "Command : %u\n"
  468. "Mode : %u\n"
  469. "Field : %u\n"
  470. "\n",
  471. ctx.ctrl_name, ctx.val_raw, ctx.val_percent, ctx.cmd, val_mode, ctx.field);
  472. fprintf(stderr, "You did not specify a valid combination of command line arguments.\n");
  473. light_usage();
  474. return false;
  475. }
  476. void light_free(void)
  477. {
  478. }
  479. /*
  480. * WARNING: `buffer` HAS to be freed by the user if not null once
  481. * returned! Size is always NAME_MAX + 1
  482. */
  483. bool light_gen_path(char const *controller, light_target_t target, light_field_t type, char **buffer)
  484. {
  485. char *path;
  486. int val = -1;
  487. if (!light_check_ctrl(controller)) {
  488. LIGHT_ERR("invalid controller, couldn't generate path");
  489. return false;
  490. }
  491. if (!buffer) {
  492. LIGHT_ERR("a valid buffer is required");
  493. return false;
  494. }
  495. *buffer = NULL;
  496. /* PATH_MAX define includes the '\0' character, so no + 1 here */
  497. path = malloc(PATH_MAX);
  498. if (!path) {
  499. LIGHT_MEMERR();
  500. return false;
  501. }
  502. if (target == LIGHT_BACKLIGHT) {
  503. switch (type) {
  504. case LIGHT_BRIGHTNESS:
  505. val = snprintf(path, PATH_MAX, "/sys/class/backlight/%s/brightness", controller);
  506. break;
  507. case LIGHT_MAX_BRIGHTNESS:
  508. val = snprintf(path, PATH_MAX, "/sys/class/backlight/%s/max_brightness", controller);
  509. break;
  510. case LIGHT_MIN_CAP:
  511. val = snprintf(path, PATH_MAX, "%s/mincap/%s", ctx.prefix, controller);
  512. break;
  513. case LIGHT_SAVERESTORE:
  514. val = snprintf(path, PATH_MAX, "%s/save/%s", ctx.prefix, controller);
  515. break;
  516. }
  517. } else {
  518. switch (type) {
  519. case LIGHT_BRIGHTNESS:
  520. val = snprintf(path, PATH_MAX, "/sys/class/leds/%s/brightness", controller);
  521. break;
  522. case LIGHT_MAX_BRIGHTNESS:
  523. val = snprintf(path, PATH_MAX, "/sys/class/leds/%s/max_brightness", controller);
  524. break;
  525. case LIGHT_MIN_CAP:
  526. val = snprintf(path, PATH_MAX, "%s/mincap/kbd/%s", ctx.prefix, controller);
  527. break;
  528. case LIGHT_SAVERESTORE:
  529. val = snprintf(path, PATH_MAX, "%s/save/kbd/%s", ctx.prefix, controller);
  530. break;
  531. }
  532. }
  533. if (val < 0) {
  534. LIGHT_ERR("snprintf failed");
  535. free(path);
  536. return false;
  537. }
  538. /* PATH_MAX define includes the '\0' character, so - 1 here */
  539. if (val > PATH_MAX - 1) {
  540. LIGHT_ERR("generated path is too long to be handled");
  541. return false;
  542. }
  543. *buffer = path;
  544. return true;
  545. }
  546. bool light_ctrl_get_brightnessPath(char const *controller, char **path)
  547. {
  548. if (!light_gen_path(controller, ctx.target, LIGHT_BRIGHTNESS, path)) {
  549. LIGHT_ERR("could not generate path to brightness file");
  550. return false;
  551. }
  552. return true;
  553. }
  554. bool light_ctrl_get_brightness(char const *controller, unsigned long *v)
  555. {
  556. char *path = NULL;
  557. bool rc = false;
  558. if (!light_ctrl_get_brightnessPath(controller, &path))
  559. return false;
  560. rc = light_file_read_val(path, v);
  561. free(path);
  562. if (!rc) {
  563. LIGHT_ERR("could not read value from brightness file");
  564. return false;
  565. }
  566. return true;
  567. }
  568. bool light_ctrl_get_brightness_maxPath(char const *controller, char **path)
  569. {
  570. if (!light_gen_path(controller, ctx.target, LIGHT_MAX_BRIGHTNESS, path)) {
  571. LIGHT_ERR("could not generate path to maximum brightness file");
  572. return false;
  573. }
  574. return true;
  575. }
  576. bool light_ctrl_get_brightness_max(char const *controller, unsigned long *v)
  577. {
  578. char *path = NULL;
  579. bool rc = false;
  580. if (!light_ctrl_get_brightness_maxPath(controller, &path))
  581. return false;
  582. rc = light_file_read_val(path, v);
  583. free(path);
  584. if (!rc) {
  585. LIGHT_ERR("could not read value from max brightness file");
  586. return false;
  587. }
  588. if (*v == 0) {
  589. LIGHT_ERR("max brightness is 0, so controller is not valid");
  590. return false;
  591. }
  592. return true;
  593. }
  594. bool light_ctrl_set_brightness(char const *controller, unsigned long v)
  595. {
  596. char *path = NULL;
  597. bool rc;
  598. if (!light_gen_path(controller, ctx.target, ctx.field, &path)) {
  599. LIGHT_ERR("could not generate path to brightness file");
  600. return false;
  601. }
  602. LIGHT_NOTE("setting brightness %lu (raw) to controller", v);
  603. rc = light_file_write_val(path, v);
  604. if (!rc) {
  605. LIGHT_ERR("could not write value to brightness file");
  606. }
  607. free(path);
  608. return rc;
  609. }
  610. bool light_ctrl_exist(char const *controller)
  611. {
  612. char *path = NULL;
  613. /* On auto mode, we need to check if we can read the max brightness value
  614. of the controller for later computation */
  615. if (ctx.ctrl == LIGHT_AUTO || ctx.field == LIGHT_MAX_BRIGHTNESS) {
  616. if (!light_ctrl_get_brightness_maxPath(controller, &path))
  617. return false;
  618. if (!light_file_is_readable(path)) {
  619. LIGHT_WARN("could not open controller max brightness "
  620. "file for reading, so controller is not accessible");
  621. free(path);
  622. return false;
  623. }
  624. free(path);
  625. }
  626. if (!light_ctrl_get_brightnessPath(controller, &path))
  627. return false;
  628. if (ctx.cmd != LIGHT_GET && ctx.cmd != LIGHT_SAVE &&
  629. ctx.field != LIGHT_MIN_CAP && !light_file_is_writable(path)) {
  630. LIGHT_WARN("could not open controller brightness file for writing, so controller is not accessible");
  631. free(path);
  632. return false;
  633. } else if (!light_file_is_readable(path)) {
  634. LIGHT_WARN("could not open controller brightness file for reading, so controller is not accessible");
  635. free(path);
  636. return false;
  637. }
  638. free(path);
  639. return true;
  640. }
  641. static bool light_ctrl_init(DIR **dir)
  642. {
  643. if (!dir) {
  644. errno = EINVAL;
  645. return false;
  646. }
  647. if (ctx.target == LIGHT_KEYBOARD)
  648. *dir = opendir("/sys/class/leds");
  649. else
  650. *dir = opendir("/sys/class/backlight");
  651. if (!*dir)
  652. return false;
  653. return true;
  654. }
  655. static bool light_ctrl_iterate(DIR *dir, char *current, size_t len)
  656. {
  657. struct dirent *d;
  658. bool found = false;
  659. if (!dir || !current)
  660. return false;
  661. while (!found) {
  662. d = readdir(dir);
  663. if (!d)
  664. return false;
  665. if (d->d_name[0] != '.') {
  666. if (!light_check_ctrl(d->d_name)) {
  667. LIGHT_WARN("invalid controller '%s' found, continuing...", d->d_name);
  668. continue;
  669. }
  670. found = true;
  671. }
  672. }
  673. snprintf(current, len, "%s", d->d_name);
  674. return true;
  675. }
  676. /* WARNING: `controller` HAS to be at most NAME_MAX, otherwise fails */
  677. bool light_ctrl_probe(char *controller, size_t len)
  678. {
  679. DIR *dir;
  680. unsigned long best = 0;
  681. bool found = false;
  682. char best_name[NAME_MAX + 1];
  683. char current[NAME_MAX + 1];
  684. if (!controller) {
  685. LIGHT_ERR("Missing controller name");
  686. return false;
  687. }
  688. if (!light_ctrl_init(&dir)) {
  689. LIGHT_ERR("Failed listing controllers: %s", strerror(errno));
  690. return false;
  691. }
  692. while (light_ctrl_iterate(dir, current, sizeof(current))) {
  693. unsigned long val = 0;
  694. LIGHT_NOTE("found '%s' controller", current);
  695. if (light_ctrl_exist(current)) {
  696. if (light_ctrl_get_brightness_max(current, &val)) {
  697. if (val > best) {
  698. found = true;
  699. best = val;
  700. snprintf(best_name, sizeof(best_name), "%s", current);
  701. ctx.has_cached_brightness_max = true;
  702. ctx.cached_brightness_max = val;
  703. } else {
  704. LIGHT_NOTE("ignoring controller as better one already found");
  705. }
  706. } else {
  707. LIGHT_WARN("could not read max brightness from file");
  708. }
  709. } else {
  710. LIGHT_WARN("controller not accessible");
  711. }
  712. }
  713. closedir(dir);
  714. if (!found) {
  715. LIGHT_ERR("could not find an accessible controller");
  716. return false;
  717. }
  718. if (best == 0) {
  719. LIGHT_ERR("found accessible controller but it's useless/corrupt");
  720. return false;
  721. }
  722. snprintf(controller, len, "%s", best_name);
  723. return true;
  724. }
  725. bool light_ctrl_get_cap_min(char const *controller, bool *has_cap, unsigned long *min_cap)
  726. {
  727. char *path = NULL;
  728. if (!light_gen_path(controller, ctx.target, LIGHT_MIN_CAP, &path)) {
  729. LIGHT_ERR("could not generate path to minimum cap file");
  730. return false;
  731. }
  732. if (!light_file_is_readable(path)) {
  733. *has_cap = false;
  734. *min_cap = 0;
  735. free(path);
  736. LIGHT_NOTE("cap file doesn't exist or can't read from it, so assuming a minimum brightness of 0");
  737. return true;
  738. }
  739. if (!light_file_read_val(path, min_cap)) {
  740. LIGHT_ERR("could not read minimum cap from file");
  741. free(path);
  742. return false;
  743. }
  744. *has_cap = true;
  745. free(path);
  746. return true;
  747. }
  748. bool light_ctrl_set_cap_min(char const *controller, unsigned long val)
  749. {
  750. char *path = NULL;
  751. if (!light_gen_path(controller, ctx.target, LIGHT_MIN_CAP, &path)) {
  752. LIGHT_ERR("could not generate path to minimum cap file");
  753. return false;
  754. }
  755. LIGHT_NOTE("setting minimum cap to %lu (raw)", val);
  756. if (!light_file_write_val(path, val)) {
  757. LIGHT_ERR("could not write to minimum cap file");
  758. free(path);
  759. return false;
  760. }
  761. free(path);
  762. return true;
  763. }
  764. bool light_ctrl_list(void)
  765. {
  766. char controller[NAME_MAX];
  767. bool found = false;
  768. DIR *dir;
  769. if (!light_ctrl_init(&dir)) {
  770. LIGHT_ERR("Failed listing controllers: %s", strerror(errno));
  771. return false;
  772. }
  773. while (light_ctrl_iterate(dir, controller, sizeof(controller))) {
  774. printf("%s\n", controller);
  775. found = true;
  776. }
  777. if (!found) {
  778. LIGHT_WARN("no controllers found, either check your system or your permissions");
  779. return false;
  780. }
  781. return true;
  782. }
  783. bool light_ctrl_save_brightness(char const *controller, unsigned long val)
  784. {
  785. char *path = NULL;
  786. if (!light_gen_path(controller, ctx.target, LIGHT_SAVERESTORE, &path)) {
  787. LIGHT_ERR("could not generate path to save/restore file");
  788. return false;
  789. }
  790. LIGHT_NOTE("saving brightness %lu (raw) to save file %s\n", val, path);
  791. if (!light_file_write_val(path, val)) {
  792. LIGHT_ERR("could not write to save/restore file");
  793. free(path);
  794. return false;
  795. }
  796. free(path);
  797. return true;
  798. }
  799. bool light_ctrl_restore_brightness(char const *controller)
  800. {
  801. char *path = NULL;
  802. unsigned long val = 0;
  803. if (!light_gen_path(controller, ctx.target, LIGHT_SAVERESTORE, &path)) {
  804. LIGHT_ERR("could not generate path to save/restore file");
  805. return false;
  806. }
  807. LIGHT_NOTE("restoring brightness from saved file %s", path);
  808. if (!light_file_read_val(path, &val)) {
  809. LIGHT_ERR("could not read saved value");
  810. free(path);
  811. return false;
  812. }
  813. if (!light_ctrl_set_brightness(controller, val)) {
  814. LIGHT_ERR("could not set restored brightness");
  815. free(path);
  816. return false;
  817. }
  818. free(path);
  819. return true;
  820. }