light.c 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. #include "light.h"
  2. #include "helpers.h"
  3. // The different device implementations
  4. #include "impl/sysfs.h"
  5. //#include "impl/razer.h"
  6. #include <stdlib.h> // malloc, free
  7. #include <string.h> // strstr
  8. #include <stdio.h> // snprintf
  9. #include <unistd.h> // geteuid
  10. #include <sys/types.h> // geteuid
  11. #include <errno.h>
  12. #include <inttypes.h> // PRIu64
  13. /* Static helper functions for this file only, prefix with _ */
  14. static void _light_get_target_path(light_context_t* ctx, char* output_path, size_t output_size)
  15. {
  16. snprintf(output_path, output_size,
  17. "%s/targets/%s/%s/%s",
  18. ctx->sys_params.conf_dir,
  19. ctx->run_params.device_target->device->enumerator->name,
  20. ctx->run_params.device_target->device->name,
  21. ctx->run_params.device_target->name
  22. );
  23. }
  24. static void _light_get_target_file(light_context_t* ctx, char* output_path, size_t output_size, char const * file)
  25. {
  26. snprintf(output_path, output_size,
  27. "%s/targets/%s/%s/%s/%s",
  28. ctx->sys_params.conf_dir,
  29. ctx->run_params.device_target->device->enumerator->name,
  30. ctx->run_params.device_target->device->name,
  31. ctx->run_params.device_target->name,
  32. file
  33. );
  34. }
  35. static uint64_t _light_get_min_cap(light_context_t *ctx)
  36. {
  37. char target_path[NAME_MAX];
  38. _light_get_target_file(ctx, target_path, sizeof(target_path), "minimum");
  39. uint64_t minimum_value = 0;
  40. if(!light_file_read_uint64(target_path, &minimum_value))
  41. {
  42. return 0;
  43. }
  44. return minimum_value;
  45. }
  46. static light_device_enumerator_t* _light_find_enumerator(light_context_t *ctx, char const *comp)
  47. {
  48. for(uint64_t e = 0; e < ctx->num_enumerators; e++)
  49. {
  50. if(strncmp(comp, ctx->enumerators[e]->name, NAME_MAX) == 0)
  51. {
  52. return ctx->enumerators[e];
  53. }
  54. }
  55. return NULL;
  56. }
  57. static light_device_t* _light_find_device(light_device_enumerator_t *en, char const *comp)
  58. {
  59. for(uint64_t d = 0; d < en->num_devices; d++)
  60. {
  61. if(strncmp(comp, en->devices[d]->name, NAME_MAX) == 0)
  62. {
  63. return en->devices[d];
  64. }
  65. }
  66. return NULL;
  67. }
  68. static light_device_target_t* _light_find_target(light_device_t * dev, char const *comp)
  69. {
  70. for(uint64_t t = 0; t < dev->num_targets; t++)
  71. {
  72. if(strncmp(comp, dev->targets[t]->name, NAME_MAX) == 0)
  73. {
  74. return dev->targets[t];
  75. }
  76. }
  77. return NULL;
  78. }
  79. static bool _light_raw_to_percent(light_device_target_t *target, uint64_t inraw, double *outpercent)
  80. {
  81. double inraw_d = (double)inraw;
  82. uint64_t max_value = 0;
  83. if(!target->get_max_value(target, &max_value))
  84. {
  85. LIGHT_ERR("couldn't read from target");
  86. return false;
  87. }
  88. double max_value_d = (double)max_value;
  89. double percent = light_percent_clamp((inraw_d / max_value_d) * 100.0);
  90. *outpercent = percent;
  91. return true;
  92. }
  93. static bool _light_percent_to_raw(light_device_target_t *target, double inpercent, uint64_t *outraw)
  94. {
  95. uint64_t max_value = 0;
  96. if(!target->get_max_value(target, &max_value))
  97. {
  98. LIGHT_ERR("couldn't read from target");
  99. return false;
  100. }
  101. double max_value_d = (double)max_value;
  102. double target_value_d = max_value_d * (light_percent_clamp(inpercent) / 100.0);
  103. uint64_t target_value = LIGHT_CLAMP((uint64_t)target_value_d, 0, max_value);
  104. *outraw = target_value;
  105. return true;
  106. }
  107. static void _light_print_usage()
  108. {
  109. printf("Usage:\n"
  110. " light [OPTIONS] <COMMAND> [VALUE]\n"
  111. "\n"
  112. "Commands:\n"
  113. " -H, -h Show this help and exit\n"
  114. " -V Show program version and exit\n"
  115. " -L List available devices\n"
  116. " -A <value> Increase brightness by value\n"
  117. " -U <value> Decrease brightness by value\n"
  118. " -S <value> Set brightness to value\n"
  119. " -G Get brightness\n"
  120. " -N <value> Set minimum brightness to value\n"
  121. " -P Get minimum brightness\n"
  122. " -I Save the current brightness\n"
  123. " -O Restore the previously saved brightness\n"
  124. "\n"
  125. "Options:\n"
  126. " -r Interpret input and output values in raw mode\n"
  127. " -s <value> Specify device target path to use, use -L to list available\n"
  128. " -v <value> Specify the verbosity level (default 0)\n"
  129. " 0: Values only\n"
  130. " 1: Values, Errors.\n"
  131. " 2: Values, Errors, Warnings.\n"
  132. " 3: Values, Errors, Warnings, Notices.\n"
  133. "\n");
  134. printf("Copyright (C) %s %s\n", LIGHT_YEAR, LIGHT_AUTHOR);
  135. printf("This is free software, see the source for copying conditions. There is NO\n"
  136. "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\n"
  137. "\n");
  138. }
  139. static bool _light_set_context_command(light_context_t *ctx, LFUNCCOMMAND new_cmd)
  140. {
  141. if(ctx->run_params.command != NULL)
  142. {
  143. LIGHT_WARN("a command was already set. ignoring.");
  144. return false;
  145. }
  146. ctx->run_params.command = new_cmd;
  147. return true;
  148. }
  149. static bool _light_parse_arguments(light_context_t *ctx, int argc, char** argv)
  150. {
  151. int32_t curr_arg = -1;
  152. int32_t log_level = 0;
  153. char ctrl_name[NAME_MAX];
  154. bool need_value = false;
  155. bool need_target = true; // default cmd is get brightness
  156. snprintf(ctrl_name, sizeof(ctrl_name), "%s", "sysfs/backlight/auto");
  157. while((curr_arg = getopt(argc, argv, "HhVGSLMNPAUOIv:s:r")) != -1)
  158. {
  159. switch(curr_arg)
  160. {
  161. // Options
  162. case 'v':
  163. if (sscanf(optarg, "%i", &log_level) != 1)
  164. {
  165. fprintf(stderr, "-v argument is not an integer.\n\n");
  166. _light_print_usage();
  167. return false;
  168. }
  169. if (log_level < 0 || log_level > 3)
  170. {
  171. fprintf(stderr, "-v argument must be between 0 and 3.\n\n");
  172. _light_print_usage();
  173. return false;
  174. }
  175. light_loglevel = (light_loglevel_t)log_level;
  176. break;
  177. case 's':
  178. snprintf(ctrl_name, sizeof(ctrl_name), "%s", optarg);
  179. break;
  180. case 'r':
  181. ctx->run_params.raw_mode = true;
  182. break;
  183. // Commands
  184. case 'H':
  185. case 'h':
  186. _light_set_context_command(ctx, light_cmd_print_help);
  187. break;
  188. case 'V':
  189. _light_set_context_command(ctx, light_cmd_print_version);
  190. break;
  191. case 'G':
  192. _light_set_context_command(ctx, light_cmd_get_brightness);
  193. need_target = true;
  194. break;
  195. case 'S':
  196. _light_set_context_command(ctx, light_cmd_set_brightness);
  197. need_value = true;
  198. need_target = true;
  199. break;
  200. case 'L':
  201. _light_set_context_command(ctx, light_cmd_list_devices);
  202. break;
  203. case 'M':
  204. _light_set_context_command(ctx, light_cmd_get_max_brightness);
  205. need_target = true;
  206. break;
  207. case 'N':
  208. _light_set_context_command(ctx, light_cmd_set_min_brightness);
  209. need_target = true;
  210. need_value = true;
  211. break;
  212. case 'P':
  213. _light_set_context_command(ctx, light_cmd_get_min_brightness);
  214. need_target = true;
  215. break;
  216. case 'A':
  217. _light_set_context_command(ctx, light_cmd_add_brightness);
  218. need_target = true;
  219. need_value = true;
  220. break;
  221. case 'U':
  222. _light_set_context_command(ctx, light_cmd_sub_brightness);
  223. need_target = true;
  224. need_value = true;
  225. break;
  226. case 'O':
  227. _light_set_context_command(ctx, light_cmd_save_brightness);
  228. need_target = true;
  229. break;
  230. case 'I':
  231. _light_set_context_command(ctx, light_cmd_restore_brightness);
  232. need_target = true;
  233. break;
  234. }
  235. }
  236. if(ctx->run_params.command == NULL)
  237. {
  238. _light_set_context_command(ctx, light_cmd_get_brightness);
  239. }
  240. if(need_target)
  241. {
  242. light_device_target_t *curr_target = light_find_device_target(ctx, ctrl_name);
  243. if(curr_target == NULL)
  244. {
  245. fprintf(stderr, "couldn't find a device target at the path \"%s\". Use -L to find one.\n\n", ctrl_name);
  246. return false;
  247. }
  248. ctx->run_params.device_target = curr_target;
  249. }
  250. if(need_value)
  251. {
  252. if ( (argc - optind) != 1)
  253. {
  254. fprintf(stderr, "please specify a <value> for this command.\n\n");
  255. _light_print_usage();
  256. return false;
  257. }
  258. if (ctx->run_params.raw_mode)
  259. {
  260. if (sscanf(argv[optind], "%lu", &ctx->run_params.value) != 1)
  261. {
  262. fprintf(stderr, "<value> is not an integer.\n\n");
  263. _light_print_usage();
  264. return false;
  265. }
  266. }
  267. else
  268. {
  269. double percent_value = 0.0;
  270. if (sscanf(argv[optind], "%lf", &percent_value) != 1)
  271. {
  272. fprintf(stderr, "<value> is not a decimal.\n\n");
  273. _light_print_usage();
  274. return false;
  275. }
  276. percent_value = light_percent_clamp(percent_value);
  277. uint64_t raw_value = 0;
  278. if(!_light_percent_to_raw(ctx->run_params.device_target, percent_value, &raw_value))
  279. {
  280. LIGHT_ERR("failed to convert from percent to raw for device target");
  281. return false;
  282. }
  283. ctx->run_params.value = raw_value;
  284. }
  285. }
  286. return true;
  287. }
  288. /* API function definitions */
  289. light_context_t* light_initialize(int argc, char **argv)
  290. {
  291. light_context_t *new_ctx = malloc(sizeof(light_context_t));
  292. // Setup default values and runtime params
  293. new_ctx->enumerators = NULL;
  294. new_ctx->num_enumerators = 0;
  295. new_ctx->run_params.command = NULL;
  296. new_ctx->run_params.device_target = NULL;
  297. new_ctx->run_params.value = 0;
  298. new_ctx->run_params.raw_mode = false;
  299. // Setup the configuration folder
  300. // If we are root, use the system-wide configuration folder, otherwise try to find a user-specific folder, or fall back to ~/.config
  301. if (geteuid() == 0)
  302. {
  303. snprintf(new_ctx->sys_params.conf_dir, sizeof(new_ctx->sys_params.conf_dir), "%s", "/etc/light");
  304. }
  305. else
  306. {
  307. char *xdg_conf = getenv("XDG_CONFIG_HOME");
  308. if (xdg_conf != NULL)
  309. {
  310. snprintf(new_ctx->sys_params.conf_dir, sizeof(new_ctx->sys_params.conf_dir), "%s/light", xdg_conf);
  311. }
  312. else
  313. {
  314. snprintf(new_ctx->sys_params.conf_dir, sizeof(new_ctx->sys_params.conf_dir), "%s/.config/light", getenv("HOME"));
  315. }
  316. }
  317. // Make sure the configuration folder exists, otherwise attempt to create it
  318. int32_t rc = light_mkpath(new_ctx->sys_params.conf_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  319. if (rc && errno != EEXIST)
  320. {
  321. LIGHT_WARN("couldn't create configuration directory");
  322. return false;
  323. }
  324. // Create the built-in enumerators
  325. light_create_enumerator(new_ctx, "sysfs", &impl_sysfs_init, &impl_sysfs_free);
  326. //light_create_enumerator(new_ctx, "razer", &impl_razer_init, &impl_razer_free);
  327. // This is where we would create enumerators from plugins as well
  328. // 1. Run the plugins get_name() function to get its name
  329. // 2. Point to the plugins init() and free() functions when creating the enumerator
  330. // initialize all enumerators, this will create all the devices and their targets
  331. if(!light_init_enumerators(new_ctx))
  332. {
  333. LIGHT_WARN("failed to initialize all enumerators");
  334. }
  335. // Parse arguments
  336. if(!_light_parse_arguments(new_ctx, argc, argv))
  337. {
  338. LIGHT_ERR("failed to parse arguments");
  339. return NULL;
  340. }
  341. return new_ctx;
  342. }
  343. bool light_execute(light_context_t *ctx)
  344. {
  345. if(ctx->run_params.command == NULL)
  346. {
  347. LIGHT_ERR("run parameters command was null, can't execute");
  348. return false;
  349. }
  350. return ctx->run_params.command(ctx);
  351. }
  352. void light_free(light_context_t *ctx)
  353. {
  354. if(!light_free_enumerators(ctx))
  355. {
  356. LIGHT_WARN("failed to free all enumerators");
  357. }
  358. free(ctx);
  359. }
  360. light_device_enumerator_t * light_create_enumerator(light_context_t *ctx, char const * name, LFUNCENUMINIT init_func, LFUNCENUMFREE free_func)
  361. {
  362. // Create a new enumerator array
  363. uint64_t new_num_enumerators = ctx->num_enumerators + 1;
  364. light_device_enumerator_t **new_enumerators = malloc(new_num_enumerators * sizeof(light_device_enumerator_t*));
  365. // Copy old enumerator array to new one
  366. for(uint64_t i = 0; i < ctx->num_enumerators; i++)
  367. {
  368. new_enumerators[i] = ctx->enumerators[i];
  369. }
  370. // Allocate the new enumerator
  371. new_enumerators[ctx->num_enumerators] = malloc(sizeof(light_device_enumerator_t*));
  372. light_device_enumerator_t *returner = new_enumerators[ctx->num_enumerators];
  373. returner->devices = NULL;
  374. returner->num_devices = 0;
  375. returner->init = init_func;
  376. returner->free = free_func;
  377. snprintf(returner->name, sizeof(returner->name), "%s", name);
  378. // Free the old enumerator array, if needed
  379. if(ctx->enumerators != NULL)
  380. {
  381. free(ctx->enumerators);
  382. }
  383. // Replace the enumerator array with the new one
  384. ctx->enumerators = new_enumerators;
  385. ctx->num_enumerators = new_num_enumerators;
  386. // Return newly created device
  387. return returner;
  388. }
  389. bool light_init_enumerators(light_context_t *ctx)
  390. {
  391. bool success = true;
  392. for(uint64_t i = 0; i < ctx->num_enumerators; i++)
  393. {
  394. light_device_enumerator_t * curr_enumerator = ctx->enumerators[i];
  395. if(!curr_enumerator->init(curr_enumerator))
  396. {
  397. success = false;
  398. }
  399. }
  400. return success;
  401. }
  402. bool light_free_enumerators(light_context_t *ctx)
  403. {
  404. bool success = true;
  405. for(uint64_t i = 0; i < ctx->num_enumerators; i++)
  406. {
  407. light_device_enumerator_t * curr_enumerator = ctx->enumerators[i];
  408. if(!curr_enumerator->free(curr_enumerator))
  409. {
  410. success = false;
  411. }
  412. free(curr_enumerator);
  413. }
  414. free(ctx->enumerators);
  415. ctx->enumerators = NULL;
  416. ctx->num_enumerators = 0;
  417. return success;
  418. }
  419. void light_add_enumerator_device(light_device_enumerator_t *enumerator, light_device_t *new_device)
  420. {
  421. // Create a new device array
  422. uint64_t new_num_devices = enumerator->num_devices + 1;
  423. light_device_t **new_devices = malloc(new_num_devices * sizeof(light_device_t*));
  424. // Copy old device array to new one
  425. for(uint64_t i = 0; i < enumerator->num_devices; i++)
  426. {
  427. new_devices[i] = enumerator->devices[i];
  428. }
  429. // Set the new device
  430. new_devices[enumerator->num_devices] = new_device;
  431. // Free the old devices array, if needed
  432. if(enumerator->devices != NULL)
  433. {
  434. free(enumerator->devices);
  435. }
  436. // Replace the devices array with the new one
  437. enumerator->devices = new_devices;
  438. enumerator->num_devices = new_num_devices;
  439. new_device->enumerator = enumerator;
  440. }
  441. void light_add_device_target(light_device_t *device, light_device_target_t *new_target)
  442. {
  443. // Create a new targets array
  444. uint64_t new_num_targets = device->num_targets + 1;
  445. light_device_target_t **new_targets = malloc(new_num_targets * sizeof(light_device_target_t*));
  446. // Copy old targets array to new one
  447. for(uint64_t i = 0; i < device->num_targets; i++)
  448. {
  449. new_targets[i] = device->targets[i];
  450. }
  451. // Set the new target
  452. new_targets[device->num_targets] = new_target;
  453. // Free the old targets array, if needed
  454. if(device->targets != NULL)
  455. {
  456. free(device->targets);
  457. }
  458. // Replace the targets array with the new one
  459. device->targets= new_targets;
  460. device->num_targets = new_num_targets;
  461. new_target->device = device;
  462. }
  463. bool light_split_target_path(char const *in_path, light_target_path_t *out_path)
  464. {
  465. char const * begin = in_path;
  466. char const * end = strstr(begin, "/");
  467. if(end == NULL)
  468. {
  469. LIGHT_WARN("invalid path passed to split_target_path");
  470. return false;
  471. }
  472. size_t size = end - begin;
  473. strncpy(out_path->enumerator, begin, size);
  474. out_path->enumerator[size] = '\0';
  475. begin = end + 1;
  476. end = strstr(begin, "/");
  477. if(end == NULL)
  478. {
  479. LIGHT_WARN("invalid path passed to split_target_path");
  480. return false;
  481. }
  482. size = end - begin;
  483. strncpy(out_path->device, begin, size);
  484. out_path->device[size] = '\0';
  485. strcpy(out_path->target, end + 1);
  486. return true;
  487. }
  488. light_device_target_t* light_find_device_target(light_context_t *ctx, char const * name)
  489. {
  490. light_target_path_t new_path;
  491. light_split_target_path(name, &new_path);
  492. /*
  493. Uncomment to debug the split function
  494. printf("enumerator: %s %u\ndevice: %s %u\ntarget: %s %u\n",
  495. new_path.enumerator, strlen(new_path.enumerator),
  496. new_path.device, strlen(new_path.device),
  497. new_path.target, strlen(new_path.target));
  498. */
  499. // find a matching enumerator
  500. light_device_enumerator_t *enumerator = _light_find_enumerator(ctx, new_path.enumerator);
  501. if(enumerator == NULL)
  502. {
  503. LIGHT_WARN("no such enumerator, \"%s\"", new_path.enumerator);
  504. return NULL;
  505. }
  506. light_device_t *device = _light_find_device(enumerator, new_path.device);
  507. if(device == NULL)
  508. {
  509. LIGHT_WARN("no such device, \"%s\"", new_path.device);
  510. return NULL;
  511. }
  512. light_device_target_t *target = _light_find_target(device, new_path.target);
  513. if(target == NULL)
  514. {
  515. LIGHT_WARN("no such target, \"%s\"", new_path.target);
  516. return NULL;
  517. }
  518. return target;
  519. }
  520. bool light_cmd_print_help(light_context_t *ctx)
  521. {
  522. _light_print_usage();
  523. return true;
  524. }
  525. bool light_cmd_print_version(light_context_t *ctx)
  526. {
  527. printf("v%s\n", VERSION);
  528. return true;
  529. }
  530. bool light_cmd_list_devices(light_context_t *ctx)
  531. {
  532. printf("Listing device targets:\n");
  533. for(uint64_t enumerator = 0; enumerator < ctx->num_enumerators; enumerator++)
  534. {
  535. light_device_enumerator_t *curr_enumerator = ctx->enumerators[enumerator];
  536. for(uint64_t device = 0; device < curr_enumerator->num_devices; device++)
  537. {
  538. light_device_t *curr_device = curr_enumerator->devices[device];
  539. for(uint64_t target = 0; target < curr_device->num_targets; target++)
  540. {
  541. light_device_target_t *curr_target = curr_device->targets[target];
  542. printf("\t%s/%s/%s\n", curr_enumerator->name, curr_device->name, curr_target->name);
  543. }
  544. }
  545. }
  546. return true;
  547. }
  548. bool light_cmd_set_brightness(light_context_t *ctx)
  549. {
  550. light_device_target_t *target = ctx->run_params.device_target;
  551. if(target == NULL)
  552. {
  553. LIGHT_ERR("didn't have a valid target, programmer mistake");
  554. return false;
  555. }
  556. uint64_t mincap = _light_get_min_cap(ctx);
  557. uint64_t value = ctx->run_params.value;
  558. if(mincap > value)
  559. {
  560. value = mincap;
  561. }
  562. if(!target->set_value(target, value))
  563. {
  564. LIGHT_ERR("failed to write to target");
  565. return false;
  566. }
  567. return true;
  568. }
  569. bool light_cmd_get_brightness(light_context_t *ctx)
  570. {
  571. light_device_target_t *target = ctx->run_params.device_target;
  572. if(target == NULL)
  573. {
  574. LIGHT_ERR("didn't have a valid target, programmer mistake");
  575. return false;
  576. }
  577. uint64_t value = 0;
  578. if(!target->get_value(target, &value))
  579. {
  580. LIGHT_ERR("failed to read from target");
  581. return false;
  582. }
  583. if(ctx->run_params.raw_mode)
  584. {
  585. printf("%" PRIu64 "\n", value);
  586. }
  587. else
  588. {
  589. double percent = 0.0;
  590. if(!_light_raw_to_percent(target, value, &percent))
  591. {
  592. LIGHT_ERR("failed to convert from raw to percent from device target");
  593. return false;
  594. }
  595. printf("%.2f\n", percent);
  596. }
  597. return true;
  598. }
  599. bool light_cmd_get_max_brightness(light_context_t *ctx)
  600. {
  601. light_device_target_t *target = ctx->run_params.device_target;
  602. if(target == NULL)
  603. {
  604. LIGHT_ERR("didn't have a valid target, programmer mistake");
  605. return false;
  606. }
  607. if(!ctx->run_params.raw_mode)
  608. {
  609. printf("100.0\n");
  610. return true;
  611. }
  612. uint64_t max_value = 0;
  613. if(!target->get_max_value(target, &max_value))
  614. {
  615. LIGHT_ERR("failed to read from device target");
  616. return false;
  617. }
  618. printf("%" PRIu64 "\n", max_value);
  619. return true;
  620. }
  621. bool light_cmd_set_min_brightness(light_context_t *ctx)
  622. {
  623. char target_path[NAME_MAX];
  624. _light_get_target_path(ctx, target_path, sizeof(target_path));
  625. // Make sure the target folder exists, otherwise attempt to create it
  626. int32_t rc = light_mkpath(target_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  627. if (rc && errno != EEXIST)
  628. {
  629. LIGHT_ERR("couldn't create target directory for minimum brightness");
  630. return false;
  631. }
  632. char target_filepath[NAME_MAX];
  633. _light_get_target_file(ctx, target_filepath, sizeof(target_filepath), "minimum");
  634. if(!light_file_write_uint64(target_filepath, ctx->run_params.value))
  635. {
  636. LIGHT_ERR("couldn't write value to minimum file");
  637. return false;
  638. }
  639. return true;
  640. }
  641. bool light_cmd_get_min_brightness(light_context_t *ctx)
  642. {
  643. char target_path[NAME_MAX];
  644. _light_get_target_file(ctx, target_path, sizeof(target_path), "minimum");
  645. uint64_t minimum_value = 0;
  646. if(!light_file_read_uint64(target_path, &minimum_value))
  647. {
  648. if(ctx->run_params.raw_mode)
  649. {
  650. printf("0\n");
  651. }
  652. else
  653. {
  654. printf("0.00\n");
  655. }
  656. return true;
  657. }
  658. if(ctx->run_params.raw_mode)
  659. {
  660. printf("%" PRIu64 "\n", minimum_value);
  661. }
  662. else
  663. {
  664. double minimum_d = 0.0;
  665. if(!_light_raw_to_percent(ctx->run_params.device_target, minimum_value, &minimum_d))
  666. {
  667. LIGHT_ERR("failed to convert value from raw to percent for device target");
  668. return false;
  669. }
  670. printf("%.2f\n", minimum_d);
  671. }
  672. return true;
  673. }
  674. bool light_cmd_add_brightness(light_context_t *ctx)
  675. {
  676. light_device_target_t *target = ctx->run_params.device_target;
  677. if(target == NULL)
  678. {
  679. LIGHT_ERR("didn't have a valid target, programmer mistake");
  680. return false;
  681. }
  682. uint64_t value = 0;
  683. if(!target->get_value(target, &value))
  684. {
  685. LIGHT_ERR("failed to read from target");
  686. return false;
  687. }
  688. uint64_t max_value = 0;
  689. if(!target->get_max_value(target, &max_value))
  690. {
  691. LIGHT_ERR("failed to read from target");
  692. return false;
  693. }
  694. value += ctx->run_params.value;
  695. uint64_t mincap = _light_get_min_cap(ctx);
  696. if(mincap > value)
  697. {
  698. value = mincap;
  699. }
  700. if(value > max_value)
  701. {
  702. value = max_value;
  703. }
  704. if(!target->set_value(target, value))
  705. {
  706. LIGHT_ERR("failed to write to target");
  707. return false;
  708. }
  709. return true;
  710. }
  711. bool light_cmd_sub_brightness(light_context_t *ctx)
  712. {
  713. light_device_target_t *target = ctx->run_params.device_target;
  714. if(target == NULL)
  715. {
  716. LIGHT_ERR("didn't have a valid target, programmer mistake");
  717. return false;
  718. }
  719. uint64_t value = 0;
  720. if(!target->get_value(target, &value))
  721. {
  722. LIGHT_ERR("failed to read from target");
  723. return false;
  724. }
  725. if(value > ctx->run_params.value)
  726. {
  727. value -= ctx->run_params.value;
  728. }
  729. else
  730. {
  731. value = 0;
  732. }
  733. uint64_t mincap = _light_get_min_cap(ctx);
  734. if(mincap > value)
  735. {
  736. value = mincap;
  737. }
  738. if(!target->set_value(target, value))
  739. {
  740. LIGHT_ERR("failed to write to target");
  741. return false;
  742. }
  743. return true;
  744. }
  745. bool light_cmd_save_brightness(light_context_t *ctx)
  746. {
  747. char target_path[NAME_MAX];
  748. _light_get_target_path(ctx, target_path, sizeof(target_path));
  749. // Make sure the target folder exists, otherwise attempt to create it
  750. int32_t rc = light_mkpath(target_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  751. if (rc && errno != EEXIST)
  752. {
  753. LIGHT_ERR("couldn't create target directory for save brightness");
  754. return false;
  755. }
  756. char target_filepath[NAME_MAX];
  757. _light_get_target_file(ctx, target_filepath, sizeof(target_filepath), "save");
  758. uint64_t curr_value = 0;
  759. if(!ctx->run_params.device_target->get_value(ctx->run_params.device_target, &curr_value))
  760. {
  761. LIGHT_ERR("couldn't read from device target");
  762. return false;
  763. }
  764. if(!light_file_write_uint64(target_filepath, curr_value))
  765. {
  766. LIGHT_ERR("couldn't write value to savefile");
  767. return false;
  768. }
  769. return true;
  770. }
  771. bool light_cmd_restore_brightness(light_context_t *ctx)
  772. {
  773. char target_path[NAME_MAX];
  774. _light_get_target_file(ctx, target_path, sizeof(target_path), "save");
  775. uint64_t saved_value = 0;
  776. if(!light_file_read_uint64(target_path, &saved_value))
  777. {
  778. LIGHT_ERR("couldn't read value from savefile");
  779. return false;
  780. }
  781. uint64_t mincap = _light_get_min_cap(ctx);
  782. if(mincap > saved_value)
  783. {
  784. saved_value = mincap;
  785. }
  786. if(!ctx->run_params.device_target->set_value(ctx->run_params.device_target, saved_value))
  787. {
  788. LIGHT_ERR("couldn't write saved value to device target");
  789. return false;
  790. }
  791. return true;
  792. }