2 * devinput - zavai /dev/input device handling
4 * Copyright (C) 2009 Enrico Zini <enrico@enricozini.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 public abstract class DevInput : zavai.Service
26 public string device { get; construct; }
28 public signal bool event(LinuxInput.Event* ev);
30 protected IOChannel fd = null;
31 protected uint fd_watch = 0;
33 protected void close_fd()
39 } catch (IOChannelError e) {
40 zavai.log.error("When closing " + device + ": " + e.message);
47 protected bool on_input_data(IOChannel source, IOCondition condition)
49 if (condition != IOCondition.IN) return true;
51 stderr.printf("GOT INPUT ON %s %d\n", device, source.unix_get_fd());
52 char[] buf = new char[sizeof(LinuxInput.Event)];
54 source.read_chars(buf, out count_read);
55 stderr.printf("READ %zu chars\n", count_read);
57 LinuxInput.Event* ie = (LinuxInput.Event*)buf;
58 stderr.printf("INPUT EVENT time %lu.%lu type %hu code %hu val %d\n", (ulong)ie->time.tv_sec, ie->time.tv_usec, ie->type, ie->code, ie->val);
61 ts1, ts2, type, code, value = struct.unpack("LLHHI", buf)
62 #print ts1, ts2, type, code, value
63 if type == 1 and code == 119:
65 #print "BUTTON RELEASE"
68 if self.last_button_press + 1 < ts1:
69 self.last_button_press = ts1
70 if self.button_press_handler is not None:
71 self.button_press_handler()
73 elif type == 5 and code == 2:
75 info("Headset plugged in")
76 self.mixer_for_headset(self)
78 info("Headset plugged out")
79 self.mixer_for_handset(self)
84 /// Start reading from the device
85 public override void start()
92 // Open the device and listed to it using the GObject main loop
93 fd = new IOChannel.file(device, "r");
94 fd.set_encoding(null);
95 fd.set_buffered(false);
96 fd_watch = fd.add_watch(IOCondition.IN, on_input_data);
101 // Stop reading from the device
102 public override void stop()
104 if (!started) return;
108 Source.remove(fd_watch);
116 public class PowerButton : DevInput
118 public signal void power_button(Posix.timeval* time, bool pressed);
122 name = "input.power_button";
123 // FIXME: change to event0 for the power button
124 // FIXME: change to event4 for the aux button and headset button
125 //device = "/dev/input/event1";
126 device = "/dev/input/event0";
129 power_button += on_power_button;
132 protected bool on_event(LinuxInput.Event* ev)
134 if (ev->type == LinuxInput.Type.KEY &&
135 ev->code == LinuxInput.Key.POWER)
137 power_button(&(ev->time), ev->val == 0 ? false : true);
142 protected void on_power_button(Posix.timeval* time, bool pressed)
146 zavai.app.push_applet("menu.power");
153 # - hook into the headset plugged/unplugged event
154 # - if unplugged, turn on handset microphone
155 # - if plugged, redo headest mixer settings
157 "Handle mixer settings, audio recording and headset button presses"
158 def __init__(self, button_press_handler = None):
159 self.saved_scenario = os.path.expanduser("~/.audiomap.state")
162 # Set mixer to record from headset and handle headset button
163 self.save_scenario(self.saved_scenario)
164 self.load_scenario("/usr/share/openmoko/scenarios/voip-handset.state")
166 # This is a work-around because I have not found a way to query for the
167 # current headset state, I can only know when it changes. So in my
168 # system I configured oeventsd with a rule to touch this file when the
169 # headset is plugged in, and remove the file when it's plugged out.
170 if os.path.exists("/tmp/has_headset"):
171 self.mixer_for_headset(True)
173 self.mixer_for_handset(True)
175 #self.mixer_set("DAPM Handset Mic", "mute")
176 #self.mixer_set("DAPM Headset Mic", "unmute")
177 #self.mixer_set("Left Mixer Sidetone Playback Sw", "unmute")
178 #self.mixer_set("ALC Mixer Mic1", "cap")
179 #self.mixer_set("Amp Spk", "mute") # We don't need the phone playing what we say
181 # Watch the headset button
182 self.button_press_handler = button_press_handler
183 self.input_fd = open("/dev/input/event4", "rb")
184 self.input_watch = gobject.io_add_watch(self.input_fd.fileno(), gobject.IO_IN, self.on_input_data)
186 self.last_button_press = 0
190 def mixer_for_headset(self, force=False):
191 if not force and self.has_headset: return
192 info("Setting mixer for headset")
193 # TODO: find out how to disable the handset microphone: this does not
194 # seem to be sufficient
196 ("DAPM Handset Mic", "mute"),
197 ("DAPM Headset Mic", "unmute"),
198 ("Left Mixer Sidetone Playback Sw", "unmute"),
199 ("ALC Mixer Mic1", "cap"),
200 ("Amp Spk", "mute") # We don't need the phone playing what we say
202 self.has_headset = True
204 def mixer_for_handset(self, force=False):
205 if not force and not self.has_headset: return
206 info("Setting mixer for handset")
208 ("DAPM Handset Mic", "unmute"),
209 ("DAPM Headset Mic", "mute"),
210 ("Left Mixer Sidetone Playback Sw", "mute"),
211 ("ALC Mixer Mic1", "cap"),
212 ("Amp Spk", "mute") # We don't need the phone playing what we say
214 self.has_headset = False
216 def set_basename(self, basename):
217 self.basename = basename
219 def start_recording(self):
220 if self.basename is None:
221 raise RuntimeError("Recording requested but basename not set")
222 self.recorder = subprocess.Popen(
223 ["arecord", "-D", "hw", "-f", "cd", "-r", "8000", "-t", "wav", self.basename + ".wav"])
225 def start_levels(self):
226 self.recorder = subprocess.Popen(
227 ["arecord", "-D", "hw", "-f", "cd", "-r", "8000", "-t", "wav", "-V", "stereo", "/dev/null"])
230 if self.recorder is not None:
231 os.kill(self.recorder.pid, signal.SIGINT)
234 # Restore mixer settings
235 self.load_scenario(self.saved_scenario)
237 gobject.source_remove(self.input_watch)
238 self.input_fd.close()
240 def on_input_data(self, source, condition):
241 buf = self.input_fd.read(16)
242 ts1, ts2, type, code, value = struct.unpack("LLHHI", buf)
243 #print ts1, ts2, type, code, value
244 if type == 1 and code == 119:
246 #print "BUTTON RELEASE"
249 if self.last_button_press + 1 < ts1:
250 self.last_button_press = ts1
251 if self.button_press_handler is not None:
252 self.button_press_handler()
253 #print "BUTTON PRESS"
254 elif type == 5 and code == 2:
256 info("Headset plugged in")
257 self.mixer_for_headset(self)
259 info("Headset plugged out")
260 self.mixer_for_handset(self)
263 def save_scenario(self, name):
264 res = subprocess.call(["alsactl", "store", "-f", name])
266 raise RuntimeError("Saving audio scenario to '%s' failed" % name)
268 def load_scenario(self, name):
269 res = subprocess.call(["alsactl", "restore", "-f", name])
271 raise RuntimeError("Loading audio scenario '%s' failed" % name)
273 def mixer_set(self, name, *args):
274 args = map(str, args)
275 res = subprocess.call(["amixer", "-q", "set", name] + args)
277 raise RuntimeError("Setting mixer '%s' to %s failed" % (name, " ".join(args)))
279 # Will do this when we find out the syntax for giving amixer commands on stdin
280 def mixer_set_many(self, *args):
281 """Perform many mixer set operations via amixer --stdin"""
282 proc = subprocess.Popen(["amixer", "-q", "--stdin"], stdin=subprocess.PIPE)
285 cmd_input.append("sset " + repr(k) + " " + repr(v))
286 (out, err) = proc.communicate(input="\n".join(cmd_input))
289 raise RuntimeError("Setting mixer failed")
291 public class CommandButton : Gtk.Button
293 private string command;
295 public CommandButton(string name, string command)
298 this.command = command;
299 clicked += on_clicked;
300 set_size_request(0, zavai.config.min_button_height);
303 public void on_clicked()
305 zavai.log.info("Run program: " + command);
306 string[] args = command.split(" ");
309 Environment.get_home_dir(),
312 SpawnFlags.SEARCH_PATH,
319 public PowerButton power_button = null;
323 power_button = new PowerButton();
325 zavai.registry.register_service(power_button);
328 public void ui_init()
331 var menu_power = new zavai.Menu("Power menu");
332 menu_power.add_widget(new CommandButton("Suspend", "apm -s"));
333 menu_power.add_widget(new CommandButton("Shutdown", "shutdown -h now"));
334 menu_power.add_widget(new CommandButton("Reboot", "shutdown -r now"));
335 zavai.registry.register_menu("menu.power", menu_power);
337 zavai.registry.getmenu("menu.main").add_applet("menu.power");