light.c 32KB

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