light.c 28KB

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