autodim.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /*
  2. * Copyright (C) 2010 Michael Buesch <m@bues.ch>
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version 2
  7. * of the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. */
  14. #include "autodim.h"
  15. #include "log.h"
  16. #include "fileaccess.h"
  17. #include "util.h"
  18. #include "main.h"
  19. #include <unistd.h>
  20. #include <fcntl.h>
  21. #include <string.h>
  22. #include <stdio.h>
  23. #include <signal.h>
  24. #include <errno.h>
  25. #include <assert.h>
  26. #include <math.h>
  27. static void autodim_set_backlight(struct autodim *ad, unsigned int percent)
  28. {
  29. struct battery *battery = backend.battery;
  30. struct backlight *backlight = backend.backlight;
  31. if (battery && battery->on_ac(battery)) {
  32. if (!backlight->autodim_enabled_on_ac)
  33. percent = ad->max_percent;
  34. }
  35. percent = min(percent, ad->max_percent);
  36. if (percent != ad->bl_percent) {
  37. ad->bl_percent = percent;
  38. backlight_set_percentage(ad->bl, percent);
  39. logverbose("Autodim: Set backlight to %u percent.\n", percent);
  40. }
  41. }
  42. static void autodim_handle_state(struct autodim *ad)
  43. {
  44. struct autodim_step *step;
  45. if (ad->state < ad->nr_steps) {
  46. step = &ad->steps[ad->state];
  47. ad->state++;
  48. autodim_set_backlight(ad, step->percent);
  49. }
  50. }
  51. static void autodim_timer_stop(struct autodim *ad)
  52. {
  53. sleeptimer_dequeue(&ad->timer);
  54. }
  55. static void autodim_timer_start(struct autodim *ad)
  56. {
  57. unsigned int sec;
  58. if (ad->state == 0)
  59. sec = ad->steps[0].second;
  60. else if (ad->state < ad->nr_steps)
  61. sec = ad->steps[ad->state].second - ad->steps[ad->state - 1].second;
  62. else
  63. return;
  64. logverbose("autodim: Next event in %u sec\n", sec);
  65. sleeptimer_set_timeout_relative(&ad->timer, sec * 1000);
  66. sleeptimer_enqueue(&ad->timer);
  67. }
  68. static void autodim_timer_callback(struct sleeptimer *timer)
  69. {
  70. struct autodim *ad = container_of(timer, struct autodim, timer);
  71. logverbose("autodim: timer triggered\n");
  72. autodim_handle_state(ad);
  73. autodim_timer_start(ad);
  74. }
  75. struct autodim * autodim_alloc(void)
  76. {
  77. struct autodim *ad;
  78. ad = zalloc(sizeof(*ad));
  79. if (!ad)
  80. return NULL;
  81. return ad;
  82. }
  83. static int autodim_force_realloc_steps(struct autodim *ad, unsigned int newcount)
  84. {
  85. void *buf;
  86. buf = realloc(ad->steps, newcount * sizeof(*ad->steps));
  87. if (!buf) {
  88. logerr("Out of memory\n");
  89. return -ENOMEM;
  90. }
  91. ad->steps = buf;
  92. ad->nr_allocated_steps = newcount;
  93. return 0;
  94. }
  95. static int autodim_realloc_steps(struct autodim *ad, unsigned int newcount)
  96. {
  97. if (newcount > ad->nr_allocated_steps)
  98. return autodim_force_realloc_steps(ad, newcount);
  99. return 0;
  100. }
  101. static int autodim_smoothen_steps(struct autodim *ad)
  102. {
  103. struct autodim_step *old_steps, *cur, *next;
  104. unsigned int old_i, new_i, nr_old_steps, sec;
  105. float secdiff, perdiff, ppsec, perc;
  106. int ret = 0;
  107. old_steps = calloc(ad->nr_steps, sizeof(*old_steps));
  108. if (!old_steps)
  109. return -ENOMEM;
  110. memcpy(old_steps, ad->steps, ad->nr_steps * sizeof(*old_steps));
  111. nr_old_steps = ad->nr_steps;
  112. ad->nr_steps = 0;
  113. for (old_i = 0, new_i = 0; ; old_i++) {
  114. ret = autodim_realloc_steps(ad, new_i + 1);
  115. if (ret)
  116. goto out;
  117. cur = &old_steps[old_i];
  118. ad->steps[new_i++] = *cur;
  119. if (old_i + 1 >= nr_old_steps)
  120. break;
  121. if (cur->percent == 0)
  122. break;
  123. next = &old_steps[old_i + 1];
  124. if (next->percent == cur->percent - 1)
  125. continue;
  126. /* Make intermediate steps */
  127. secdiff = (float)next->second - (float)cur->second;
  128. perdiff = (float)cur->percent - (float)next->percent;
  129. if (secdiff <= 0 || perdiff <= 0)
  130. continue;
  131. ppsec = perdiff / secdiff;
  132. sec = cur->second + 1;
  133. perc = (float)cur->percent - ppsec;
  134. while (sec < next->second) {
  135. ret = autodim_realloc_steps(ad, new_i + 1);
  136. if (ret)
  137. goto out;
  138. ad->steps[new_i].second = sec;
  139. ad->steps[new_i].percent = max((int)roundf(perc), 0);
  140. new_i++;
  141. perc -= ppsec;
  142. sec += 1;
  143. }
  144. }
  145. ad->nr_steps = new_i;
  146. out:
  147. free(old_steps);
  148. return ret;
  149. }
  150. static int autodim_optimize_steps(struct autodim *ad)
  151. {
  152. struct autodim_step *cur, *next;
  153. unsigned int i;
  154. int err;
  155. /* Remove duplicates */
  156. for (i = 0; ad->nr_steps && i < ad->nr_steps - 1; i++) {
  157. cur = &ad->steps[i];
  158. next = &ad->steps[i + 1];
  159. if (cur->percent == next->percent ||
  160. cur->second >= next->second) {
  161. /* Remove next. */
  162. memmove(&ad->steps[i + 1], &ad->steps[i + 2],
  163. (ad->nr_steps - i - 2) * sizeof(*cur));
  164. ad->nr_steps--;
  165. }
  166. }
  167. err = autodim_force_realloc_steps(ad, ad->nr_steps);
  168. if (err)
  169. return err;
  170. return 0;
  171. }
  172. static void autodim_dump_steps(struct autodim *ad, const char *info)
  173. {
  174. unsigned int i;
  175. loginfo("Autodim steps (%s): ", info);
  176. for (i = 0; i < ad->nr_steps; i++) {
  177. loginfo("%u/%u%s", ad->steps[i].second, ad->steps[i].percent,
  178. (i + 1 == ad->nr_steps) ? "" : " ");
  179. }
  180. loginfo("\n");
  181. }
  182. static int autodim_steps_get(struct autodim *ad, struct config_file *config)
  183. {
  184. char *string, *s, *next;
  185. unsigned int stepnr = 0;
  186. int ret = 0;
  187. string = strdup(config_get(config, "BACKLIGHT", "autodim_steps",
  188. "15/75 25/50 50/25 75/10"));
  189. if (!string)
  190. return -ENOMEM;
  191. s = string_strip(string);
  192. while (1) {
  193. next = strchr(s, ' ');
  194. if (next) {
  195. next[0] = '\0';
  196. next++;
  197. }
  198. if (strempty(s))
  199. break;
  200. ret = autodim_realloc_steps(ad, stepnr + 1);
  201. if (ret)
  202. goto out;
  203. if (sscanf(s, "%u/%u", &ad->steps[stepnr].second,
  204. &ad->steps[stepnr].percent) != 2) {
  205. logerr("Failed to parse autodim_steps config value\n");
  206. ret = -EINVAL;
  207. goto out;
  208. }
  209. if (stepnr > 0) {
  210. if (ad->steps[stepnr - 1].second >= ad->steps[stepnr].second) {
  211. logerr("Error: autodim_steps, previous step idle-seconds "
  212. "value bigger than adjacent value\n");
  213. ret = -EINVAL;
  214. goto out;
  215. }
  216. }
  217. stepnr++;
  218. if (!next)
  219. break;
  220. s = string_strip(next);
  221. }
  222. ad->nr_steps = stepnr;
  223. if (loglevel_is_debug())
  224. autodim_dump_steps(ad, "parsed-config");
  225. if (config_get_bool(config, "BACKLIGHT", "autodim_smooth", 0)) {
  226. ret = autodim_smoothen_steps(ad);
  227. if (ret)
  228. goto out;
  229. if (loglevel_is_debug())
  230. autodim_dump_steps(ad, "smoothened");
  231. }
  232. ret = autodim_optimize_steps(ad);
  233. if (ret)
  234. goto out;
  235. if (loglevel_is_debug())
  236. autodim_dump_steps(ad, "optimized");
  237. out:
  238. free(string);
  239. return ret;
  240. }
  241. int autodim_init(struct autodim *ad, struct backlight *bl,
  242. struct config_file *config)
  243. {
  244. LIST_HEAD(dir_entries);
  245. struct dir_entry *dir_entry;
  246. int err, i, fd, count, percent;
  247. char path[PATH_MAX + 1];
  248. ad->bl = bl;
  249. ad->bl->autodim_enabled++;
  250. count = list_directory(&dir_entries, "/dev/input");
  251. if (count <= 0) {
  252. logerr("Failed to list /dev/input\n");
  253. err = -ENOENT;
  254. goto error;
  255. }
  256. ad->fds = calloc(count, sizeof(*ad->fds));
  257. if (!ad->fds) {
  258. err = -ENOMEM;
  259. goto err_free_dir_entries;
  260. }
  261. i = 0;
  262. list_for_each_entry(dir_entry, &dir_entries, list) {
  263. if (dir_entry->type != DT_CHR)
  264. continue;
  265. snprintf(path, sizeof(path), "/dev/input/%s", dir_entry->name);
  266. fd = open(path, O_RDONLY);
  267. if (fd < 0) {
  268. if (errno == ENODEV)
  269. continue;
  270. logerr("Failed to open %s: %s\n",
  271. path, strerror(errno));
  272. continue; /* Continue anyway */
  273. }
  274. err = fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK);
  275. err |= fcntl(fd, F_SETSIG, SIGUSR1);
  276. err |= fcntl(fd, F_SETOWN, getpid());
  277. if (err) {
  278. logerr("Failed to set O_ASYNC\n");
  279. close(fd);
  280. err = -ENOENT;
  281. goto err_free_fds;
  282. }
  283. ad->fds[i++] = fd;
  284. logverbose("Autodim: Watching input device %s\n", path);
  285. }
  286. ad->nr_fds = i;
  287. dir_entries_free(&dir_entries);
  288. percent = backlight_get_percentage(ad->bl);
  289. if (percent < 0)
  290. percent = 100;
  291. ad->bl_percent = percent;
  292. ad->max_percent = percent;
  293. err = autodim_steps_get(ad, config);
  294. if (err)
  295. goto err_free_fds;
  296. sleeptimer_init(&ad->timer, "autodim", autodim_timer_callback);
  297. autodim_timer_start(ad);
  298. logdebug("Auto-dimming enabled\n");
  299. return 0;
  300. err_free_fds:
  301. free(ad->fds);
  302. ad->fds = NULL;
  303. ad->nr_fds = 0;
  304. err_free_dir_entries:
  305. dir_entries_free(&dir_entries);
  306. error:
  307. ad->bl->autodim_enabled--;
  308. return err;
  309. }
  310. void autodim_destroy(struct autodim *ad)
  311. {
  312. unsigned int i;
  313. if (!ad)
  314. return;
  315. ad->bl->autodim_enabled--;
  316. autodim_timer_stop(ad);
  317. for (i = 0; i < ad->nr_fds; i++)
  318. close(ad->fds[i]);
  319. free(ad->fds);
  320. ad->fds = NULL;
  321. logdebug("Auto-dimming disabled\n");
  322. }
  323. void autodim_free(struct autodim *ad)
  324. {
  325. if (ad) {
  326. free(ad->steps);
  327. memset(ad, 0, sizeof(*ad));
  328. free(ad);
  329. }
  330. }
  331. void autodim_suspend(struct autodim *ad)
  332. {
  333. if (!ad)
  334. return;
  335. if (!ad->suspended) {
  336. autodim_timer_stop(ad);
  337. //TODO disable input events.
  338. logdebug("Auto-dimming suspended\n");
  339. }
  340. ad->suspended++;
  341. }
  342. void autodim_resume(struct autodim *ad)
  343. {
  344. if (!ad)
  345. return;
  346. ad->suspended--;
  347. if (!ad->suspended) {
  348. autodim_handle_input_event(ad);
  349. autodim_timer_start(ad);
  350. logdebug("Auto-dimming resumed\n");
  351. }
  352. }
  353. void autodim_set_max_percent(struct autodim *ad, int max_percent)
  354. {
  355. if (max_percent < 0)
  356. max_percent = 100;
  357. max_percent = clamp(max_percent, 0, 100);
  358. if (ad->max_percent != (unsigned int)max_percent) {
  359. ad->max_percent = (unsigned int)max_percent;
  360. autodim_handle_input_event(ad);
  361. }
  362. }
  363. void autodim_handle_input_event(struct autodim *ad)
  364. {
  365. logverbose("Autodim: Got input event.\n");
  366. ad->state = 0;
  367. autodim_timer_start(ad);
  368. autodim_set_backlight(ad, ad->max_percent);
  369. }
  370. void autodim_handle_battery_event(struct autodim *ad)
  371. {
  372. logverbose("Autodim: Got battery event.\n");
  373. /* Handle possible change of "on-AC" state. */
  374. autodim_set_backlight(ad, ad->bl_percent);
  375. }