Use jenkins_one_at_a_time_hash to seed blinking
[darcs-mirror-pidgin-blinklight.git] / src / blink.c
1 /* © 2009 Joachim Breitner
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU General Public License
5  * as published by the Free Software Foundation; either version 2
6  * of the License, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16  */
17 #ifdef HAVE_CONFIG_H
18 # include "../config.h"
19 #endif
20
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <blink.h>
27
28 #define OFF    0
29 #define ON     1
30 #define TOGGLE 2
31
32
33 struct interface {
34         char *sysfs;
35         char *commands[2];
36         char *scanline;
37 };
38
39 #define INTERFACES 3
40 static struct interface interfaces[] = {
41         {"/proc/acpi/ibm/light",                        {"off","on"},   "status: %9s" },
42         {"/proc/acpi/asus/mled",                        {"0","1"},          "%9s" },
43         {"/sys/class/leds/asus:phone/brightness",   {"0","1"},      "%9s" }
44 };
45
46 static struct interface *interface = NULL;
47
48 typedef struct {
49         int     state;
50         int     time;
51 } blinky;
52
53 typedef struct {
54         blinky *seq;
55         int pos;
56 } blinkstate;
57
58 /* TODO: We should really allow an option to continue blinking indefinitely
59          until user opens message */
60 blinky default_seq[] = {
61         {TOGGLE,        150},
62         {TOGGLE,        125},
63         {TOGGLE,        150},
64         {TOGGLE,        0}
65 };
66
67 static guint
68 blinklight_blink(blinkstate *bstate) {
69         FILE *file;
70         char *new_state = NULL;
71         char old_state[10] = "";
72         int ret;
73
74         blinky *seq = &bstate->seq[bstate->pos];
75
76         if (interface == NULL) return FALSE;
77
78         // purple_debug_info("pidgin-blinklight","blink called with parameter: %i\n", seq->state);
79         
80         if (seq->state == ON)  new_state=interface->commands[ON];
81         if (seq->state == OFF) new_state=interface->commands[OFF];
82         if (seq->state == TOGGLE) {
83                 // purple_debug_info("pidgin-blinklight","trying to toggle\n");
84                 file = fopen(interface->sysfs,"r");
85                       if (file == NULL) { perror ("Trying to open sysfs for reading"); return FALSE;};
86                 ret = fscanf(file,interface->scanline,old_state);
87                       if (ret == EOF)  { perror ("Trying to read from sysfs"); return FALSE;};
88                 ret = fclose(file);
89                       if (ret != 0) { perror ("Trying to close sysfs"); return FALSE;};
90                 // purple_debug_info("pidgin-blinklight","done reading old state %s\n", old_state);
91
92                 if (strcmp(old_state,interface->commands[ON])  == 0) new_state=interface->commands[OFF];
93                 if (strcmp(old_state,interface->commands[OFF]) == 0) new_state=interface->commands[ON];
94         }
95         
96         if (new_state == NULL) {
97                 // purple_debug_info("pidgin-blinklight","No new state defined\n");
98                 return FALSE;
99         }
100         // purple_debug_info("pidgin-blinklight","setting new state: %s\n", new_state);
101
102         file = fopen(interface->sysfs,"w");
103                 if (file == NULL) { perror ("Trying to open sysfs for writing"); return FALSE;};
104         ret = fprintf(file,"%s",new_state);
105                 if (ret < 0)   { perror ("Trying to write to sysfs"); return FALSE;};
106         ret = fclose(file);
107                 if (ret != 0) { perror ("Trying to close sysfs"); return FALSE;};
108
109         // purple_debug_info("pidgin-blinklight","done setting new state: %s\n", new_state);
110         
111         if (seq->time) { 
112                 bstate->pos++;
113                 blinklight_timeout_add(seq->time,(GSourceFunc)blinklight_blink,bstate);
114         } else {
115                 free(bstate->seq);
116                 free(bstate);
117         }
118         return FALSE;
119 }
120
121 // From http://en.wikipedia.org/wiki/Jenkins_hash_function
122 static guint32
123 jenkins_one_at_a_time_hash(char *key)
124 {
125     guint32 hash = 0;
126  
127     while (*key){
128         hash += *(key++);
129         hash += (hash << 10);
130         hash ^= (hash >> 6);
131     }
132     hash += (hash << 3);
133     hash ^= (hash >> 11);
134     hash += (hash << 15);
135     return hash;
136 }
137
138
139 void
140 blinklight_startblink(char *seed) {
141         int length = 4;
142         blinkstate *bstate = malloc(sizeof(blinkstate));
143         blinky *seq ;
144
145         if (seed == NULL) {
146                 seq = malloc(sizeof(default_seq));
147                 memcpy(seq, default_seq, sizeof(default_seq));
148
149         } else {
150                 seq = calloc(sizeof(blinky),length);
151                 
152                 // Initialize to four toggle commands
153                 for (int i=0; i<length; i++) {
154                         seq[i].state = TOGGLE;
155                 }
156
157                 guint32 hash = jenkins_one_at_a_time_hash(seed);
158                 // Set timeing based on string
159                 // Range: 500 + [0,1]*200 ms
160                 for (int i=0; i<length-1; i++) {
161                         // Uses last 8 bits
162                         seq[i].time = 50 + hash % 200;
163                         // So remove them
164                         hash = (hash << 8); 
165                         printf("Time %d: %d\n", i, seq[i].time);
166                 }
167         }
168
169         bstate->seq = seq;
170         bstate->pos = 0;
171         blinklight_blink(bstate);
172 }
173
174 char *
175 blinklight_init() {
176         // figure out which interface to use
177         for (int i=0; i< INTERFACES; i++) {
178                 if (! access(interfaces[i].sysfs, R_OK)) {
179                         // File exists and is readable (not checking writable because of possible race condition)
180                         interface = &interfaces[i];
181                         return interfaces[i].sysfs;
182                 }
183                 
184         }
185         return NULL;
186 }