light.c 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  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, target_value;
  139. if(!target->get_max_value(target, &max_value))
  140. {
  141. LIGHT_ERR("couldn't read from target");
  142. return false;
  143. }
  144. inpercent = light_percent_clamp(inpercent);
  145. /* Round to the nearest integer by adding 0.5 after division.
  146. Reduces error introduced by truncation after multiple increments or
  147. decrements. */
  148. target_value = max_value * inpercent / 100.0 + 0.5;
  149. *outraw = LIGHT_CLAMP(target_value, 0, max_value);
  150. return true;
  151. }
  152. static void _light_print_usage()
  153. {
  154. printf("Usage:\n"
  155. " light [OPTIONS] [VALUE]\n"
  156. "\n"
  157. "Commands:\n"
  158. " -H, -h Show this help and exit\n"
  159. " -V Show program version and exit\n"
  160. " -L List available devices\n"
  161. " -A Increase brightness by value\n"
  162. " -U Decrease brightness by value\n"
  163. " -T Multiply brightness by value (can be a non-whole number, ignores raw mode)\n"
  164. " -S Set brightness to value\n"
  165. " -G Get brightness\n"
  166. " -N Set minimum brightness to value\n"
  167. " -P Get minimum brightness\n"
  168. " -O Save the current brightness\n"
  169. " -I Restore the previously saved brightness\n"
  170. "\n"
  171. "Options:\n"
  172. " -r Interpret input and output values in raw mode (ignored for -T)\n"
  173. " -s Specify device target path to use, use -L to list available\n"
  174. " -v Specify the verbosity level (default 0)\n"
  175. " 0: Values only\n"
  176. " 1: Values, Errors.\n"
  177. " 2: Values, Errors, Warnings.\n"
  178. " 3: Values, Errors, Warnings, Notices.\n"
  179. "\n");
  180. printf("Copyright (C) %s %s\n", LIGHT_YEAR, LIGHT_AUTHOR);
  181. printf("This is free software, see the source for copying conditions. There is NO\n"
  182. "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\n"
  183. "\n");
  184. }
  185. static bool _light_set_context_command(light_context_t *ctx, LFUNCCOMMAND new_cmd)
  186. {
  187. if(ctx->run_params.command != NULL)
  188. {
  189. LIGHT_WARN("a command was already set. ignoring.");
  190. return false;
  191. }
  192. ctx->run_params.command = new_cmd;
  193. return true;
  194. }
  195. static bool _light_parse_arguments(light_context_t *ctx, int argc, char** argv)
  196. {
  197. int32_t curr_arg = -1;
  198. int32_t log_level = 0;
  199. char ctrl_name[NAME_MAX];
  200. bool need_value = false;
  201. bool need_float_value = false;
  202. bool need_target = true; // default cmd is get brightness
  203. bool specified_target = false;
  204. snprintf(ctrl_name, sizeof(ctrl_name), "%s", "sysfs/backlight/auto");
  205. while((curr_arg = getopt(argc, argv, "HhVGSLMNPAUTOIv:s:r")) != -1)
  206. {
  207. switch(curr_arg)
  208. {
  209. // Options
  210. case 'v':
  211. if(sscanf(optarg, "%i", &log_level) != 1)
  212. {
  213. fprintf(stderr, "-v argument is not an integer.\n\n");
  214. _light_print_usage();
  215. return false;
  216. }
  217. if(log_level < 0 || log_level > 3)
  218. {
  219. fprintf(stderr, "-v argument must be between 0 and 3.\n\n");
  220. _light_print_usage();
  221. return false;
  222. }
  223. light_loglevel = (light_loglevel_t)log_level;
  224. break;
  225. case 's':
  226. snprintf(ctrl_name, sizeof(ctrl_name), "%s", optarg);
  227. specified_target = true;
  228. break;
  229. case 'r':
  230. ctx->run_params.raw_mode = true;
  231. break;
  232. // Commands
  233. case 'H':
  234. case 'h':
  235. _light_set_context_command(ctx, light_cmd_print_help);
  236. break;
  237. case 'V':
  238. _light_set_context_command(ctx, light_cmd_print_version);
  239. break;
  240. case 'G':
  241. _light_set_context_command(ctx, light_cmd_get_brightness);
  242. need_target = true;
  243. break;
  244. case 'S':
  245. _light_set_context_command(ctx, light_cmd_set_brightness);
  246. need_value = true;
  247. need_target = true;
  248. break;
  249. case 'L':
  250. _light_set_context_command(ctx, light_cmd_list_devices);
  251. break;
  252. case 'M':
  253. _light_set_context_command(ctx, light_cmd_get_max_brightness);
  254. need_target = true;
  255. break;
  256. case 'N':
  257. _light_set_context_command(ctx, light_cmd_set_min_brightness);
  258. need_target = true;
  259. need_value = true;
  260. break;
  261. case 'P':
  262. _light_set_context_command(ctx, light_cmd_get_min_brightness);
  263. need_target = true;
  264. break;
  265. case 'A':
  266. _light_set_context_command(ctx, light_cmd_add_brightness);
  267. need_target = true;
  268. need_value = true;
  269. break;
  270. case 'U':
  271. _light_set_context_command(ctx, light_cmd_sub_brightness);
  272. need_target = true;
  273. need_value = true;
  274. break;
  275. case 'T':
  276. _light_set_context_command(ctx, light_cmd_mul_brightness);
  277. need_target = true;
  278. need_float_value = true;
  279. break;
  280. case 'O':
  281. _light_set_context_command(ctx, light_cmd_save_brightness);
  282. need_target = true;
  283. break;
  284. case 'I':
  285. _light_set_context_command(ctx, light_cmd_restore_brightness);
  286. need_target = true;
  287. break;
  288. }
  289. }
  290. if(ctx->run_params.command == NULL)
  291. {
  292. _light_set_context_command(ctx, light_cmd_get_brightness);
  293. }
  294. if(need_target)
  295. {
  296. light_device_target_t *curr_target = light_find_device_target(ctx, ctrl_name);
  297. if(curr_target == NULL)
  298. {
  299. if(specified_target)
  300. {
  301. fprintf(stderr, "We couldn't find the specified device target at the path \"%s\". Use -L to find one.\n\n", ctrl_name);
  302. return false;
  303. }
  304. else
  305. {
  306. 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");
  307. curr_target = light_find_device_target(ctx, "util/test/dryrun");
  308. }
  309. }
  310. ctx->run_params.device_target = curr_target;
  311. }
  312. if(need_value || need_float_value)
  313. {
  314. if( (argc - optind) != 1)
  315. {
  316. fprintf(stderr, "please specify a <value> for this command.\n\n");
  317. _light_print_usage();
  318. return false;
  319. }
  320. }
  321. if(need_value)
  322. {
  323. if(ctx->run_params.raw_mode)
  324. {
  325. if(sscanf(argv[optind], "%lu", &ctx->run_params.value) != 1)
  326. {
  327. fprintf(stderr, "<value> is not an integer.\n\n");
  328. _light_print_usage();
  329. return false;
  330. }
  331. }
  332. else
  333. {
  334. double percent_value = 0.0;
  335. if(sscanf(argv[optind], "%lf", &percent_value) != 1)
  336. {
  337. fprintf(stderr, "<value> is not a decimal.\n\n");
  338. _light_print_usage();
  339. return false;
  340. }
  341. percent_value = light_percent_clamp(percent_value);
  342. uint64_t raw_value = 0;
  343. if(!_light_percent_to_raw(ctx->run_params.device_target, percent_value, &raw_value))
  344. {
  345. LIGHT_ERR("failed to convert from percent to raw for device target");
  346. return false;
  347. }
  348. ctx->run_params.value = raw_value;
  349. }
  350. }
  351. if(need_float_value)
  352. {
  353. if(sscanf(argv[optind], "%f", &ctx->run_params.float_value) != 1)
  354. {
  355. fprintf(stderr, "<value> is not a float.\n\n");
  356. _light_print_usage();
  357. return false;
  358. }
  359. }
  360. return true;
  361. }
  362. /* API function definitions */
  363. light_context_t* light_initialize(int argc, char **argv)
  364. {
  365. light_context_t *new_ctx = malloc(sizeof(light_context_t));
  366. // Setup default values and runtime params
  367. new_ctx->enumerators = NULL;
  368. new_ctx->num_enumerators = 0;
  369. new_ctx->run_params.command = NULL;
  370. new_ctx->run_params.device_target = NULL;
  371. new_ctx->run_params.value = 0;
  372. new_ctx->run_params.raw_mode = false;
  373. // Setup the configuration folder
  374. // If we are root, use the system-wide configuration folder, otherwise try to find a user-specific folder, or fall back to ~/.config
  375. if(geteuid() == 0)
  376. {
  377. snprintf(new_ctx->sys_params.conf_dir, sizeof(new_ctx->sys_params.conf_dir), "%s", "/etc/light");
  378. }
  379. else
  380. {
  381. char *xdg_conf = getenv("XDG_CONFIG_HOME");
  382. if(xdg_conf != NULL)
  383. {
  384. snprintf(new_ctx->sys_params.conf_dir, sizeof(new_ctx->sys_params.conf_dir), "%s/light", xdg_conf);
  385. }
  386. else
  387. {
  388. snprintf(new_ctx->sys_params.conf_dir, sizeof(new_ctx->sys_params.conf_dir), "%s/.config/light", getenv("HOME"));
  389. }
  390. }
  391. // Make sure the configuration folder exists, otherwise attempt to create it
  392. int32_t rc = light_mkpath(new_ctx->sys_params.conf_dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  393. if(rc && errno != EEXIST)
  394. {
  395. LIGHT_WARN("couldn't create configuration directory");
  396. return false;
  397. }
  398. // Create the built-in enumerators
  399. light_create_enumerator(new_ctx, "sysfs", &impl_sysfs_init, &impl_sysfs_free);
  400. light_create_enumerator(new_ctx, "util", &impl_util_init, &impl_util_free);
  401. light_create_enumerator(new_ctx, "razer", &impl_razer_init, &impl_razer_free);
  402. // This is where we would create enumerators from plugins as well
  403. // 1. Run the plugins get_name() function to get its name
  404. // 2. Point to the plugins init() and free() functions when creating the enumerator
  405. // initialize all enumerators, this will create all the devices and their targets
  406. if(!light_init_enumerators(new_ctx))
  407. {
  408. LIGHT_WARN("failed to initialize all enumerators");
  409. }
  410. // Parse arguments
  411. if(!_light_parse_arguments(new_ctx, argc, argv))
  412. {
  413. LIGHT_ERR("failed to parse arguments");
  414. return NULL;
  415. }
  416. return new_ctx;
  417. }
  418. bool light_execute(light_context_t *ctx)
  419. {
  420. if(ctx->run_params.command == NULL)
  421. {
  422. LIGHT_ERR("run parameters command was null, can't execute");
  423. return false;
  424. }
  425. return ctx->run_params.command(ctx);
  426. }
  427. void light_free(light_context_t *ctx)
  428. {
  429. if(!light_free_enumerators(ctx))
  430. {
  431. LIGHT_WARN("failed to free all enumerators");
  432. }
  433. free(ctx);
  434. }
  435. light_device_enumerator_t * light_create_enumerator(light_context_t *ctx, char const * name, LFUNCENUMINIT init_func, LFUNCENUMFREE free_func)
  436. {
  437. // Create a new enumerator array
  438. uint64_t new_num_enumerators = ctx->num_enumerators + 1;
  439. light_device_enumerator_t **new_enumerators = malloc(new_num_enumerators * sizeof(light_device_enumerator_t*));
  440. // Copy old enumerator array to new one
  441. for(uint64_t i = 0; i < ctx->num_enumerators; i++)
  442. {
  443. new_enumerators[i] = ctx->enumerators[i];
  444. }
  445. // Allocate the new enumerator
  446. new_enumerators[ctx->num_enumerators] = malloc(sizeof(light_device_enumerator_t));
  447. light_device_enumerator_t *returner = new_enumerators[ctx->num_enumerators];
  448. returner->devices = NULL;
  449. returner->num_devices = 0;
  450. returner->init = init_func;
  451. returner->free = free_func;
  452. snprintf(returner->name, sizeof(returner->name), "%s", name);
  453. // Free the old enumerator array, if needed
  454. if(ctx->enumerators != NULL)
  455. {
  456. free(ctx->enumerators);
  457. }
  458. // Replace the enumerator array with the new one
  459. ctx->enumerators = new_enumerators;
  460. ctx->num_enumerators = new_num_enumerators;
  461. // Return newly created device
  462. return returner;
  463. }
  464. bool light_init_enumerators(light_context_t *ctx)
  465. {
  466. bool success = true;
  467. for(uint64_t i = 0; i < ctx->num_enumerators; i++)
  468. {
  469. light_device_enumerator_t * curr_enumerator = ctx->enumerators[i];
  470. if(!curr_enumerator->init(curr_enumerator))
  471. {
  472. success = false;
  473. }
  474. }
  475. return success;
  476. }
  477. bool light_free_enumerators(light_context_t *ctx)
  478. {
  479. bool success = true;
  480. for(uint64_t i = 0; i < ctx->num_enumerators; i++)
  481. {
  482. light_device_enumerator_t * curr_enumerator = ctx->enumerators[i];
  483. if(!curr_enumerator->free(curr_enumerator))
  484. {
  485. success = false;
  486. }
  487. if(curr_enumerator->devices != NULL)
  488. {
  489. for(uint64_t d = 0; d < curr_enumerator->num_devices; d++)
  490. {
  491. light_delete_device(curr_enumerator->devices[d]);
  492. }
  493. free(curr_enumerator->devices);
  494. curr_enumerator->devices = NULL;
  495. }
  496. free(curr_enumerator);
  497. }
  498. free(ctx->enumerators);
  499. ctx->enumerators = NULL;
  500. ctx->num_enumerators = 0;
  501. return success;
  502. }
  503. bool light_split_target_path(char const *in_path, light_target_path_t *out_path)
  504. {
  505. char const * begin = in_path;
  506. char const * end = strstr(begin, "/");
  507. if(end == NULL)
  508. {
  509. LIGHT_WARN("invalid path passed to split_target_path");
  510. return false;
  511. }
  512. size_t size = end - begin;
  513. strncpy(out_path->enumerator, begin, size);
  514. out_path->enumerator[size] = '\0';
  515. begin = end + 1;
  516. end = strstr(begin, "/");
  517. if(end == NULL)
  518. {
  519. LIGHT_WARN("invalid path passed to split_target_path");
  520. return false;
  521. }
  522. size = end - begin;
  523. strncpy(out_path->device, begin, size);
  524. out_path->device[size] = '\0';
  525. strcpy(out_path->target, end + 1);
  526. return true;
  527. }
  528. light_device_target_t* light_find_device_target(light_context_t *ctx, char const * name)
  529. {
  530. light_target_path_t new_path;
  531. if(!light_split_target_path(name, &new_path))
  532. {
  533. LIGHT_WARN("light_find_device_target needs a path in the format of \"enumerator/device/target\", the following format is not recognized: \"%s\"", name);
  534. return NULL;
  535. }
  536. /*
  537. Uncomment to debug the split function
  538. printf("enumerator: %s %u\ndevice: %s %u\ntarget: %s %u\n",
  539. new_path.enumerator, strlen(new_path.enumerator),
  540. new_path.device, strlen(new_path.device),
  541. new_path.target, strlen(new_path.target));
  542. */
  543. // find a matching enumerator
  544. light_device_enumerator_t *enumerator = _light_find_enumerator(ctx, new_path.enumerator);
  545. if(enumerator == NULL)
  546. {
  547. LIGHT_WARN("no such enumerator, \"%s\"", new_path.enumerator);
  548. return NULL;
  549. }
  550. light_device_t *device = _light_find_device(enumerator, new_path.device);
  551. if(device == NULL)
  552. {
  553. LIGHT_WARN("no such device, \"%s\"", new_path.device);
  554. return NULL;
  555. }
  556. light_device_target_t *target = _light_find_target(device, new_path.target);
  557. if(target == NULL)
  558. {
  559. LIGHT_WARN("no such target, \"%s\"", new_path.target);
  560. return NULL;
  561. }
  562. return target;
  563. }
  564. bool light_cmd_print_help(light_context_t *ctx)
  565. {
  566. _light_print_usage();
  567. return true;
  568. }
  569. bool light_cmd_print_version(light_context_t *ctx)
  570. {
  571. printf("v%s\n", VERSION);
  572. return true;
  573. }
  574. bool light_cmd_list_devices(light_context_t *ctx)
  575. {
  576. printf("Listing device targets:\n");
  577. for(uint64_t enumerator = 0; enumerator < ctx->num_enumerators; enumerator++)
  578. {
  579. light_device_enumerator_t *curr_enumerator = ctx->enumerators[enumerator];
  580. for(uint64_t device = 0; device < curr_enumerator->num_devices; device++)
  581. {
  582. light_device_t *curr_device = curr_enumerator->devices[device];
  583. for(uint64_t target = 0; target < curr_device->num_targets; target++)
  584. {
  585. light_device_target_t *curr_target = curr_device->targets[target];
  586. printf("\t%s/%s/%s\n", curr_enumerator->name, curr_device->name, curr_target->name);
  587. }
  588. }
  589. }
  590. return true;
  591. }
  592. bool light_cmd_set_brightness(light_context_t *ctx)
  593. {
  594. light_device_target_t *target = ctx->run_params.device_target;
  595. if(target == NULL)
  596. {
  597. LIGHT_ERR("didn't have a valid target, programmer mistake");
  598. return false;
  599. }
  600. uint64_t mincap = _light_get_min_cap(ctx);
  601. uint64_t value = ctx->run_params.value;
  602. if(mincap > value)
  603. {
  604. value = mincap;
  605. }
  606. if(!target->set_value(target, value))
  607. {
  608. LIGHT_ERR("failed to write to target");
  609. return false;
  610. }
  611. return true;
  612. }
  613. bool light_cmd_get_brightness(light_context_t *ctx)
  614. {
  615. light_device_target_t *target = ctx->run_params.device_target;
  616. if(target == NULL)
  617. {
  618. LIGHT_ERR("didn't have a valid target, programmer mistake");
  619. return false;
  620. }
  621. uint64_t value = 0;
  622. if(!target->get_value(target, &value))
  623. {
  624. LIGHT_ERR("failed to read from target");
  625. return false;
  626. }
  627. if(ctx->run_params.raw_mode)
  628. {
  629. printf("%" PRIu64 "\n", value);
  630. }
  631. else
  632. {
  633. double percent = 0.0;
  634. if(!_light_raw_to_percent(target, value, &percent))
  635. {
  636. LIGHT_ERR("failed to convert from raw to percent from device target");
  637. return false;
  638. }
  639. printf("%.2f\n", percent);
  640. }
  641. return true;
  642. }
  643. bool light_cmd_get_max_brightness(light_context_t *ctx)
  644. {
  645. light_device_target_t *target = ctx->run_params.device_target;
  646. if(target == NULL)
  647. {
  648. LIGHT_ERR("didn't have a valid target, programmer mistake");
  649. return false;
  650. }
  651. if(!ctx->run_params.raw_mode)
  652. {
  653. printf("100.0\n");
  654. return true;
  655. }
  656. uint64_t max_value = 0;
  657. if(!target->get_max_value(target, &max_value))
  658. {
  659. LIGHT_ERR("failed to read from device target");
  660. return false;
  661. }
  662. printf("%" PRIu64 "\n", max_value);
  663. return true;
  664. }
  665. bool light_cmd_set_min_brightness(light_context_t *ctx)
  666. {
  667. char target_path[NAME_MAX];
  668. _light_get_target_path(ctx, target_path, sizeof(target_path));
  669. // Make sure the target folder exists, otherwise attempt to create it
  670. int32_t rc = light_mkpath(target_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  671. if(rc && errno != EEXIST)
  672. {
  673. LIGHT_ERR("couldn't create target directory for minimum brightness");
  674. return false;
  675. }
  676. char target_filepath[NAME_MAX];
  677. _light_get_target_file(ctx, target_filepath, sizeof(target_filepath), "minimum");
  678. if(!light_file_write_uint64(target_filepath, ctx->run_params.value))
  679. {
  680. LIGHT_ERR("couldn't write value to minimum file");
  681. return false;
  682. }
  683. return true;
  684. }
  685. bool light_cmd_get_min_brightness(light_context_t *ctx)
  686. {
  687. char target_path[NAME_MAX];
  688. _light_get_target_file(ctx, target_path, sizeof(target_path), "minimum");
  689. uint64_t minimum_value = 0;
  690. if(!light_file_read_uint64(target_path, &minimum_value))
  691. {
  692. if(ctx->run_params.raw_mode)
  693. {
  694. printf("0\n");
  695. }
  696. else
  697. {
  698. printf("0.00\n");
  699. }
  700. return true;
  701. }
  702. if(ctx->run_params.raw_mode)
  703. {
  704. printf("%" PRIu64 "\n", minimum_value);
  705. }
  706. else
  707. {
  708. double minimum_d = 0.0;
  709. if(!_light_raw_to_percent(ctx->run_params.device_target, minimum_value, &minimum_d))
  710. {
  711. LIGHT_ERR("failed to convert value from raw to percent for device target");
  712. return false;
  713. }
  714. printf("%.2f\n", minimum_d);
  715. }
  716. return true;
  717. }
  718. bool light_cmd_add_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. uint64_t max_value = 0;
  733. if(!target->get_max_value(target, &max_value))
  734. {
  735. LIGHT_ERR("failed to read from target");
  736. return false;
  737. }
  738. value += ctx->run_params.value;
  739. uint64_t mincap = _light_get_min_cap(ctx);
  740. if(mincap > value)
  741. {
  742. value = mincap;
  743. }
  744. if(value > max_value)
  745. {
  746. value = max_value;
  747. }
  748. if(!target->set_value(target, value))
  749. {
  750. LIGHT_ERR("failed to write to target");
  751. return false;
  752. }
  753. return true;
  754. }
  755. bool light_cmd_sub_brightness(light_context_t *ctx)
  756. {
  757. light_device_target_t *target = ctx->run_params.device_target;
  758. if(target == NULL)
  759. {
  760. LIGHT_ERR("didn't have a valid target, programmer mistake");
  761. return false;
  762. }
  763. uint64_t value = 0;
  764. if(!target->get_value(target, &value))
  765. {
  766. LIGHT_ERR("failed to read from target");
  767. return false;
  768. }
  769. if(value > ctx->run_params.value)
  770. {
  771. value -= ctx->run_params.value;
  772. }
  773. else
  774. {
  775. value = 0;
  776. }
  777. uint64_t mincap = _light_get_min_cap(ctx);
  778. if(mincap > value)
  779. {
  780. value = mincap;
  781. }
  782. if(!target->set_value(target, value))
  783. {
  784. LIGHT_ERR("failed to write to target");
  785. return false;
  786. }
  787. return true;
  788. }
  789. bool light_cmd_mul_brightness(light_context_t *ctx)
  790. {
  791. light_device_target_t *target = ctx->run_params.device_target;
  792. if(target == NULL)
  793. {
  794. LIGHT_ERR("didn't have a valid target, programmer mistake");
  795. return false;
  796. }
  797. uint64_t value = 0;
  798. if(!target->get_value(target, &value))
  799. {
  800. LIGHT_ERR("failed to read from target");
  801. return false;
  802. }
  803. uint64_t max_value = 0;
  804. if(!target->get_max_value(target, &max_value))
  805. {
  806. LIGHT_ERR("failed to read from target");
  807. return false;
  808. }
  809. uint64_t old_value = value;
  810. value *= ctx->run_params.float_value;
  811. // Check that we actually de/increase value
  812. if(value == old_value)
  813. {
  814. if(ctx->run_params.float_value > 1)
  815. value++;
  816. if(ctx->run_params.float_value < 1 && value > 0)
  817. value--;
  818. }
  819. uint64_t mincap = _light_get_min_cap(ctx);
  820. if(mincap > value)
  821. {
  822. value = mincap;
  823. }
  824. if(value > max_value)
  825. {
  826. value = max_value;
  827. }
  828. if(!target->set_value(target, value))
  829. {
  830. LIGHT_ERR("failed to write to target");
  831. return false;
  832. }
  833. return true;
  834. }
  835. bool light_cmd_save_brightness(light_context_t *ctx)
  836. {
  837. char target_path[NAME_MAX];
  838. _light_get_target_path(ctx, target_path, sizeof(target_path));
  839. // Make sure the target folder exists, otherwise attempt to create it
  840. int32_t rc = light_mkpath(target_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  841. if(rc && errno != EEXIST)
  842. {
  843. LIGHT_ERR("couldn't create target directory for save brightness");
  844. return false;
  845. }
  846. char target_filepath[NAME_MAX];
  847. _light_get_target_file(ctx, target_filepath, sizeof(target_filepath), "save");
  848. uint64_t curr_value = 0;
  849. if(!ctx->run_params.device_target->get_value(ctx->run_params.device_target, &curr_value))
  850. {
  851. LIGHT_ERR("couldn't read from device target");
  852. return false;
  853. }
  854. if(!light_file_write_uint64(target_filepath, curr_value))
  855. {
  856. LIGHT_ERR("couldn't write value to savefile");
  857. return false;
  858. }
  859. return true;
  860. }
  861. bool light_cmd_restore_brightness(light_context_t *ctx)
  862. {
  863. char target_path[NAME_MAX];
  864. _light_get_target_file(ctx, target_path, sizeof(target_path), "save");
  865. uint64_t saved_value = 0;
  866. if(!light_file_read_uint64(target_path, &saved_value))
  867. {
  868. LIGHT_ERR("couldn't read value from savefile");
  869. return false;
  870. }
  871. uint64_t mincap = _light_get_min_cap(ctx);
  872. if(mincap > saved_value)
  873. {
  874. saved_value = mincap;
  875. }
  876. if(!ctx->run_params.device_target->set_value(ctx->run_params.device_target, saved_value))
  877. {
  878. LIGHT_ERR("couldn't write saved value to device target");
  879. return false;
  880. }
  881. return true;
  882. }
  883. light_device_t *light_create_device(light_device_enumerator_t *enumerator, char const *name, void *device_data)
  884. {
  885. light_device_t *new_device = malloc(sizeof(light_device_t));
  886. new_device->enumerator = enumerator;
  887. new_device->targets = NULL;
  888. new_device->num_targets = 0;
  889. new_device->device_data = device_data;
  890. snprintf(new_device->name, sizeof(new_device->name), "%s", name);
  891. _light_add_enumerator_device(enumerator, new_device);
  892. return new_device;
  893. }
  894. void light_delete_device(light_device_t *device)
  895. {
  896. for(uint64_t i = 0; i < device->num_targets; i++)
  897. {
  898. light_delete_device_target(device->targets[i]);
  899. }
  900. if(device->targets != NULL)
  901. {
  902. free(device->targets);
  903. }
  904. if(device->device_data != NULL)
  905. {
  906. free(device->device_data);
  907. }
  908. free(device);
  909. }
  910. 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)
  911. {
  912. light_device_target_t *new_target = malloc(sizeof(light_device_target_t));
  913. new_target->device = device;
  914. new_target->set_value = setfunc;
  915. new_target->get_value = getfunc;
  916. new_target->get_max_value = getmaxfunc;
  917. new_target->custom_command = cmdfunc;
  918. new_target->device_target_data = target_data;
  919. snprintf(new_target->name, sizeof(new_target->name), "%s", name);
  920. _light_add_device_target(device, new_target);
  921. return new_target;
  922. }
  923. void light_delete_device_target(light_device_target_t *device_target)
  924. {
  925. if(device_target->device_target_data != NULL)
  926. {
  927. free(device_target->device_target_data);
  928. }
  929. free(device_target);
  930. }