light.c 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997
  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. /* -- Controller selection -- */
  179. case 'a':
  180. ASSERT_CTRLSET();
  181. ctx.ctrl = LIGHT_AUTO;
  182. break;;
  183. case 's':
  184. ASSERT_CTRLSET();
  185. ctx.ctrl = LIGHT_SPECIFY;
  186. if (!light_check_ctrl(optarg))
  187. return false;
  188. strncpy(ctx.ctrl_name, optarg, NAME_MAX);
  189. ctx.ctrl_name[NAME_MAX] = '\0';
  190. break;
  191. /* -- Value modes -- */
  192. case 'p':
  193. ASSERT_VALSET();
  194. ctx.val_mode = LIGHT_PERCENT;
  195. break;
  196. case 'r':
  197. ASSERT_VALSET();
  198. ctx.val_mode = LIGHT_RAW;
  199. break;
  200. /* -- Other -- */
  201. case 'v':
  202. if (sscanf(optarg, "%i", &loglevel) != 1) {
  203. fprintf(stderr, "-v ARG is not recognizable.\n\n");
  204. light_usage();
  205. return false;
  206. }
  207. if (loglevel < 0 || loglevel > 3) {
  208. fprintf(stderr, "-v ARG must be between 0 and 3.\n\n");
  209. light_usage();
  210. return false;
  211. }
  212. light_loglevel = (light_loglevel_t)loglevel;
  213. break;
  214. }
  215. }
  216. if (!light_check_ops()) {
  217. light_usage();
  218. return false;
  219. }
  220. /* If we need a <value> (for writing), make sure we have it! */
  221. if (ctx.cmd == LIGHT_SET ||
  222. ctx.cmd == LIGHT_ADD || ctx.cmd == LIGHT_SUB) {
  223. if (argc - optind != 1) {
  224. fprintf(stderr, "Light needs an argument for <value>.\n\n");
  225. light_usage();
  226. return false;
  227. }
  228. if (ctx.val_mode == LIGHT_PERCENT) {
  229. if (sscanf(argv[optind], "%lf", &ctx.val_percent) != 1) {
  230. fprintf(stderr, "<value> is not specified in a recognizable format.\n\n");
  231. light_usage();
  232. return false;
  233. }
  234. ctx.val_percent = light_percent_clamp(ctx.val_percent);
  235. } else {
  236. if (sscanf(argv[optind], "%lu", &ctx.val_raw) != 1) {
  237. fprintf(stderr, "<value> is not specified in a recognizable format.\n\n");
  238. light_usage();
  239. return false;
  240. }
  241. }
  242. }
  243. return true;
  244. }
  245. static int mkpath(char *dir, mode_t mode)
  246. {
  247. struct stat sb;
  248. if (!dir) {
  249. errno = EINVAL;
  250. return -1;
  251. }
  252. if (!stat(dir, &sb))
  253. return 0;
  254. mkpath(dirname(strdupa(dir)), mode);
  255. return mkdir(dir, mode);
  256. }
  257. bool light_initialize(int argc, char **argv)
  258. {
  259. light_cmd_t mode;
  260. int rc;
  261. /* Classic SUID root mode or new user based cache files */
  262. if (geteuid() == 0)
  263. snprintf(ctx.prefix, sizeof(ctx.prefix), "%s", "/etc/light");
  264. else
  265. snprintf(ctx.prefix, sizeof(ctx.prefix), "%s/.cache/light", getenv("HOME"));
  266. light_defaults();
  267. if (!light_parse_args(argc, argv)) {
  268. LIGHT_ERR("could not parse arguments");
  269. return false;
  270. }
  271. /* Just return true for operation modes that do not need initialization */
  272. mode = ctx.cmd;
  273. if (mode == LIGHT_PRINT_HELP || mode == LIGHT_PRINT_VERSION || mode == LIGHT_LIST_CTRL)
  274. return true;
  275. /* Make sure we have a valid cache directory for all files */
  276. if (mode == LIGHT_SAVE || (mode == LIGHT_SET && ctx.field == LIGHT_MIN_CAP)) {
  277. const char *dirs[5] = {
  278. "/mincap/kbd", "/save/kbd", NULL
  279. };
  280. char path[strlen(ctx.prefix) + 20];
  281. int i;
  282. for (i = 0; dirs[i]; i++) {
  283. snprintf(path, sizeof(path), "%s%s", ctx.prefix, dirs[i]);
  284. rc = mkpath(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  285. if (rc && errno != EEXIST) {
  286. LIGHT_ERR("'%s' does not exist and could not be created,"
  287. " make sure this application is run as root.", path);
  288. return false;
  289. }
  290. }
  291. }
  292. /* Make sure we have a valid controller before we proceed */
  293. if (ctx.ctrl == LIGHT_AUTO) {
  294. LIGHT_NOTE("Automatic mode -- finding best controller");
  295. if (!light_ctrl_probe(ctx.ctrl_name)) {
  296. LIGHT_ERR("could not find suitable controller");
  297. return false;
  298. }
  299. } else if (!light_ctrl_exist(ctx.ctrl_name)) {
  300. LIGHT_ERR("selected controller '%s' is not valid", ctx.ctrl_name);
  301. return false;
  302. }
  303. return true;
  304. }
  305. /* Print help and version info */
  306. static bool light_info(void)
  307. {
  308. if (ctx.cmd == LIGHT_PRINT_HELP) {
  309. light_usage();
  310. return true;
  311. }
  312. if (ctx.cmd == LIGHT_PRINT_VERSION) {
  313. printf("v%s\n", VERSION);
  314. return true;
  315. }
  316. if (ctx.cmd == LIGHT_LIST_CTRL) {
  317. /* listControllers() can return false, but only if it does not find any controllers. That is not enough for an unsuccessfull run. */
  318. light_ctrl_list();
  319. return true;
  320. }
  321. return false;
  322. }
  323. static bool validate(unsigned long *cur_raw, unsigned long *max_raw, bool *has_cap, unsigned long *min_cap)
  324. {
  325. if (ctx.has_cached_brightness_max) {
  326. *max_raw = ctx.cached_brightness_max;
  327. } else if (!light_ctrl_get_brightness_max(ctx.ctrl_name, max_raw)) {
  328. LIGHT_ERR("could not get max brightness");
  329. return false;
  330. }
  331. /* No need to go further if targetting mincap */
  332. if (ctx.field == LIGHT_MIN_CAP || ctx.field == LIGHT_MAX_BRIGHTNESS) {
  333. /* Init other values to 0 */
  334. *cur_raw = *min_cap = 0;
  335. *has_cap = false;
  336. return true;
  337. }
  338. if (!light_ctrl_get_brightness(ctx.ctrl_name, cur_raw)) {
  339. LIGHT_ERR("could not get brightness");
  340. return false;
  341. }
  342. if (!light_ctrl_get_cap_min(ctx.ctrl_name, has_cap, min_cap)) {
  343. LIGHT_ERR("could not get min brightness");
  344. return false;
  345. }
  346. if (*has_cap && *min_cap > *max_raw) {
  347. LIGHT_WARN("invalid minimum cap (raw) value of '%lu' for controller, ignoring and using 0",
  348. *min_cap);
  349. LIGHT_WARN("minimum cap must be inferior to '%lu'", *max_raw);
  350. min_cap = 0;
  351. }
  352. return true;
  353. }
  354. bool light_execute(void)
  355. {
  356. light_val_mode_t val_mode;
  357. unsigned long cur_raw; /* The current brightness, in raw units */
  358. double cur_percent; /* The current brightness, in percent */
  359. unsigned long max_raw; /* The max brightness, in percent */
  360. unsigned long min_cap; /* The minimum cap, in raw units */
  361. double min_cap_percent; /* The minimum cap, in percent */
  362. bool has_cap; /* If we have a minimum cap */
  363. if (light_info())
  364. return true;
  365. if (!validate(&cur_raw, &max_raw, &has_cap, &min_cap))
  366. return false;
  367. val_mode = ctx.val_mode;
  368. cur_percent = light_percent_clamp(((double)cur_raw) / ((double)max_raw) * 100);
  369. min_cap_percent = light_percent_clamp(((double)min_cap) / ((double)max_raw) * 100);
  370. LIGHT_NOTE("executing light on '%s' controller", ctx.ctrl_name);
  371. /* Handle get operations */
  372. if (ctx.cmd == LIGHT_GET) {
  373. switch (ctx.field) {
  374. case LIGHT_BRIGHTNESS:
  375. (val_mode == LIGHT_RAW) ? printf("%lu\n", cur_raw) : printf("%.2f\n", cur_percent);
  376. break;
  377. case LIGHT_MAX_BRIGHTNESS:
  378. (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 */
  379. break;
  380. case LIGHT_MIN_CAP:
  381. (val_mode == LIGHT_RAW) ? printf("%lu\n", min_cap) : printf("%.2f\n", min_cap_percent);
  382. break;
  383. case LIGHT_SAVERESTORE:
  384. break;
  385. }
  386. return true;
  387. }
  388. /* Handle saves and restores */
  389. if (ctx.cmd == LIGHT_SAVE) {
  390. if (!light_ctrl_save_brightness(ctx.ctrl_name, cur_raw)) {
  391. LIGHT_ERR("could not save brightness");
  392. return false;
  393. }
  394. return true;
  395. }
  396. if (ctx.cmd == LIGHT_RESTORE) {
  397. if (!light_ctrl_restore_brightness(ctx.ctrl_name)) {
  398. LIGHT_ERR("could not restore brightness");
  399. return false;
  400. }
  401. return true;
  402. }
  403. /* Handle set/add/sub operations */
  404. if (ctx.cmd == LIGHT_SET ||
  405. ctx.cmd == LIGHT_ADD || ctx.cmd == LIGHT_SUB) {
  406. unsigned long raw;
  407. if (val_mode == LIGHT_RAW)
  408. raw = ctx.val_raw;
  409. else
  410. raw = (unsigned long)((ctx.val_percent * ((double)max_raw)) / 100.0);
  411. if (ctx.field == LIGHT_MIN_CAP) {
  412. /* Handle minimum cap files */
  413. if (!light_ctrl_set_cap_min(ctx.ctrl_name, LIGHT_CLAMP(raw, 0, max_raw))) {
  414. LIGHT_ERR("could not set minimum cap");
  415. return false;
  416. }
  417. /* All good? Return true. */
  418. return true;
  419. } else if (ctx.field == LIGHT_BRIGHTNESS) {
  420. /* Handle brightness writing */
  421. unsigned long writeVal;
  422. switch (ctx.cmd) {
  423. case LIGHT_SET:
  424. writeVal = LIGHT_CLAMP(raw, min_cap, max_raw);
  425. break;
  426. case LIGHT_ADD:
  427. writeVal = LIGHT_CLAMP(cur_raw + raw, min_cap, max_raw);
  428. break;
  429. case LIGHT_SUB:
  430. /* check if we're going below 0, which wouldn't work with unsigned values */
  431. if (cur_raw < raw) {
  432. light_log_clamp_min(min_cap);
  433. writeVal = min_cap;
  434. break;
  435. }
  436. writeVal = LIGHT_CLAMP(cur_raw - raw, min_cap, max_raw);
  437. break;
  438. /* we have already taken other possibilities, so we shouldn't get here */
  439. default:
  440. return false;
  441. }
  442. /* Attempt to write */
  443. if (!light_ctrl_set_brightness(ctx.ctrl_name, writeVal)) {
  444. LIGHT_ERR("could not set brightness");
  445. return false;
  446. }
  447. /* All good? return true. */
  448. return true;
  449. }
  450. }
  451. /* Handle saves and restores */
  452. if (ctx.cmd == LIGHT_SAVE) {
  453. if (!light_ctrl_save_brightness(ctx.ctrl_name, cur_raw)) {
  454. LIGHT_ERR("could not save brightness");
  455. return false;
  456. }
  457. return true;
  458. }
  459. if (ctx.cmd == LIGHT_RESTORE) {
  460. if (!light_ctrl_restore_brightness(ctx.ctrl_name)) {
  461. LIGHT_ERR("could not restore brightness");
  462. return false;
  463. }
  464. return true;
  465. }
  466. fprintf(stderr,
  467. "Controller : %s\n"
  468. "Value : %lu\n"
  469. "Value %% : %.2f %%\n"
  470. "Command : %u\n"
  471. "Mode : %u\n"
  472. "Field : %u\n"
  473. "\n",
  474. ctx.ctrl_name, ctx.val_raw, ctx.val_percent, ctx.cmd, val_mode, ctx.field);
  475. fprintf(stderr, "You did not specify a valid combination of command line arguments.\n");
  476. light_usage();
  477. return false;
  478. }
  479. void light_free(void)
  480. {
  481. }
  482. /*
  483. * WARNING: `buffer` HAS to be freed by the user if not null once
  484. * returned! Size is always NAME_MAX + 1
  485. */
  486. bool light_gen_path(char const *controller, light_target_t target, light_field_t type, char **buffer)
  487. {
  488. char *path;
  489. int val = -1;
  490. if (!light_check_ctrl(controller)) {
  491. LIGHT_ERR("invalid controller, couldn't generate path");
  492. return false;
  493. }
  494. if (!buffer) {
  495. LIGHT_ERR("a valid buffer is required");
  496. return false;
  497. }
  498. *buffer = NULL;
  499. /* PATH_MAX define includes the '\0' character, so no + 1 here */
  500. path = malloc(PATH_MAX);
  501. if (!path) {
  502. LIGHT_MEMERR();
  503. return false;
  504. }
  505. if (target == LIGHT_BACKLIGHT) {
  506. switch (type) {
  507. case LIGHT_BRIGHTNESS:
  508. val = snprintf(path, PATH_MAX, "/sys/class/backlight/%s/brightness", controller);
  509. break;
  510. case LIGHT_MAX_BRIGHTNESS:
  511. val = snprintf(path, PATH_MAX, "/sys/class/backlight/%s/max_brightness", controller);
  512. break;
  513. case LIGHT_MIN_CAP:
  514. val = snprintf(path, PATH_MAX, "%s/mincap/%s", ctx.prefix, controller);
  515. break;
  516. case LIGHT_SAVERESTORE:
  517. val = snprintf(path, PATH_MAX, "%s/save/%s", ctx.prefix, controller);
  518. break;
  519. }
  520. } else {
  521. switch (type) {
  522. case LIGHT_BRIGHTNESS:
  523. val = snprintf(path, PATH_MAX, "/sys/class/leds/%s/brightness", controller);
  524. break;
  525. case LIGHT_MAX_BRIGHTNESS:
  526. val = snprintf(path, PATH_MAX, "/sys/class/leds/%s/max_brightness", controller);
  527. break;
  528. case LIGHT_MIN_CAP:
  529. val = snprintf(path, PATH_MAX, "%s/mincap/kbd/%s", ctx.prefix, controller);
  530. break;
  531. case LIGHT_SAVERESTORE:
  532. val = snprintf(path, PATH_MAX, "%s/save/kbd/%s", ctx.prefix, controller);
  533. break;
  534. }
  535. }
  536. if (val < 0) {
  537. LIGHT_ERR("snprintf failed");
  538. free(path);
  539. return false;
  540. }
  541. /* PATH_MAX define includes the '\0' character, so - 1 here */
  542. if (val > PATH_MAX - 1) {
  543. LIGHT_ERR("generated path is too long to be handled");
  544. return false;
  545. }
  546. *buffer = path;
  547. return true;
  548. }
  549. bool light_ctrl_get_brightnessPath(char const *controller, char **path)
  550. {
  551. if (!light_gen_path(controller, ctx.target, LIGHT_BRIGHTNESS, path)) {
  552. LIGHT_ERR("could not generate path to brightness file");
  553. return false;
  554. }
  555. return true;
  556. }
  557. bool light_ctrl_get_brightness(char const *controller, unsigned long *v)
  558. {
  559. char *path = NULL;
  560. bool rc = false;
  561. if (!light_ctrl_get_brightnessPath(controller, &path))
  562. return false;
  563. rc = light_file_read_val(path, v);
  564. free(path);
  565. if (!rc) {
  566. LIGHT_ERR("could not read value from brightness file");
  567. return false;
  568. }
  569. return true;
  570. }
  571. bool light_ctrl_get_brightness_maxPath(char const *controller, char **path)
  572. {
  573. if (!light_gen_path(controller, ctx.target, LIGHT_MAX_BRIGHTNESS, path)) {
  574. LIGHT_ERR("could not generate path to maximum brightness file");
  575. return false;
  576. }
  577. return true;
  578. }
  579. bool light_ctrl_get_brightness_max(char const *controller, unsigned long *v)
  580. {
  581. char *path = NULL;
  582. bool rc = false;
  583. if (!light_ctrl_get_brightness_maxPath(controller, &path))
  584. return false;
  585. rc = light_file_read_val(path, v);
  586. free(path);
  587. if (!rc) {
  588. LIGHT_ERR("could not read value from max brightness file");
  589. return false;
  590. }
  591. if (*v == 0) {
  592. LIGHT_ERR("max brightness is 0, so controller is not valid");
  593. return false;
  594. }
  595. return true;
  596. }
  597. bool light_ctrl_set_brightness(char const *controller, unsigned long v)
  598. {
  599. char *path = NULL;
  600. bool rc;
  601. if (!light_gen_path(controller, ctx.target, ctx.field, &path)) {
  602. LIGHT_ERR("could not generate path to brightness file");
  603. return false;
  604. }
  605. LIGHT_NOTE("setting brightness %lu (raw) to controller", v);
  606. rc = light_file_write_val(path, v);
  607. if (!rc) {
  608. LIGHT_ERR("could not write value to brightness file");
  609. }
  610. free(path);
  611. return rc;
  612. }
  613. bool light_ctrl_exist(char const *controller)
  614. {
  615. char *path = NULL;
  616. /* On auto mode, we need to check if we can read the max brightness value
  617. of the controller for later computation */
  618. if (ctx.ctrl == LIGHT_AUTO || ctx.field == LIGHT_MAX_BRIGHTNESS) {
  619. if (!light_ctrl_get_brightness_maxPath(controller, &path))
  620. return false;
  621. if (!light_file_is_readable(path)) {
  622. LIGHT_WARN("could not open controller max brightness "
  623. "file for reading, so controller is not accessible");
  624. free(path);
  625. return false;
  626. }
  627. free(path);
  628. }
  629. if (!light_ctrl_get_brightnessPath(controller, &path))
  630. return false;
  631. if (ctx.cmd != LIGHT_GET && ctx.cmd != LIGHT_SAVE &&
  632. ctx.field != LIGHT_MIN_CAP && !light_file_is_writable(path)) {
  633. LIGHT_WARN("could not open controller brightness file for writing, so controller is not accessible");
  634. free(path);
  635. return false;
  636. } else if (!light_file_is_readable(path)) {
  637. LIGHT_WARN("could not open controller brightness file for reading, so controller is not accessible");
  638. free(path);
  639. return false;
  640. }
  641. free(path);
  642. return true;
  643. }
  644. static bool light_ctrl_init(DIR **dir)
  645. {
  646. if (!dir) {
  647. errno = EINVAL;
  648. return false;
  649. }
  650. if (ctx.target == LIGHT_KEYBOARD)
  651. *dir = opendir("/sys/class/leds");
  652. else
  653. *dir = opendir("/sys/class/backlight");
  654. if (!*dir)
  655. return false;
  656. return true;
  657. }
  658. static bool light_ctrl_iterate(DIR *dir, char *current)
  659. {
  660. struct dirent *d;
  661. bool found = false;
  662. if (!dir || !current)
  663. return false;
  664. while (!found) {
  665. d = readdir(dir);
  666. if (!d)
  667. return false;
  668. if (d->d_name[0] != '.') {
  669. if (!light_check_ctrl(d->d_name)) {
  670. LIGHT_WARN("invalid controller '%s' found, continuing...", d->d_name);
  671. continue;
  672. }
  673. found = true;
  674. }
  675. }
  676. strncpy(current, d->d_name, NAME_MAX);
  677. current[NAME_MAX] = '\0';
  678. return true;
  679. }
  680. /* WARNING: `controller` HAS to be at most NAME_MAX, otherwise fails */
  681. bool light_ctrl_probe(char *controller)
  682. {
  683. DIR *dir;
  684. unsigned long best = 0;
  685. bool found = false;
  686. char best_name[NAME_MAX + 1];
  687. char current[NAME_MAX + 1];
  688. if (!controller) {
  689. LIGHT_ERR("Missing controller name");
  690. return false;
  691. }
  692. if (!light_ctrl_init(&dir)) {
  693. LIGHT_ERR("Failed listing controllers: %s", strerror(errno));
  694. return false;
  695. }
  696. while (light_ctrl_iterate(dir, current)) {
  697. unsigned long val = 0;
  698. LIGHT_NOTE("found '%s' controller", current);
  699. if (light_ctrl_exist(current)) {
  700. if (light_ctrl_get_brightness_max(current, &val)) {
  701. if (val > best) {
  702. found = true;
  703. best = val;
  704. strncpy(best_name, current, NAME_MAX);
  705. best_name[NAME_MAX] = '\0';
  706. ctx.has_cached_brightness_max = true;
  707. ctx.cached_brightness_max = val;
  708. } else {
  709. LIGHT_NOTE("ignoring controller as better one already found");
  710. }
  711. } else {
  712. LIGHT_WARN("could not read max brightness from file");
  713. }
  714. } else {
  715. LIGHT_WARN("controller not accessible");
  716. }
  717. }
  718. closedir(dir);
  719. if (!found) {
  720. LIGHT_ERR("could not find an accessible controller");
  721. return false;
  722. }
  723. if (best == 0) {
  724. LIGHT_ERR("found accessible controller but it's useless/corrupt");
  725. return false;
  726. }
  727. strncpy(controller, best_name, NAME_MAX);
  728. controller[NAME_MAX] = '\0';
  729. return true;
  730. }
  731. bool light_ctrl_get_cap_min(char const *controller, bool *has_cap, unsigned long *min_cap)
  732. {
  733. char *path = NULL;
  734. if (!light_gen_path(controller, ctx.target, LIGHT_MIN_CAP, &path)) {
  735. LIGHT_ERR("could not generate path to minimum cap file");
  736. return false;
  737. }
  738. if (!light_file_is_readable(path)) {
  739. *has_cap = false;
  740. *min_cap = 0;
  741. free(path);
  742. LIGHT_NOTE("cap file doesn't exist or can't read from it, so assuming a minimum brightness of 0");
  743. return true;
  744. }
  745. if (!light_file_read_val(path, min_cap)) {
  746. LIGHT_ERR("could not read minimum cap from file");
  747. free(path);
  748. return false;
  749. }
  750. *has_cap = true;
  751. free(path);
  752. return true;
  753. }
  754. bool light_ctrl_set_cap_min(char const *controller, unsigned long val)
  755. {
  756. char *path = NULL;
  757. if (!light_gen_path(controller, ctx.target, LIGHT_MIN_CAP, &path)) {
  758. LIGHT_ERR("could not generate path to minimum cap file");
  759. return false;
  760. }
  761. LIGHT_NOTE("setting minimum cap to %lu (raw)", val);
  762. if (!light_file_write_val(path, val)) {
  763. LIGHT_ERR("could not write to minimum cap file");
  764. free(path);
  765. return false;
  766. }
  767. free(path);
  768. return true;
  769. }
  770. bool light_ctrl_list(void)
  771. {
  772. char controller[NAME_MAX + 1];
  773. bool found = false;
  774. DIR *dir;
  775. if (!light_ctrl_init(&dir)) {
  776. LIGHT_ERR("Failed listing controllers: %s", strerror(errno));
  777. return false;
  778. }
  779. while (light_ctrl_iterate(dir, controller)) {
  780. printf("%s\n", controller);
  781. found = true;
  782. }
  783. if (!found) {
  784. LIGHT_WARN("no controllers found, either check your system or your permissions");
  785. return false;
  786. }
  787. return true;
  788. }
  789. bool light_ctrl_save_brightness(char const *controller, unsigned long val)
  790. {
  791. char *path = NULL;
  792. if (!light_gen_path(controller, ctx.target, LIGHT_SAVERESTORE, &path)) {
  793. LIGHT_ERR("could not generate path to save/restore file");
  794. return false;
  795. }
  796. LIGHT_NOTE("saving brightness %lu (raw) to save file %s\n", val, path);
  797. if (!light_file_write_val(path, val)) {
  798. LIGHT_ERR("could not write to save/restore file");
  799. free(path);
  800. return false;
  801. }
  802. free(path);
  803. return true;
  804. }
  805. bool light_ctrl_restore_brightness(char const *controller)
  806. {
  807. char *path = NULL;
  808. unsigned long val = 0;
  809. if (!light_gen_path(controller, ctx.target, LIGHT_SAVERESTORE, &path)) {
  810. LIGHT_ERR("could not generate path to save/restore file");
  811. return false;
  812. }
  813. LIGHT_NOTE("restoring brightness from saved file %s", path);
  814. if (!light_file_read_val(path, &val)) {
  815. LIGHT_ERR("could not read saved value");
  816. free(path);
  817. return false;
  818. }
  819. if (!light_ctrl_set_brightness(controller, val)) {
  820. LIGHT_ERR("could not set restored brightness");
  821. free(path);
  822. return false;
  823. }
  824. free(path);
  825. return true;
  826. }