From 2410d1cba02872da361ad86086176867a19df0b0 Mon Sep 17 00:00:00 2001 From: gregor herrmann Date: Fri, 26 Mar 2010 14:41:14 +0100 Subject: [PATCH] Revert "Split non-ui functions in separate lib" This reverts commit b58c46170a650521947aeaff5283a7a758ec75a1. (Revert for now, doesn't build.) --- CMakeLists.txt | 1 - README | 16 +- src/CMakeLists.txt | 15 +- src/app.vala | 31 +++ src/app_power.vala | 19 +- {zavai => src}/at.vala | 0 {zavai => src}/audio.vala | 0 {zavai => src}/bluetooth.vala | 0 {zavai => src}/clock.vala | 0 {zavai => src}/config.vala | 31 --- {zavai => src}/core.vala | 10 +- {zavai => src}/dbus-extra.vapi | 0 {zavai => src}/devkit-power-gobject.vapi | 0 {zavai => src}/gps.vala | 0 {zavai => src}/gsm.vala | 0 {zavai => src}/input.vala | 0 {zavai => src}/libgps.vapi | 0 {zavai => src}/libomhacks.vapi | 0 {zavai => src}/linux-input.vapi | 6 - {zavai => src}/log.vala | 0 {zavai => src}/lua5.1.vapi | 0 {zavai => src}/power.vala | 0 {zavai => src}/registry.vala | 0 {zavai => src}/uevent.vala | 0 {zavai => src}/wifi.vala | 0 {zavai => src}/x11.vapi | 0 src/zavai.vala | 2 +- zavai/__init__.py | 40 +++ zavai/app.py | 91 +++++++ zavai/audio.py | 295 +++++++++++++++++++++++ zavai/conf.py | 33 +++ zavai/config.py | 23 ++ {src => zavai}/gps.py | 0 zavai/menu.py | 68 ++++++ zavai/plugins.py | 39 +++ zavai/registry.py | 210 ++++++++++++++++ 36 files changed, 862 insertions(+), 68 deletions(-) rename {zavai => src}/at.vala (100%) rename {zavai => src}/audio.vala (100%) rename {zavai => src}/bluetooth.vala (100%) rename {zavai => src}/clock.vala (100%) rename {zavai => src}/config.vala (86%) rename {zavai => src}/core.vala (95%) rename {zavai => src}/dbus-extra.vapi (100%) rename {zavai => src}/devkit-power-gobject.vapi (100%) rename {zavai => src}/gps.vala (100%) rename {zavai => src}/gsm.vala (100%) rename {zavai => src}/input.vala (100%) rename {zavai => src}/libgps.vapi (100%) rename {zavai => src}/libomhacks.vapi (100%) rename {zavai => src}/linux-input.vapi (86%) rename {zavai => src}/log.vala (100%) rename {zavai => src}/lua5.1.vapi (100%) rename {zavai => src}/power.vala (100%) rename {zavai => src}/registry.vala (100%) rename {zavai => src}/uevent.vala (100%) rename {zavai => src}/wifi.vala (100%) rename {zavai => src}/x11.vapi (100%) create mode 100644 zavai/__init__.py create mode 100644 zavai/app.py create mode 100755 zavai/audio.py create mode 100644 zavai/conf.py create mode 100644 zavai/config.py rename {src => zavai}/gps.py (100%) create mode 100644 zavai/menu.py create mode 100644 zavai/plugins.py create mode 100644 zavai/registry.py diff --git a/CMakeLists.txt b/CMakeLists.txt index ae82bc4..a022b78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,5 @@ set(zavai_version "0.1") add_subdirectory(gtkfisheyelist) add_subdirectory(polygen) add_subdirectory(player) -add_subdirectory(zavai) add_subdirectory(src) #add_subdirectory(hooks) diff --git a/README b/README index 0d17b4c..0bfa0a0 100644 --- a/README +++ b/README @@ -177,27 +177,22 @@ TODO list / wish list http://git.freesmartphone.org/?p=specs.git;a=blob_plain;f=html/index.html;hb=HEAD * Features to add: - - if GPS time is more than 1 minute and less than 1 hour different than the - system time, automatically sync it - if GPS time is more than 1 hour different than the system time, show a - "SYNC" button that will sync it if pressed - log - write data to disk as log happens (to have at least partial logs if power is cut) - more detailed GPX data (dop, elev..) - message (structured) (json?) - - flash aux when there are unacknowledged log entries - turn on/off gsm + start frameworkd as a subprocess, configured to only do phone + go through the dbus motions of turning on this and that, and entering PIN hardcoded in zavai config + GSM status on main screen (with messages while coming online, and operator, power and so on) - - log incoming messages - - log and refuse incoming calls + - log and refuse incoming calls and messages to "move" the GSM device to my laptop: socat FILE:/dev/ttySAC0 TCP-LISTEN:12345,bind=192.168.0.202 socat TCP:192.168.0.202:12345 PTY,link=/tmp/gps,raw,echo=0 + - alarm + play sound at alarm trigger - leave expired alarm on screen until acknowledged @@ -220,6 +215,13 @@ TODO list / wish list - audio notes - record audio notes, logging start and stop so it gets an associated GPX - shortcut icon in main screen + - check gobject introspection and dynamic loading + - there could be a basic loader core that just dynamically loads bits as + available/needed. Only the battery icon and calendar can be loaded on a + laptop, while on the moko one can also load the power menu. + Different bits can effectively work as plugins, to be there or not. + Alternative plugins can be loaded based on different technologies, too (a + gpsd satellite monitor, or a fso satellite monitor). - zavai does not look good in landscape mode - replace menus with a widget taking a list of actions, which then lays them out according to the screen size diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 06cacbd..657b95d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,7 @@ set(zavai_version 0.1) set(packages gtk+-2.0 dbus-glib-1>=0.80 libwnck-1.0>=2.26.0 lua5.1 libomhacks x11 gdk-x11-2.0 libgps gstreamer-0.10) add_packages(ZAVAI ${packages}) -set(VALA_PACKAGES ${packages} posix linux-input dbus-extra gtkfisheyelist libzavai) +set(VALA_PACKAGES ${packages} posix linux-input dbus-extra gtkfisheyelist) set(VFLAGS --vapidir=${zavai_SOURCE_DIR} --vapidir=${gtkfisheyelist_BINARY_DIR}) add_definitions(-Wall) # -Werror @@ -22,15 +22,14 @@ else() message("-- Not using devkit-power-gobject") endif () -set(VFLAGS ${VFLAGS} --vapidir=${zavai_BINARY_DIR} --vapidir=${libzavai_SOURCE_DIR} --vapidir=${libzavai_BINARY_DIR}) - file(GLOB libvala [a-y]*.vala widgets/*.vala) -add_vala_library(libzavaiui ${libvala}) -add_library(libzavaiui STATIC ${libzavaiui_CSOURCES}) +add_vala_library(libzavai ${libvala}) +add_library(libzavai STATIC ${libzavai_CSOURCES}) -add_definitions(-DWNCK_I_KNOW_THIS_IS_UNSTABLE -DI_KNOW_THE_DEVICEKIT_POWER_API_IS_SUBJECT_TO_CHANGE -I${gtkfisheyelist_BINARY_DIR} -I${zavai_BINARY_DIR} -I${libzavai_BINARY_DIR}) -set(VALA_PACKAGES ${VALA_PACKAGES} libzavai libzavaiui) -link_libraries(gtkfisheyelist-static libzavai libzavaiui) +add_definitions(-DWNCK_I_KNOW_THIS_IS_UNSTABLE -DI_KNOW_THE_DEVICEKIT_POWER_API_IS_SUBJECT_TO_CHANGE -I${gtkfisheyelist_BINARY_DIR} -I${zavai_BINARY_DIR}) +set(VFLAGS ${VFLAGS} --vapidir=${zavai_BINARY_DIR}) +set(VALA_PACKAGES ${VALA_PACKAGES} libzavai) +link_libraries(gtkfisheyelist-static libzavai) file(GLOB zavala zavai.vala) add_vala_program(zavai ${zavala}) diff --git a/src/app.vala b/src/app.vala index 6366b6a..5281f56 100644 --- a/src/app.vala +++ b/src/app.vala @@ -158,6 +158,37 @@ public class Zavai : Gtk.Window, zavai.Resource show_all(); } } + + public void run_script(string command) + { + zavai.log.info("Run program: " + command); + string[] args = command.split(" "); + Pid pid; + try { + Process.spawn_async( + Environment.get_home_dir(), + args, + null, + SpawnFlags.SEARCH_PATH, + null, + out pid); + } catch (SpawnError e) { + zavai.log.error("Running " + command + ": " + e.message); + } + } + + public int run_script_sync(string command, out string std_out, out string std_err) + { + int status = -1; + zavai.log.info("Run program: " + command); + string[] args = command.split(" "); + try { + bool res = Process.spawn_sync(Environment.get_home_dir(), args, null, SpawnFlags.SEARCH_PATH, null, out std_out, out std_err, out status); + } catch (SpawnError e) { + zavai.log.error("Running " + command + ": " + e.message); + } + return status; + } } public abstract class Applet : Gtk.VBox, Resource diff --git a/src/app_power.vala b/src/app_power.vala index 8fedf3f..33be3e9 100644 --- a/src/app_power.vala +++ b/src/app_power.vala @@ -129,7 +129,7 @@ public class Power : zavai.Resource, Object } */ try { - zavai.config.run_script("pm-suspend"); + zavai.app.run_script("pm-suspend"); done = true; zavai.log.info("Suspend was done with zavai."); } catch (Error e) { @@ -141,7 +141,7 @@ public class Power : zavai.Resource, Object { try { //usage.Shutdown(); - zavai.config.run_script("shutdown -h now"); + zavai.app.run_script("shutdown -h now"); } catch (Error e) { zavai.log.error("Shutting down phone: " + e.message); } @@ -150,7 +150,7 @@ public class Power : zavai.Resource, Object { try { //usage.Reboot(); - zavai.config.run_script("shutdown -r now"); + zavai.app.run_script("shutdown -r now"); } catch (Error e) { zavai.log.error("Rebooting phone: " + e.message); } @@ -174,7 +174,8 @@ public class Power : zavai.Resource, Object // FIXME: X won't see events, but it's still generating interrupts, // isn't it? - if (Posix.ioctl(screen_lock_fd, LinuxInput.Evio.CGRAB, locked ? 1 : 0) != 0) + int EVIOCGRAB = 0x40044590; + if (Posix.ioctl(screen_lock_fd, EVIOCGRAB, locked ? 1 : 0) != 0) { zavai.log.error("Cannot EVIOCGRAB /dev/input/event1"); Posix.close(screen_lock_fd); @@ -485,7 +486,7 @@ public class Backlight: zavai.Service public void wiggle() { try { - zavai.config.run_script(zavai.config.homedir + "/display wiggle"); + zavai.app.run_script(zavai.config.homedir + "/display wiggle"); } catch (Error e) { zavai.log.error("Requesting/releasing resource Display: " + e.message); } @@ -496,7 +497,7 @@ public class Backlight: zavai.Service if (!started) { try { - zavai.config.run_script(zavai.config.homedir + "/display lock_off"); + zavai.app.run_script(zavai.config.homedir + "/display lock_off"); } catch (GLib.Error e) { zavai.log.error(e.message); } @@ -506,7 +507,7 @@ public class Backlight: zavai.Service public void unlock_screen() { try { - zavai.config.run_script(zavai.config.homedir + "/display defaults"); + zavai.app.run_script(zavai.config.homedir + "/display defaults"); } catch (GLib.Error e) { zavai.log.error(e.message); } @@ -516,7 +517,7 @@ public class Backlight: zavai.Service { if (started) return; try { - zavai.config.run_script(zavai.config.homedir + "/display lock_on"); + zavai.app.run_script(zavai.config.homedir + "/display lock_on"); zavai.log.info("Acquired display"); base.start(); } catch (GLib.Error e) { @@ -529,7 +530,7 @@ public class Backlight: zavai.Service { if (!started) return; try { - zavai.config.run_script(zavai.config.homedir + "/display defaults"); + zavai.app.run_script(zavai.config.homedir + "/display defaults"); zavai.log.info("Released display"); base.stop(); } catch (GLib.Error e) { diff --git a/zavai/at.vala b/src/at.vala similarity index 100% rename from zavai/at.vala rename to src/at.vala diff --git a/zavai/audio.vala b/src/audio.vala similarity index 100% rename from zavai/audio.vala rename to src/audio.vala diff --git a/zavai/bluetooth.vala b/src/bluetooth.vala similarity index 100% rename from zavai/bluetooth.vala rename to src/bluetooth.vala diff --git a/zavai/clock.vala b/src/clock.vala similarity index 100% rename from zavai/clock.vala rename to src/clock.vala diff --git a/zavai/config.vala b/src/config.vala similarity index 86% rename from zavai/config.vala rename to src/config.vala index 444bbc2..3f9ff41 100644 --- a/zavai/config.vala +++ b/src/config.vala @@ -227,37 +227,6 @@ public class Config } refresh_from_lua(); } - - public void run_script(string command) - { - zavai.log.info("Run program: " + command); - string[] args = command.split(" "); - Pid pid; - try { - Process.spawn_async( - Environment.get_home_dir(), - args, - null, - SpawnFlags.SEARCH_PATH, - null, - out pid); - } catch (SpawnError e) { - zavai.log.error("Running " + command + ": " + e.message); - } - } - - public int run_script_sync(string command, out string std_out, out string std_err) - { - int status = -1; - zavai.log.info("Run program: " + command); - string[] args = command.split(" "); - try { - bool res = Process.spawn_sync(Environment.get_home_dir(), args, null, SpawnFlags.SEARCH_PATH, null, out std_out, out std_err, out status); - } catch (SpawnError e) { - zavai.log.error("Running " + command + ": " + e.message); - } - return status; - } } public Config config = null; diff --git a/zavai/core.vala b/src/core.vala similarity index 95% rename from zavai/core.vala rename to src/core.vala index 3c31ccd..27c621b 100644 --- a/zavai/core.vala +++ b/src/core.vala @@ -157,7 +157,7 @@ public abstract class ScriptService : Service { try { // Then run our own script - zavai.config.run_script(zavai.config.homedir + "/" + name + " start"); + zavai.app.run_script(zavai.config.homedir + "/" + name + " start"); return true; } catch (Error e) { zavai.log.error("Running " + zavai.config.homedir + "/" + name + " start: " + e.message); @@ -169,7 +169,7 @@ public abstract class ScriptService : Service { try { // Then run our own script - zavai.config.run_script(zavai.config.homedir + "/" + name + " stop"); + zavai.app.run_script(zavai.config.homedir + "/" + name + " stop"); return true; } catch (Error e) { zavai.log.error("Running " + zavai.config.homedir + "/" + name + " stop: " + e.message); @@ -182,7 +182,7 @@ public abstract class ScriptService : Service string std_out; string std_err; string command = zavai.config.homedir + "/" + name + " status"; - int res = zavai.config.run_script_sync(command, out std_out, out std_err); + int res = zavai.app.run_script_sync(command, out std_out, out std_err); if (res != 0) { zavai.log.error("Running " + command + ": " + std_err); @@ -211,7 +211,7 @@ public abstract class ScriptMonitorService : Service string command = zavai.config.homedir + "/" + name + " pre"; try { // Then run our own script - zavai.config.run_script(command); + zavai.app.run_script(command); } catch (Error e) { zavai.log.error("Running " + command + ": " + e.message); return false; @@ -254,7 +254,7 @@ stderr.printf("STATUS %d\n", status); string command = zavai.config.homedir + "/" + name + " post"; try { // Then run our own script - zavai.config.run_script(command); + zavai.app.run_script(command); } catch (Error e) { zavai.log.error("Running " + command + ": " + e.message); return; diff --git a/zavai/dbus-extra.vapi b/src/dbus-extra.vapi similarity index 100% rename from zavai/dbus-extra.vapi rename to src/dbus-extra.vapi diff --git a/zavai/devkit-power-gobject.vapi b/src/devkit-power-gobject.vapi similarity index 100% rename from zavai/devkit-power-gobject.vapi rename to src/devkit-power-gobject.vapi diff --git a/zavai/gps.vala b/src/gps.vala similarity index 100% rename from zavai/gps.vala rename to src/gps.vala diff --git a/zavai/gsm.vala b/src/gsm.vala similarity index 100% rename from zavai/gsm.vala rename to src/gsm.vala diff --git a/zavai/input.vala b/src/input.vala similarity index 100% rename from zavai/input.vala rename to src/input.vala diff --git a/zavai/libgps.vapi b/src/libgps.vapi similarity index 100% rename from zavai/libgps.vapi rename to src/libgps.vapi diff --git a/zavai/libomhacks.vapi b/src/libomhacks.vapi similarity index 100% rename from zavai/libomhacks.vapi rename to src/libomhacks.vapi diff --git a/zavai/linux-input.vapi b/src/linux-input.vapi similarity index 86% rename from zavai/linux-input.vapi rename to src/linux-input.vapi index 211eab5..835f849 100644 --- a/zavai/linux-input.vapi +++ b/src/linux-input.vapi @@ -33,10 +33,4 @@ namespace LinuxInput PWR, FF_STATUS } - - [CCode (cprefix="EVIO")] - public enum Evio - { - CGRAB - } } diff --git a/zavai/log.vala b/src/log.vala similarity index 100% rename from zavai/log.vala rename to src/log.vala diff --git a/zavai/lua5.1.vapi b/src/lua5.1.vapi similarity index 100% rename from zavai/lua5.1.vapi rename to src/lua5.1.vapi diff --git a/zavai/power.vala b/src/power.vala similarity index 100% rename from zavai/power.vala rename to src/power.vala diff --git a/zavai/registry.vala b/src/registry.vala similarity index 100% rename from zavai/registry.vala rename to src/registry.vala diff --git a/zavai/uevent.vala b/src/uevent.vala similarity index 100% rename from zavai/uevent.vala rename to src/uevent.vala diff --git a/zavai/wifi.vala b/src/wifi.vala similarity index 100% rename from zavai/wifi.vala rename to src/wifi.vala diff --git a/zavai/x11.vapi b/src/x11.vapi similarity index 100% rename from zavai/x11.vapi rename to src/x11.vapi diff --git a/src/zavai.vala b/src/zavai.vala index d1dede5..50a2c39 100644 --- a/src/zavai.vala +++ b/src/zavai.vala @@ -117,7 +117,7 @@ static int main (string[] args) { zavai.ui.main.init(); zavai.ui.gps.init(); zavai.ui.gsm.init(); - zavai.config.run_script(zavai.config.homedir + "/display init"); + zavai.app.run_script(zavai.config.homedir + "/display init"); zavai.ui.power.init(); zavai.ui.aux.init(); zavai.ui.kbd.init(); diff --git a/zavai/__init__.py b/zavai/__init__.py new file mode 100644 index 0000000..4d48db9 --- /dev/null +++ b/zavai/__init__.py @@ -0,0 +1,40 @@ +# zavai shared code +# +# Copyright (C) 2009 Enrico Zini +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from conf import read_config +from config import Config +from plugins import load_plugins +from registry import Registry, Resource, Service, get_parent, default_label +from menu import Menu, MenuButton, LinkButton, ToggleButton +from app import Zavai, Applet +from gps import GPS, GPX +from audio import Audio, Recorder + +VERSION="0.1" + +def warn(*args): + import sys + sys.stderr.write(" ".join(map(str, args)) + "\n") +def info(*args): + import sys + sys.stderr.write(" ".join(map(str, args)) + "\n") + +def set_quiet(): + def info(*args): + pass + diff --git a/zavai/app.py b/zavai/app.py new file mode 100644 index 0000000..4ce7852 --- /dev/null +++ b/zavai/app.py @@ -0,0 +1,91 @@ +# app - zavai main window +# +# Copyright (C) 2009 Enrico Zini +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import sys +from gettext import gettext as _ +import gtk, gobject +import zavai + +class Zavai(gtk.Window, zavai.Resource): + def __init__(self, registry, name): + super(Zavai, self).__init__() + self.registry = registry + self.current = None + self.activate_resource("menu.main") + + def activate_resource(self, name): + widget = self.registry.resource(name) + if widget is None: + widget = self.registry.resource("menu.main") + if isinstance(widget, gtk.Action): + widget.activate() + else: + self.show_widget(name, widget) + + def show_widget(self, name, widget): + # Remove the current widget. + # If it is an Applet, stop it + if self.current is not None: + cur = self.registry.resource(self.current) + if isinstance(cur, zavai.Applet): + cur.stop() + self.remove(self.get_child()) + self.current = None + + # Add the new widget. If it is an applet, start it + self.add(widget) + self.current = name + if isinstance(widget, zavai.Applet): + widget.start() + widget.show_all() + + def run(self): + self.set_size_request(100, 200) + #self.fullscreen() + self.show_all() + gtk.main() + +class Applet(gtk.VBox): + name = gobject.property(type=str) + label = gobject.property(type=str) + + def __init__(self, registry, name, label = None): + super(Applet, self).__init__() + + self.zavai_registry = registry + + self.props.name = name + if label is None: + self.props.label = zavai.default_label(name) + else: + self.props.label = label + + self.back_link = zavai.LinkButton(registry, zavai.get_parent(name), _("Back")) + self.pack_end(self.back_link, False, False) + + def add(self, widget): + self.pack_start(widget, True, True) + + def shutdown(self): + self.stop() + + def start(self, *args): + pass + + def stop(self, *args): + pass diff --git a/zavai/audio.py b/zavai/audio.py new file mode 100755 index 0000000..9d7835e --- /dev/null +++ b/zavai/audio.py @@ -0,0 +1,295 @@ +# audio - zavai audio resource +# +# Copyright (C) 2009 Enrico Zini +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import zavai +import struct +import gobject +import os +import os.path +import subprocess +import signal +#import sys +#import time +#import dbus + +class Recorder(zavai.Resource): + def __init__(self, registry): + super(Recorder, self).__init__() + self.registry = registry + self.recorder = None + + def shutdown(self): + self.stop() + + def start(self, filename): + if self.recorder is not None: return + + self.registry.resource("audio").connect("audio", self) + + self.recorder = subprocess.Popen( + ["arecord", "-D", "hw", "-f", "cd", "-r", "8000", "-t", "wav", filename]) + + def stop(self): + if self.recorder is None: return + + os.kill(self.recorder.pid, signal.SIGINT) + self.recorder.wait() + self.recorder = None + + self.registry.resource("audio").disconnect("audio", self) + + +# TODO: +# - hook into the headset plugged/unplugged event +# - if unplugged, turn on handset microphone +# - if plugged, redo headest mixer settings +class Audio(zavai.Service): + "Handle mixer settings, audio recording and headset button presses" + def __init__(self, registry, name): + super(Audio, self).__init__(["audio", "button", "jack"]) + + conf = registry.resource("conf") + self.saved_scenario = os.path.join(conf.homedir, "audiomap.state") + + self.input_fd = None + self.input_watch = None + + + def start(self): + # Setup the mixer + # Set mixer to record from headset and handle headset button + self.save_scenario(self.saved_scenario) + self.load_scenario("/usr/share/openmoko/scenarios/voip-handset.state") + + # This is a work-around because I have not found a way to query for the + # current headset state, I can only know when it changes. So in my + # system I configured oeventsd with a rule to touch this file when the + # headset is plugged in, and remove the file when it's plugged out. + if os.path.exists("/tmp/has_headset"): + self.mixer_for_headset(force = True) + else: + self.mixer_for_handset(force = True) + + # Watch the event device + self.input_fd = open("/dev/input/event4", "rb") + self.input_watch = gobject.io_add_watch(self.input_fd.fileno(), gobject.IO_IN, self.on_input_data) + self.last_button_press = 0 + +# #self.mixer_set("DAPM Handset Mic", "mute") +# #self.mixer_set("DAPM Headset Mic", "unmute") +# #self.mixer_set("Left Mixer Sidetone Playback Sw", "unmute") +# #self.mixer_set("ALC Mixer Mic1", "cap") +# #self.mixer_set("Amp Spk", "mute") # We don't need the phone playing what we say +# +# self.recorder = None +# self.basename = None + + def stop(self): + # Stop watching the event device + if self.input_fd is None: + return + gobject.source_remove(self.input_watch) + self.input_watch = None + self.input_fd.close() + self.input_fd = None + + # Restore mixer settings + self.load_scenario(self.saved_scenario) + + def on_input_data(self, source, condition): + buf = self.input_fd.read(16) + ts1, ts2, type, code, value = struct.unpack("LLHHI", buf) + if type == 1 and code == 119: + if self.last_button_press + 1 < ts1: + self.last_button_press = ts1 + else: + return True + if value: + zavai.info("Headset button release") + self.notify("button", False) + else: + zavai.info("Headset button press") + self.notify("button", True) + elif type == 5 and code == 2: + if value: + zavai.info("Headset plugged in") + self.mixer_for_headset() + self.notify("jack", True) + else: + zavai.info("Headset plugged out") + self.mixer_for_handset() + self.notify("jack", False) + return True + + def mixer_for_headset(self, force=False): + if not force and self.has_headset: return + zavai.info("Setting mixer for headset") + # TODO: find out how to disable the handset microphone: this does not + # seem to be sufficient + self.mixer_set_many( + ("DAPM Handset Mic", "mute"), + ("DAPM Headset Mic", "unmute"), + ("Left Mixer Sidetone Playback Sw", "unmute"), + ("ALC Mixer Mic1", "cap"), + ("Amp Spk", "mute") # We don't need the phone playing what we say + ) + self.has_headset = True + + def mixer_for_handset(self, force=False): + if not force and not self.has_headset: return + zavai.info("Setting mixer for handset") + self.mixer_set_many( + ("DAPM Handset Mic", "unmute"), + ("DAPM Headset Mic", "mute"), + ("Left Mixer Sidetone Playback Sw", "mute"), + ("ALC Mixer Mic1", "cap"), + ("Amp Spk", "mute") # We don't need the phone playing what we say + ) + self.has_headset = False + +# def set_basename(self, basename): +# self.basename = basename +# +# def start_levels(self): +# self.recorder = subprocess.Popen( +# ["arecord", "-D", "hw", "-f", "cd", "-r", "8000", "-t", "wav", "-V", "stereo", "/dev/null"]) + + def save_scenario(self, name): + while True: + res = subprocess.call(["alsactl", "store", "-f", name]) + if res == 0: return + if res > 0: + raise RuntimeError("Saving audio scenario to '%s' failed" % name) + + def load_scenario(self, name): + while True: + res = subprocess.call(["alsactl", "restore", "-f", name]) + if res == 0: return + if res > 0: + raise RuntimeError("Loading audio scenario '%s' failed with error %d" % (name, res)) + + def mixer_set(self, name, *args): + args = map(str, args) + while True: + res = subprocess.call(["amixer", "-q", "set", name] + args) + if res == 0: return + if res > 0: + raise RuntimeError("Setting mixer '%s' to %s failed with error %d" % (name, " ".join(args), res)) + + def mixer_set_many(self, *args): + """Perform many mixer set operations via amixer --stdin""" + cmd_input = [] + for k, v in args: + cmd_input.append("sset " + repr(k) + " " + repr(v)) + while True: + proc = subprocess.Popen(["amixer", "-q", "--stdin"], stdin=subprocess.PIPE) + (out, err) = proc.communicate(input="\n".join(cmd_input)) + res = proc.wait() + if res == 0: return + if res > 0: + raise RuntimeError("Setting mixer failed with error %d" % res) + + +#class Hub: +# """Hub that manages all the various resources that we use, and initiates +# operations.""" +# def __init__(self, bus = None): +# self.bus = bus +# self.waiting_for_fix = False +# self.basename = None +# self.recorder = None +# self.gpx = None +# self.gps = None +# self.gps_monitor = None +# self.audio = None +# self.last_pos = None +# +# def shutdown(self): +# # Stop recording +# if self.audio is not None: +# self.audio.close() +# +# # Close waypoints file +# if self.gpx is not None: +# self.gpx.close() +# +# # Stop the GPS monitor +# if self.gps_monitor: +# self.gps_monitor.stop() +# +# # Release the GPS +# if self.gps is not None: +# self.gps.close() +# +# def levels(self): +# self.audio = Audio() +# self.audio.start_levels() +# +# def record(self): +# self.audio = Audio(self.make_waypoint) +# self.gps = GPS() +# # Get a fix and start recording +# if not self.gps.wait_for_fix(self.start_recording): +# self.gps_monitor = GPSMonitor(self.gps) +# self.gps_monitor.start() +# +# def monitor(self): +# self.audio = None +# self.gps = GPS() +# self.gps_monitor = GPSMonitor(self.gps) +# self.gps_monitor.start() +# +# def start_recording(self): +# if self.gps_monitor: +# self.gps_monitor.stop() +# self.gps_monitor = None +# +# if not self.audio: +# return +# +# # Sync system time +# gpstime = self.gps.gps_time.GetTime() +# subprocess.call(["date", "-s", "@%d" % gpstime]) +# subprocess.call(["hwclock", "--systohc"]) +# +# # Compute basename for output files +# self.basename = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(gpstime)) +# self.basename = os.path.join(AUDIODIR, self.basename) +# +# # Start recording the GPX track +# self.gpx = GPX(self.basename) +# self.gps.track_position(self.on_position_changed) +# +# # Start recording in background forking arecord +# self.audio.set_basename(self.basename) +# self.audio.start_recording() +# +# def on_position_changed(self, fields, tstamp, lat, lon, alt): +# self.last_pos = (fields, tstamp, lat, lon, alt) +# if self.gpx: +# self.gpx.trackpoint(tstamp, lat, lon, alt) +# +# def make_waypoint(self): +# if self.gpx is None: +# return +# if self.last_pos is None: +# self.last_pos = self.gps.gps_position.GetPosition() +# (fields, tstamp, lat, lon, alt) = self.last_pos +# self.gpx.waypoint(tstamp, lat, lon, alt) +# info("Making waypoint at %s: %f, %f, %f" % ( +# time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(tstamp)), lat, lon, alt)) diff --git a/zavai/conf.py b/zavai/conf.py new file mode 100644 index 0000000..ba253a4 --- /dev/null +++ b/zavai/conf.py @@ -0,0 +1,33 @@ +import os +import ConfigParser, StringIO + +def read_config(rootDir = None, defaults = None, nick="octofuss"): + """ + Read octofuss configuration, returning a ConfigParser object + """ + if rootDir == None: + rootDir = os.environ.get(nick.upper() + "_CONFDIR", "/etc/" + nick) + files = [] + def trytouse(path): + if os.path.exists(path): + files.append(path) + + # Start with the main config file + trytouse(os.path.join(rootDir, nick + ".conf")) + + # Add snippets found in rc.d style directory + subdir = os.path.join(rootDir, nick + ".conf.d") + if os.path.isdir(subdir): + for file in sorted(os.listdir(subdir)): + if file.startswith('#'): continue + if file.startswith('.'): continue + if file.endswith('~'): continue + if file.endswith('.bak'): continue + trytouse(os.path.join(subdir, file)) + + config = ConfigParser.ConfigParser() + if defaults != None: + infile = StringIO.StringIO(defaults) + config.readfp(infile, "defaults") + config.read(files) + return config diff --git a/zavai/config.py b/zavai/config.py new file mode 100644 index 0000000..e328cb8 --- /dev/null +++ b/zavai/config.py @@ -0,0 +1,23 @@ +import os.path +import zavai + +class Config: + def __init__(self): + self.conf = zavai.read_config(nick="zavai") + + def get(self, section, name, default=None): + if self.conf.has_section(section): + if self.conf.has_option(section, name): + return self.conf.get(section, name) + return None + + def _get_homedir(self): + res = self.get("global", "home") + if res is None: + res = os.path.expanduser("~/.zavai") + if not os.path.isdir(res): + zavai.info("Creating directory", res) + os.makedirs(res) + return res + + homedir = property(_get_homedir) diff --git a/src/gps.py b/zavai/gps.py similarity index 100% rename from src/gps.py rename to zavai/gps.py diff --git a/zavai/menu.py b/zavai/menu.py new file mode 100644 index 0000000..629495a --- /dev/null +++ b/zavai/menu.py @@ -0,0 +1,68 @@ +# menu - zavai menus +# +# Copyright (C) 2009 Enrico Zini +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import sys +from gettext import gettext as _ +import zavai +import gtk + +class MenuButton(gtk.Button): + def __init__(self, *args, **kw): + super(MenuButton, self).__init__(*args, **kw) + # TODO: take this from the configuration + self.set_size_request(0, 80) + +class Menu(gtk.VBox, zavai.Resource): + def __init__(self, registry, name, parent = None, *args, **kw): + super(Menu, self).__init__() + self.vbox = self + + if parent is not None: + self.vbox = gtk.VBox() + self.pack_start(self.vbox, False, False) + self.pack_start(gtk.Label(""), True, True) + self.pack_start(LinkButton(registry, parent, _("Back")), False, False) + + def add_child(self, widget): + self.vbox.pack_start(widget, False, False) + +class LinkButton(MenuButton, zavai.Resource): + def __init__(self, registry, targetName, label=None, action=None): + if action is not None: + super(LinkButton, self).__init__() + self.target = targetName + self.registry = registry + self.action = action + action.connect_proxy(self) + self.connect("clicked", self.on_click) + else: + super(LinkButton, self).__init__(label) + self.target = targetName + self.registry = registry + self.action = None + self.connect("clicked", self.on_click) + + def on_click(self, *args): + self.registry.resource("app").activate_resource(self.target) + +class ToggleButton(gtk.ToggleButton): + def __init__(self, registry, targetName, action, *args, **kw): + super(ToggleButton, self).__init__() + # TODO: take this from the configuration + self.set_size_request(0, 80) + action.connect_proxy(self) diff --git a/zavai/plugins.py b/zavai/plugins.py new file mode 100644 index 0000000..d736610 --- /dev/null +++ b/zavai/plugins.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# +# File: plugins.py +# +# Copyright (C) 2008 Christopher R. Gabriel +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# + +import os +import os.path +import imp +import sys + +def load_plugins(pluginpath=None, nick="octofuss"): + """return a list of all available plugins + in the given directory""" + + if not pluginpath: + pluginpath = os.environ.get(nick.upper() + "_PLUGINS", "plugins") + + # if the plugin path need to be auto-discovered + # starting from a specified module + #pluginpath = os.path.join(os.path.dirname(imp.find_module("octofussd")[1]), "extensions/") + #pluginpath = "." + pluginfiles = [fname[:-3] for fname in os.listdir(pluginpath) if fname.endswith(".py") and not fname.startswith(".") and not fname.endswith("~")] + + for fname in sorted(pluginfiles): + oldpath = sys.path + try: + sys.path.append(os.path.abspath(pluginpath)) + res = imp.load_source(fname, os.path.join(pluginpath, fname) + ".py") + finally: + sys.path = oldpath + yield res + diff --git a/zavai/registry.py b/zavai/registry.py new file mode 100644 index 0000000..4fc2101 --- /dev/null +++ b/zavai/registry.py @@ -0,0 +1,210 @@ +# registry - zavai resource registry +# +# Copyright (C) 2009 Enrico Zini +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import zavai +import gtk + +def get_parent(s): + "Get the parent name for s" + pos = s.rfind(".") + if pos == -1: return None + res = s[:pos] + if res == "menu": return None + return res + +def default_label(s): + "Compute a default label given the last element of a path" + pos = s.rfind(".") + if pos == -1: return s.capitalize() + return s[pos+1:].capitalize() + + +class Registry(object): + """Collection of resources. + + Various factories can be registered by name on the registry. Then when an + object is requested for the first time, it is created using the factory. + When it is requested again, the existing object is reused. + """ + + def __init__(self): + self.factories = dict() + self.objects = dict() + self.labels = dict() + + def register(self, obj, name=None): + """Register an object at the given path. + + Name the path to this object, like "menu.gps.monitor". + """ + if name is None: + name = obj.props.name + + if name in self.objects: + return KeyError("%s is already registered", name) + zavai.info("Registering", name) + self.objects[name] = obj + + if name.startswith("menu."): + self.add_to_menu(name) + + def register_factory(self, fac, name, label = None): + """Register an object factory at the given path. + + Name the path to this object, like "menu.gps.monitor". + """ + if name in self.factories: + return KeyError("Factory %s is already registered", name) + zavai.info("Registering factory", name) + self.factories[name] = fac + if label is not None: self.labels[name] = label + + def add_to_menu(self, name): + "Add the applet with the given name to the menu structure" + parent = get_parent(name) + if parent is not None: + zavai.info("Add to menu", name, parent) + menu = self.menu(parent) + + obj = self.resource(name) + if isinstance(obj, gtk.ToggleAction): + menu.add_child(zavai.ToggleButton(self, name, action=obj)) + elif isinstance(obj, gtk.Action): + menu.add_child(zavai.LinkButton(self, name, action=obj)) + else: + menu.add_child(zavai.LinkButton(self, name, self.label(name))) + + def label(self, name): + "Return the label for the object with the given name" + res = self.labels.get(name) + if res is not None: + return res + try: + obj = self.resource(name) + return obj.props.label + except: + return default_label(name) + + def resource(self, name): + """Get a resource from the registry. + + If no resource exists at `name` but there is a factory, instantiate the + object using the factory. + + If not even a factory exists at `name`, returns None. + """ + res = self.objects.get(name, None) + if res is None: + fac = self.factories.get(name, None) + if fac is not None: + res = self.objects[name] = fac(self, name) + return res + + def menu(self, name): + """Get a menu resource, automatically creating it if it is missing. + + Menus are created automatically linked to a parent menu, according to + the hierarchy in `name`. + """ + res = self.resource(name) + if res is None: + # Check if it is a toplevel menu + if name.startswith("menu."): + parent = get_parent(name[5:]) + if parent is not None: + parent = "menu." + parent + else: + parent = get_parent(name) + + res = zavai.Menu(self, name, parent) + self.register(res, name) + return res + + def shutdown(self): + """Shut down all objects in this Registry. + + After shutting down, all objects cannot be used anymore""" + for o in self.objects.itervalues(): + if isinstance(o, Resource): + o.shutdown() + self.objects.clear() + +class Resource(object): + def __init__(self): + super(Resource, self).__init__() + + def shutdown(self): + """Shut down this resource. + + Normally one does nothing here, but it is important to give resources a + chance to do cleanup when the program quits. + + This can be used for tasks like closing the tags on a GPX track, + releasing a FSO resource, restoring mixer settings and so on. + """ + pass + +class Service(Resource): + "Service that is activated only when someone is listening" + def __init__(self, types = []): + """ + Initialise a service that can emit signals for the given event types + """ + super(Service, self).__init__() + self.callbacks = dict() + for t in types: + self.callbacks[t] = set() + self.started = False + + def shutdown(self): + if self.started: + self.stop() + + def start(self): + "Activate the service" + pass + + def stop(self): + "Deactivate the service" + pass + + def notify(self, type, *args, **kw): + "Call all callbacks with the given parameters" + for cb in self.callbacks[type]: + cb(*args, **kw) + + def has_callbacks(self): + for i in self.callbacks.values(): + if i: return True + return False + + def connect(self, type, callback): + "Connect a callback to this resource, activating it if needed" + do_start = not self.has_callbacks() + self.callbacks[type].add(callback) + if do_start: + self.start() + self.started = True + + def disconnect(self, type, callback): + "Disconnect a callback to this resource, activating it if needed" + if not self.has_callbacks(): return + self.callbacks[type].discard(callback) + if not self.has_callbacks(): + self.stop() + self.started = False -- 2.39.5