light.c 33KB

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