light.c 29KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031
  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. bool specified_target = false;
  200. snprintf(ctrl_name, sizeof(ctrl_name), "%s", "sysfs/backlight/auto");
  201. while((curr_arg = getopt(argc, argv, "HhVGSLMNPAUOIv:s:r")) != -1)
  202. {
  203. switch(curr_arg)
  204. {
  205. // Options
  206. case 'v':
  207. if (sscanf(optarg, "%i", &log_level) != 1)
  208. {
  209. fprintf(stderr, "-v argument is not an integer.\n\n");
  210. _light_print_usage();
  211. return false;
  212. }
  213. if (log_level < 0 || log_level > 3)
  214. {
  215. fprintf(stderr, "-v argument must be between 0 and 3.\n\n");
  216. _light_print_usage();
  217. return false;
  218. }
  219. light_loglevel = (light_loglevel_t)log_level;
  220. break;
  221. case 's':
  222. snprintf(ctrl_name, sizeof(ctrl_name), "%s", optarg);
  223. specified_target = true;
  224. break;
  225. case 'r':
  226. ctx->run_params.raw_mode = true;
  227. break;
  228. // Commands
  229. case 'H':
  230. case 'h':
  231. _light_set_context_command(ctx, light_cmd_print_help);
  232. break;
  233. case 'V':
  234. _light_set_context_command(ctx, light_cmd_print_version);
  235. break;
  236. case 'G':
  237. _light_set_context_command(ctx, light_cmd_get_brightness);
  238. need_target = true;
  239. break;
  240. case 'S':
  241. _light_set_context_command(ctx, light_cmd_set_brightness);
  242. need_value = true;
  243. need_target = true;
  244. break;
  245. case 'L':
  246. _light_set_context_command(ctx, light_cmd_list_devices);
  247. break;
  248. case 'M':
  249. _light_set_context_command(ctx, light_cmd_get_max_brightness);
  250. need_target = true;
  251. break;
  252. case 'N':
  253. _light_set_context_command(ctx, light_cmd_set_min_brightness);
  254. need_target = true;
  255. need_value = true;
  256. break;
  257. case 'P':
  258. _light_set_context_command(ctx, light_cmd_get_min_brightness);
  259. need_target = true;
  260. break;
  261. case 'A':
  262. _light_set_context_command(ctx, light_cmd_add_brightness);
  263. need_target = true;
  264. need_value = true;
  265. break;
  266. case 'U':
  267. _light_set_context_command(ctx, light_cmd_sub_brightness);
  268. need_target = true;
  269. need_value = true;
  270. break;
  271. case 'O':
  272. _light_set_context_command(ctx, light_cmd_save_brightness);
  273. need_target = true;
  274. break;
  275. case 'I':
  276. _light_set_context_command(ctx, light_cmd_restore_brightness);
  277. need_target = true;
  278. break;
  279. }
  280. }
  281. if(ctx->run_params.command == NULL)
  282. {
  283. _light_set_context_command(ctx, light_cmd_get_brightness);
  284. }
  285. if(need_target)
  286. {
  287. light_device_target_t *curr_target = light_find_device_target(ctx, ctrl_name);
  288. if(curr_target == NULL)
  289. {
  290. if(specified_target)
  291. {
  292. fprintf(stderr, "We couldn't find the specified device target at the path \"%s\". Use -L to find one.\n\n", ctrl_name);
  293. return false;
  294. }
  295. else
  296. {
  297. fprintf(stderr, "No backlight controller was found, so we could not decide an automatic target. The current command will have no effect. Please use -L to find a target and then specify it with -s.\n\n");
  298. curr_target = light_find_device_target(ctx, "util/test/dryrun");
  299. }
  300. }
  301. ctx->run_params.device_target = curr_target;
  302. }
  303. if(need_value)
  304. {
  305. if ( (argc - optind) != 1)
  306. {
  307. fprintf(stderr, "please specify a <value> for this command.\n\n");
  308. _light_print_usage();
  309. return false;
  310. }
  311. if (ctx->run_params.raw_mode)
  312. {
  313. if (sscanf(argv[optind], "%lu", &ctx->run_params.value) != 1)
  314. {
  315. fprintf(stderr, "<value> is not an integer.\n\n");
  316. _light_print_usage();
  317. return false;
  318. }
  319. }
  320. else
  321. {
  322. double percent_value = 0.0;
  323. if (sscanf(argv[optind], "%lf", &percent_value) != 1)
  324. {
  325. fprintf(stderr, "<value> is not a decimal.\n\n");
  326. _light_print_usage();
  327. return false;
  328. }
  329. percent_value = light_percent_clamp(percent_value);
  330. uint64_t raw_value = 0;
  331. if(!_light_percent_to_raw(ctx->run_params.device_target, percent_value, &raw_value))
  332. {
  333. LIGHT_ERR("failed to convert from percent to raw for device target");
  334. return false;
  335. }
  336. ctx->run_params.value = raw_value;
  337. }
  338. }
  339. return true;
  340. }
  341. /* API function definitions */
  342. light_context_t* light_initialize(int argc, char **argv)
  343. {
  344. light_context_t *new_ctx = malloc(sizeof(light_context_t));
  345. // Setup default values and runtime params
  346. new_ctx->enumerators = NULL;
  347. new_ctx->num_enumerators = 0;
  348. new_ctx->run_params.command = NULL;
  349. new_ctx->run_params.device_target = NULL;
  350. new_ctx->run_params.value = 0;
  351. new_ctx->run_params.raw_mode = false;
  352. // Setup the configuration folder
  353. // If we are root, use the system-wide configuration folder, otherwise try to find a user-specific folder, or fall back to ~/.config
  354. if (geteuid() == 0)
  355. {
  356. snprintf(new_ctx->sys_params.conf_dir, sizeof(new_ctx->sys_params.conf_dir), "%s", "/etc/light");
  357. }
  358. else
  359. {
  360. char *xdg_conf = getenv("XDG_CONFIG_HOME");
  361. if (xdg_conf != NULL)
  362. {
  363. snprintf(new_ctx->sys_params.conf_dir, sizeof(new_ctx->sys_params.conf_dir), "%s/light", xdg_conf);
  364. }
  365. else
  366. {
  367. snprintf(new_ctx->sys_params.conf_dir, sizeof(new_ctx->sys_params.conf_dir), "%s/.config/light", getenv("HOME"));
  368. }
  369. }
  370. // Make sure the configuration folder exists, otherwise attempt to create it
  371. int32_t rc = light_mkpath(new_ctx->sys_params.conf_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  372. if (rc && errno != EEXIST)
  373. {
  374. LIGHT_WARN("couldn't create configuration directory");
  375. return false;
  376. }
  377. // Create the built-in enumerators
  378. light_create_enumerator(new_ctx, "sysfs", &impl_sysfs_init, &impl_sysfs_free);
  379. light_create_enumerator(new_ctx, "util", &impl_util_init, &impl_util_free);
  380. light_create_enumerator(new_ctx, "razer", &impl_razer_init, &impl_razer_free);
  381. // This is where we would create enumerators from plugins as well
  382. // 1. Run the plugins get_name() function to get its name
  383. // 2. Point to the plugins init() and free() functions when creating the enumerator
  384. // initialize all enumerators, this will create all the devices and their targets
  385. if(!light_init_enumerators(new_ctx))
  386. {
  387. LIGHT_WARN("failed to initialize all enumerators");
  388. }
  389. // Parse arguments
  390. if(!_light_parse_arguments(new_ctx, argc, argv))
  391. {
  392. LIGHT_ERR("failed to parse arguments");
  393. return NULL;
  394. }
  395. return new_ctx;
  396. }
  397. bool light_execute(light_context_t *ctx)
  398. {
  399. if(ctx->run_params.command == NULL)
  400. {
  401. LIGHT_ERR("run parameters command was null, can't execute");
  402. return false;
  403. }
  404. return ctx->run_params.command(ctx);
  405. }
  406. void light_free(light_context_t *ctx)
  407. {
  408. if(!light_free_enumerators(ctx))
  409. {
  410. LIGHT_WARN("failed to free all enumerators");
  411. }
  412. free(ctx);
  413. }
  414. light_device_enumerator_t * light_create_enumerator(light_context_t *ctx, char const * name, LFUNCENUMINIT init_func, LFUNCENUMFREE free_func)
  415. {
  416. // Create a new enumerator array
  417. uint64_t new_num_enumerators = ctx->num_enumerators + 1;
  418. light_device_enumerator_t **new_enumerators = malloc(new_num_enumerators * sizeof(light_device_enumerator_t*));
  419. // Copy old enumerator array to new one
  420. for(uint64_t i = 0; i < ctx->num_enumerators; i++)
  421. {
  422. new_enumerators[i] = ctx->enumerators[i];
  423. }
  424. // Allocate the new enumerator
  425. new_enumerators[ctx->num_enumerators] = malloc(sizeof(light_device_enumerator_t));
  426. light_device_enumerator_t *returner = new_enumerators[ctx->num_enumerators];
  427. returner->devices = NULL;
  428. returner->num_devices = 0;
  429. returner->init = init_func;
  430. returner->free = free_func;
  431. snprintf(returner->name, sizeof(returner->name), "%s", name);
  432. // Free the old enumerator array, if needed
  433. if(ctx->enumerators != NULL)
  434. {
  435. free(ctx->enumerators);
  436. }
  437. // Replace the enumerator array with the new one
  438. ctx->enumerators = new_enumerators;
  439. ctx->num_enumerators = new_num_enumerators;
  440. // Return newly created device
  441. return returner;
  442. }
  443. bool light_init_enumerators(light_context_t *ctx)
  444. {
  445. bool success = true;
  446. for(uint64_t i = 0; i < ctx->num_enumerators; i++)
  447. {
  448. light_device_enumerator_t * curr_enumerator = ctx->enumerators[i];
  449. if(!curr_enumerator->init(curr_enumerator))
  450. {
  451. success = false;
  452. }
  453. }
  454. return success;
  455. }
  456. bool light_free_enumerators(light_context_t *ctx)
  457. {
  458. bool success = true;
  459. for(uint64_t i = 0; i < ctx->num_enumerators; i++)
  460. {
  461. light_device_enumerator_t * curr_enumerator = ctx->enumerators[i];
  462. if(!curr_enumerator->free(curr_enumerator))
  463. {
  464. success = false;
  465. }
  466. if(curr_enumerator->devices != NULL)
  467. {
  468. for(uint64_t d = 0; d < curr_enumerator->num_devices; d++)
  469. {
  470. light_delete_device(curr_enumerator->devices[d]);
  471. }
  472. free(curr_enumerator->devices);
  473. curr_enumerator->devices = NULL;
  474. }
  475. free(curr_enumerator);
  476. }
  477. free(ctx->enumerators);
  478. ctx->enumerators = NULL;
  479. ctx->num_enumerators = 0;
  480. return success;
  481. }
  482. bool light_split_target_path(char const *in_path, light_target_path_t *out_path)
  483. {
  484. char const * begin = in_path;
  485. char const * end = strstr(begin, "/");
  486. if(end == NULL)
  487. {
  488. LIGHT_WARN("invalid path passed to split_target_path");
  489. return false;
  490. }
  491. size_t size = end - begin;
  492. strncpy(out_path->enumerator, begin, size);
  493. out_path->enumerator[size] = '\0';
  494. begin = end + 1;
  495. end = strstr(begin, "/");
  496. if(end == NULL)
  497. {
  498. LIGHT_WARN("invalid path passed to split_target_path");
  499. return false;
  500. }
  501. size = end - begin;
  502. strncpy(out_path->device, begin, size);
  503. out_path->device[size] = '\0';
  504. strcpy(out_path->target, end + 1);
  505. return true;
  506. }
  507. light_device_target_t* light_find_device_target(light_context_t *ctx, char const * name)
  508. {
  509. light_target_path_t new_path;
  510. light_split_target_path(name, &new_path);
  511. /*
  512. Uncomment to debug the split function
  513. printf("enumerator: %s %u\ndevice: %s %u\ntarget: %s %u\n",
  514. new_path.enumerator, strlen(new_path.enumerator),
  515. new_path.device, strlen(new_path.device),
  516. new_path.target, strlen(new_path.target));
  517. */
  518. // find a matching enumerator
  519. light_device_enumerator_t *enumerator = _light_find_enumerator(ctx, new_path.enumerator);
  520. if(enumerator == NULL)
  521. {
  522. LIGHT_WARN("no such enumerator, \"%s\"", new_path.enumerator);
  523. return NULL;
  524. }
  525. light_device_t *device = _light_find_device(enumerator, new_path.device);
  526. if(device == NULL)
  527. {
  528. LIGHT_WARN("no such device, \"%s\"", new_path.device);
  529. return NULL;
  530. }
  531. light_device_target_t *target = _light_find_target(device, new_path.target);
  532. if(target == NULL)
  533. {
  534. LIGHT_WARN("no such target, \"%s\"", new_path.target);
  535. return NULL;
  536. }
  537. return target;
  538. }
  539. bool light_cmd_print_help(light_context_t *ctx)
  540. {
  541. _light_print_usage();
  542. return true;
  543. }
  544. bool light_cmd_print_version(light_context_t *ctx)
  545. {
  546. printf("v%s\n", VERSION);
  547. return true;
  548. }
  549. bool light_cmd_list_devices(light_context_t *ctx)
  550. {
  551. printf("Listing device targets:\n");
  552. for(uint64_t enumerator = 0; enumerator < ctx->num_enumerators; enumerator++)
  553. {
  554. light_device_enumerator_t *curr_enumerator = ctx->enumerators[enumerator];
  555. for(uint64_t device = 0; device < curr_enumerator->num_devices; device++)
  556. {
  557. light_device_t *curr_device = curr_enumerator->devices[device];
  558. for(uint64_t target = 0; target < curr_device->num_targets; target++)
  559. {
  560. light_device_target_t *curr_target = curr_device->targets[target];
  561. printf("\t%s/%s/%s\n", curr_enumerator->name, curr_device->name, curr_target->name);
  562. }
  563. }
  564. }
  565. return true;
  566. }
  567. bool light_cmd_set_brightness(light_context_t *ctx)
  568. {
  569. light_device_target_t *target = ctx->run_params.device_target;
  570. if(target == NULL)
  571. {
  572. LIGHT_ERR("didn't have a valid target, programmer mistake");
  573. return false;
  574. }
  575. uint64_t mincap = _light_get_min_cap(ctx);
  576. uint64_t value = ctx->run_params.value;
  577. if(mincap > value)
  578. {
  579. value = mincap;
  580. }
  581. if(!target->set_value(target, value))
  582. {
  583. LIGHT_ERR("failed to write to target");
  584. return false;
  585. }
  586. return true;
  587. }
  588. bool light_cmd_get_brightness(light_context_t *ctx)
  589. {
  590. light_device_target_t *target = ctx->run_params.device_target;
  591. if(target == NULL)
  592. {
  593. LIGHT_ERR("didn't have a valid target, programmer mistake");
  594. return false;
  595. }
  596. uint64_t value = 0;
  597. if(!target->get_value(target, &value))
  598. {
  599. LIGHT_ERR("failed to read from target");
  600. return false;
  601. }
  602. if(ctx->run_params.raw_mode)
  603. {
  604. printf("%" PRIu64 "\n", value);
  605. }
  606. else
  607. {
  608. double percent = 0.0;
  609. if(!_light_raw_to_percent(target, value, &percent))
  610. {
  611. LIGHT_ERR("failed to convert from raw to percent from device target");
  612. return false;
  613. }
  614. printf("%.2f\n", percent);
  615. }
  616. return true;
  617. }
  618. bool light_cmd_get_max_brightness(light_context_t *ctx)
  619. {
  620. light_device_target_t *target = ctx->run_params.device_target;
  621. if(target == NULL)
  622. {
  623. LIGHT_ERR("didn't have a valid target, programmer mistake");
  624. return false;
  625. }
  626. if(!ctx->run_params.raw_mode)
  627. {
  628. printf("100.0\n");
  629. return true;
  630. }
  631. uint64_t max_value = 0;
  632. if(!target->get_max_value(target, &max_value))
  633. {
  634. LIGHT_ERR("failed to read from device target");
  635. return false;
  636. }
  637. printf("%" PRIu64 "\n", max_value);
  638. return true;
  639. }
  640. bool light_cmd_set_min_brightness(light_context_t *ctx)
  641. {
  642. char target_path[NAME_MAX];
  643. _light_get_target_path(ctx, target_path, sizeof(target_path));
  644. // Make sure the target folder exists, otherwise attempt to create it
  645. int32_t rc = light_mkpath(target_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  646. if (rc && errno != EEXIST)
  647. {
  648. LIGHT_ERR("couldn't create target directory for minimum brightness");
  649. return false;
  650. }
  651. char target_filepath[NAME_MAX];
  652. _light_get_target_file(ctx, target_filepath, sizeof(target_filepath), "minimum");
  653. if(!light_file_write_uint64(target_filepath, ctx->run_params.value))
  654. {
  655. LIGHT_ERR("couldn't write value to minimum file");
  656. return false;
  657. }
  658. return true;
  659. }
  660. bool light_cmd_get_min_brightness(light_context_t *ctx)
  661. {
  662. char target_path[NAME_MAX];
  663. _light_get_target_file(ctx, target_path, sizeof(target_path), "minimum");
  664. uint64_t minimum_value = 0;
  665. if(!light_file_read_uint64(target_path, &minimum_value))
  666. {
  667. if(ctx->run_params.raw_mode)
  668. {
  669. printf("0\n");
  670. }
  671. else
  672. {
  673. printf("0.00\n");
  674. }
  675. return true;
  676. }
  677. if(ctx->run_params.raw_mode)
  678. {
  679. printf("%" PRIu64 "\n", minimum_value);
  680. }
  681. else
  682. {
  683. double minimum_d = 0.0;
  684. if(!_light_raw_to_percent(ctx->run_params.device_target, minimum_value, &minimum_d))
  685. {
  686. LIGHT_ERR("failed to convert value from raw to percent for device target");
  687. return false;
  688. }
  689. printf("%.2f\n", minimum_d);
  690. }
  691. return true;
  692. }
  693. bool light_cmd_add_brightness(light_context_t *ctx)
  694. {
  695. light_device_target_t *target = ctx->run_params.device_target;
  696. if(target == NULL)
  697. {
  698. LIGHT_ERR("didn't have a valid target, programmer mistake");
  699. return false;
  700. }
  701. uint64_t value = 0;
  702. if(!target->get_value(target, &value))
  703. {
  704. LIGHT_ERR("failed to read from target");
  705. return false;
  706. }
  707. uint64_t max_value = 0;
  708. if(!target->get_max_value(target, &max_value))
  709. {
  710. LIGHT_ERR("failed to read from target");
  711. return false;
  712. }
  713. value += ctx->run_params.value;
  714. uint64_t mincap = _light_get_min_cap(ctx);
  715. if(mincap > value)
  716. {
  717. value = mincap;
  718. }
  719. if(value > max_value)
  720. {
  721. value = max_value;
  722. }
  723. if(!target->set_value(target, value))
  724. {
  725. LIGHT_ERR("failed to write to target");
  726. return false;
  727. }
  728. return true;
  729. }
  730. bool light_cmd_sub_brightness(light_context_t *ctx)
  731. {
  732. light_device_target_t *target = ctx->run_params.device_target;
  733. if(target == NULL)
  734. {
  735. LIGHT_ERR("didn't have a valid target, programmer mistake");
  736. return false;
  737. }
  738. uint64_t value = 0;
  739. if(!target->get_value(target, &value))
  740. {
  741. LIGHT_ERR("failed to read from target");
  742. return false;
  743. }
  744. if(value > ctx->run_params.value)
  745. {
  746. value -= ctx->run_params.value;
  747. }
  748. else
  749. {
  750. value = 0;
  751. }
  752. uint64_t mincap = _light_get_min_cap(ctx);
  753. if(mincap > value)
  754. {
  755. value = mincap;
  756. }
  757. if(!target->set_value(target, value))
  758. {
  759. LIGHT_ERR("failed to write to target");
  760. return false;
  761. }
  762. return true;
  763. }
  764. bool light_cmd_save_brightness(light_context_t *ctx)
  765. {
  766. char target_path[NAME_MAX];
  767. _light_get_target_path(ctx, target_path, sizeof(target_path));
  768. // Make sure the target folder exists, otherwise attempt to create it
  769. int32_t rc = light_mkpath(target_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  770. if (rc && errno != EEXIST)
  771. {
  772. LIGHT_ERR("couldn't create target directory for save brightness");
  773. return false;
  774. }
  775. char target_filepath[NAME_MAX];
  776. _light_get_target_file(ctx, target_filepath, sizeof(target_filepath), "save");
  777. uint64_t curr_value = 0;
  778. if(!ctx->run_params.device_target->get_value(ctx->run_params.device_target, &curr_value))
  779. {
  780. LIGHT_ERR("couldn't read from device target");
  781. return false;
  782. }
  783. if(!light_file_write_uint64(target_filepath, curr_value))
  784. {
  785. LIGHT_ERR("couldn't write value to savefile");
  786. return false;
  787. }
  788. return true;
  789. }
  790. bool light_cmd_restore_brightness(light_context_t *ctx)
  791. {
  792. char target_path[NAME_MAX];
  793. _light_get_target_file(ctx, target_path, sizeof(target_path), "save");
  794. uint64_t saved_value = 0;
  795. if(!light_file_read_uint64(target_path, &saved_value))
  796. {
  797. LIGHT_ERR("couldn't read value from savefile");
  798. return false;
  799. }
  800. uint64_t mincap = _light_get_min_cap(ctx);
  801. if(mincap > saved_value)
  802. {
  803. saved_value = mincap;
  804. }
  805. if(!ctx->run_params.device_target->set_value(ctx->run_params.device_target, saved_value))
  806. {
  807. LIGHT_ERR("couldn't write saved value to device target");
  808. return false;
  809. }
  810. return true;
  811. }
  812. light_device_t *light_create_device(light_device_enumerator_t *enumerator, char const *name, void *device_data)
  813. {
  814. light_device_t *new_device = malloc(sizeof(light_device_t));
  815. new_device->enumerator = enumerator;
  816. new_device->targets = NULL;
  817. new_device->num_targets = 0;
  818. new_device->device_data = device_data;
  819. snprintf(new_device->name, sizeof(new_device->name), "%s", name);
  820. _light_add_enumerator_device(enumerator, new_device);
  821. return new_device;
  822. }
  823. void light_delete_device(light_device_t *device)
  824. {
  825. for(uint64_t i = 0; i < device->num_targets; i++)
  826. {
  827. light_delete_device_target(device->targets[i]);
  828. }
  829. if(device->targets != NULL)
  830. {
  831. free(device->targets);
  832. }
  833. if(device->device_data != NULL)
  834. {
  835. free(device->device_data);
  836. }
  837. free(device);
  838. }
  839. 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)
  840. {
  841. light_device_target_t *new_target = malloc(sizeof(light_device_target_t));
  842. new_target->device = device;
  843. new_target->set_value = setfunc;
  844. new_target->get_value = getfunc;
  845. new_target->get_max_value = getmaxfunc;
  846. new_target->custom_command = cmdfunc;
  847. new_target->device_target_data = target_data;
  848. snprintf(new_target->name, sizeof(new_target->name), "%s", name);
  849. _light_add_device_target(device, new_target);
  850. return new_target;
  851. }
  852. void light_delete_device_target(light_device_target_t *device_target)
  853. {
  854. if(device_target->device_target_data != NULL)
  855. {
  856. free(device_target->device_target_data);
  857. }
  858. free(device_target);
  859. }