light.c 33KB

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