light.c 28KB

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