From f6fe775bb6cf8bd1c20b8938ef2f523c5f34ebc4 Mon Sep 17 00:00:00 2001 From: Michael Mrozek Date: Sat, 11 Jun 2011 09:17:00 +0200 Subject: [PATCH] pandora-scripts: Added new version of op_nubmode and added that to sudoers --- recipes/pandora-system/pandora-scripts.bb | 5 +- .../pandora-scripts/nubmode.glade | 187 ++++---- .../pandora-scripts/op_nubmode.py | 442 +++++++++++------- .../pandora-scripts/pndlogo.png | Bin 5449 -> 1929 bytes .../pandora-scripts/reset_nubs.sh | 4 + recipes/pandora-system/pandora-sudoers.bb | 2 +- .../pandora-sudoers/50_openpandora | 2 +- 7 files changed, 370 insertions(+), 272 deletions(-) create mode 100755 recipes/pandora-system/pandora-scripts/reset_nubs.sh diff --git a/recipes/pandora-system/pandora-scripts.bb b/recipes/pandora-system/pandora-scripts.bb index b16b4b1..a80a15d 100644 --- a/recipes/pandora-system/pandora-scripts.bb +++ b/recipes/pandora-system/pandora-scripts.bb @@ -6,7 +6,7 @@ COMPATIBLE_MACHINE = "omap3-pandora" DEPENDS = "zenity dbus" RDEPENDS = "zenity dbus" -PR = "r62" +PR = "r64" SRC_URI = " \ file://op_bright.sh \ @@ -50,6 +50,7 @@ SRC_URI = " \ file://service.conf \ file://nub_profiles.conf \ file://nubmode.glade \ + file://reset_nubs.sh \ file://pndlogo.png \ file://default_up \ file://none_up \ @@ -94,7 +95,9 @@ do_install() { install -m 0755 ${WORKDIR}/op_battlow.sh ${D}${prefix}/pandora/scripts/ install -m 0755 ${WORKDIR}/op_bright_up.sh ${D}${prefix}/pandora/scripts/ install -m 0755 ${WORKDIR}/op_menu.sh ${D}${prefix}/pandora/scripts/ + install -m 0755 ${WORKDIR}/reset_nubs.sh ${D}${prefix}/pandora/scripts/ install -m 0644 ${WORKDIR}/pndlogo.png ${D}${prefix}/pandora/scripts/ + install -m 0644 ${WORKDIR}/nubmode.glade ${D}${prefix}/pandora/scripts/ diff --git a/recipes/pandora-system/pandora-scripts/nubmode.glade b/recipes/pandora-system/pandora-scripts/nubmode.glade index a77be2b..8bbd937 100755 --- a/recipes/pandora-system/pandora-scripts/nubmode.glade +++ b/recipes/pandora-system/pandora-scripts/nubmode.glade @@ -5,6 +5,7 @@ Nub configuration center + True @@ -24,7 +25,7 @@ True - + mouse movement True True @@ -37,42 +38,42 @@ - + mouse buttons True True False True True - LeftRadio_mouse + Rmouse0 1 - + scrolling True True False True True - LeftRadio_mouse + Rmouse0 2 - + joystick True True False True True - LeftRadio_mouse + Rmouse0 3 @@ -104,7 +105,7 @@ True - + mouse movement True True @@ -117,42 +118,42 @@ - + mouse buttons True True False True True - RightRadio_mouse + Rmouse1 1 - + scrolling True True False True True - RightRadio_mouse + Rmouse1 2 - + joystick True True False True True - RightRadio_mouse + Rmouse1 3 @@ -179,7 +180,7 @@ True True - lbutton + button0 0 @@ -191,7 +192,7 @@ True True - lmouse + mouse0 0 @@ -274,7 +275,7 @@ True True - lscroll + rate0 0 @@ -286,7 +287,7 @@ True True - lscrollx + scrollx0 0 @@ -298,7 +299,7 @@ True True - lscrolly + scrolly0 0 @@ -310,7 +311,7 @@ True True - rmouse + mouse1 0 @@ -324,7 +325,7 @@ True True - rbutton + button1 0 @@ -338,7 +339,7 @@ True True - rscroll + rate1 0 @@ -352,7 +353,7 @@ True True - rscrollx + scrollx1 0 @@ -367,7 +368,7 @@ True True delayed - rscrolly + scrolly1 0 @@ -389,77 +390,33 @@ - - Reset left nub + True - True - True - - 8 - 9 + 1 + 2 + 9 + 10 2 - - Reset right nub + True - True - True - + 2 - 2 3 - 8 - 9 - 2 - - - - - Scan nub state - True - True - True - top - - - - 1 - 2 - 8 - 9 - 2 + 10 + 11 - + + 84 True - - - 84 - True - pndlogo.png - - - 0 - - - - - Apply configuration - True - True - True - - - - 1 - - + pndlogo.png 1 @@ -475,117 +432,141 @@ + 2 + 3 9 10 2 - + + Delete profile True + True + True + - 1 - 2 9 10 2 - - Load profile + + Write nub settings True True True - + 2 3 - 9 - 10 + 8 + 9 2 - + + Read nub settings True - 2 + True + True + top + - 3 - 10 - 11 + 1 + 2 + 8 + 9 + 2 + + + + + Reset nubs... + True + True + True + + + + 8 + 9 + 2 - + 150 50 300 1 10 - + 150 50 300 1 10 - + 20 1 40 1 10 - + 20 1 40 1 10 - + 20 1 40 1 10 - + 20 1 40 1 10 - + 7 -32 32 1 10 - + 7 -32 32 1 10 - + 7 -32 32 1 10 - + 7 -32 32 diff --git a/recipes/pandora-system/pandora-scripts/op_nubmode.py b/recipes/pandora-system/pandora-scripts/op_nubmode.py index f20dfda..7b858ff 100755 --- a/recipes/pandora-system/pandora-scripts/op_nubmode.py +++ b/recipes/pandora-system/pandora-scripts/op_nubmode.py @@ -1,11 +1,5 @@ #!/usr/bin/python -# TODO: -# - fix nub reset (permission issue) and add test -# - wrap into PND -# - upload into beta software, gather & process feedback -# - upload into repo - import os import re import sys @@ -13,88 +7,164 @@ import gtk import time import optparse -# EDs reset in op_nubmode.sh -# -# @left-nub: 3-0066/reset - -# echo 1 > /sys/bus/i2c/drivers/vsense/3-0067/reset -# sleep 1 -# echo 0 > /sys/bus/i2c/drivers/vsense/3-0067/reset -# curmode=$(cat /proc/pandora/nub1/mode) -# echo mouse > /proc/pandora/nub1/mode -# while ! zenity --question --title="Resetted right nub" --text="The right nub has been resetted.\nPlease try to move the mouse cursor\nto test if it is working properly." --ok-label="Working properly" --cancel-label="Reset again"; do -# echo 1 > /sys/bus/i2c/drivers/vsense/3-0067/reset -# sleep 1 -# echo 0 > /sys/bus/i2c/drivers/vsense/3-0067/reset -# done -# echo $curmode > /proc/pandora/nub1/mode - # ================================================================ GUI_DESCRIPTION = 'nubmode.glade' PROFILES = '/etc/pandora/conf/nub_profiles.conf' +# Shell command to reset nub: 3-0066 = left-nub, 3-0067 = right-nub +# apparently they are linked and resetting one resets both. +#RESET_CMD = 'echo %i > /sys/bus/i2c/drivers/vsense/3-00%i/reset' +RESET_CMD = "sudo ./reset_nubs.sh" + +# Valid values for mode setting MODES = ("mouse", "mbuttons", "scroll", "absolute") -SLIDERS = ("mouse", "button", "scroll", "scrollx", "scrolly") +# Paths for reading and writing different configuration options %i -> 0 | 1 +SETTINGS = dict( + mode='/proc/pandora/nub%s/mode', + mouse='/proc/pandora/nub%s/mouse_sensitivity', + button='/proc/pandora/nub%s/mbutton_threshold', + rate='/proc/pandora/nub%s/scroll_rate', + scrollx='/proc/pandora/nub%s/scrollx_sensitivity', + scrolly='/proc/pandora/nub%s/scrolly_sensitivity', +) + +# format for saving/loading +FILE_ORDER = ("mode", "mouse", "button", "rate", "scrollx", "scrolly") + +# Default configuration DEFAULT_PROFILENAME = "Default" -DEFAULT_PROFILE = [DEFAULT_PROFILENAME, - "mouse 150 20 20 7 7", "mbuttons 150 20 20 7 7"] +DEFAULT_DICT = dict(mode0='mouse', mode1='mbuttons', mouse0='150', mouse1='150', + button0='20', button1='20', rate0='20', rate1='20', + scrollx0='7', scrollx1='7', scrolly0='7', scrolly1='7') + +# Types of model changes for view updates +(MODEL_PROFILE_CHANGE, MODEL_VALUE_CHANGE) = range(2) + +# Documentation for commandline options +HELP_RESET = "Reset specified nub(s). Format: left,right" +HELP_LEFT_NUB = ("Configure left nub. Include -a flag to activate. Format: %s. E.g. %s" % + (' '.join(FILE_ORDER), + ' '.join(DEFAULT_DICT[k+'0'] for k in FILE_ORDER))) +HELP_RIGHT_NUB = ("Configure right nub. Include -a flag to activate. Format: %s. E.g. %s" % + (' '.join(FILE_ORDER), + ' '.join(DEFAULT_DICT[k+'1'] for k in FILE_ORDER))) +HELP_SAVE = "Store current configuration as specified profile (no spaces allowed)" +HELP_APPLY = "Write currently loaded configuration to nubs." +HELP_LOAD = "Load and apply specified nub configuration profile" +HELP_DEL = "Delete specified nub configuration profile" + +# Commandline input type checking +TYPECHECK = dict(mode='(%s)' % '|'.join(MODES), + mouse='(\d+)', button='(\d+)', + rate='(\d+)', scrollx='(\d+)', scrolly='(\d+)') +RE_FORMAT = ','.join(TYPECHECK[k] for k in FILE_ORDER) +BOUNDS = dict(mouse=(50, 300), button=(1, 40), rate=(1, 40), + scrollx=(-32,32), scrolly=(-32,32)) -RESET_CMD_LEFT = 'echo %i > /sys/bus/i2c/drivers/vsense/3-0066/reset' -RESET_CMD_RIGHT = 'echo %i > /sys/bus/i2c/drivers/vsense/3-0067/reset' +# ================================================================ +# There is a bug in setting scrollx/scrolly sensitivity in the +# firmware (to be fixed in hotfix 6). +# Detect and set FIX_SCROLLXY_BUG to use a workaround. -# Settings read/written to /proc/pandora/nub (: 0 or 1) -FILES = "mode mouse_sensitivity mbutton_threshold scroll_rate scrollx_sensitivity scrolly_sensitivity".split(' ') -LEFT_NUB_CONFIG = [os.path.join('/proc/pandora/nub0', x) for x in FILES] -RIGHT_NUB_CONFIG = [os.path.join('/proc/pandora/nub1', x) for x in FILES] +def ReadWriteTest(value=None): + with open(SETTINGS['scrollx'] % 0, 'w' if value else 'r') as f: + return f.write('%s\n' % value) if value else f.readline().rstrip() -RE_FORMAT = "(%s),(\d+),(\d+),(\d+),(\d+),(\d+)" % '|'.join(MODES) -BOUNDS = ((50, 300), (1, 40), (1, 40), (-32, 32), (-32, 32)) +tmp = int(ReadWriteTest()) # backup original +ReadWriteTest(tmp + (-1 if tmp < 0 else 1)) # write corrected value +FIX_SCROLLXY_BUG = int(ReadWriteTest()) == tmp # fix bug if equal to original +ReadWriteTest(tmp + ((-1 if tmp < 0 else 1) if FIX_SCROLLXY_BUG else 0)) # restore # ================================================================ -def Validate(value): - mo = re.match(RE_FORMAT, value) - if mo and all(lower <= int(value) <= upper for (value, (lower, upper)) in - zip(mo.groups()[1:], BOUNDS)): - return True, list(mo.groups()) - else: - return False, None - -def ReadConfigFromProc(paths): - config = [] - for filepath in paths: - with open(filepath) as f: - config.append(f.readline().strip()) +def ReadProc(): + config = {} + for key, value in SETTINGS.iteritems(): + for c in '01': + with open(value % c, 'r') as f: + config[key+c] = f.readline().strip() return config -def StoreConfigToProc(paths, values): - # fix for scrollx, scrolly being stored one closer to zero than specified - values[-2:] = [int(v) + (-1 if v < 0 else 1) for v in values[-2:]] - - for filepath, value in zip(paths, values): - with open(filepath, 'w') as f: - f.write('%s\n' % value) +def StoreProc(dictionary): + # fix for value decrement/increment after written + if FIX_SCROLLXY_BUG: + dictionary = dictionary.copy() + for key in ('scrollx0', 'scrolly0', 'scrollx1', 'scrolly1'): + value = int(dictionary[key]) + value += -1 if value < 0 else 1 + dictionary[key] = str(value) + for key, value in SETTINGS.iteritems(): + for c in '01': + with open(value % c, 'w') as f: + f.write('%s\n' % dictionary[key+c]) + +def ProfileToString(dictionary): + return ' '.join(dictionary[k+c] for c in '01' for k in FILE_ORDER) + +def StringToProfile(line): + return dict(zip((k+c for c in '01' for k in FILE_ORDER), line.split(' '))) +def Validate(value): + mo = re.match(RE_FORMAT, value) + if mo: # verify bounds + values = dict(zip(FILE_ORDER, mo.groups())) + if all(BOUNDS[k][0] <= int(values[k]) <= BOUNDS[k][1] for k in BOUNDS): + return values + return {} -class Nub(object): - def __init__(self, builder, modeprefix, sliderprefix): - self.radios = [builder.get_object(modeprefix % m) for m in MODES] - self.sliders = [builder.get_object(sliderprefix % s) for s in SLIDERS] +# ================================================================ - def SetConfig(self, config): - i = MODES.index(config[0]) - self.radios[i].set_active(True) - for s, v in zip(self.sliders, config[1:]): - s.value = int(v) +class NubModel(object): + def __init__(self, view=None): + self.views = [view] if view else [] + self.settings = ReadProc() + self.profiles = {} + with open(PROFILES) as f: + name = None + for line in f: + if name is None: + name = line.rstrip() + else: + self.profiles[name] = StringToProfile(line.rstrip()) + name = None + + def notify(self, reason, *args): + for v in self.views: + v.update_view(reason, *args) + + def set_profile(self, name, dictionary): + notify = name not in self.profiles + self.profiles.setdefault(name, {}).update(dictionary) + if notify: + self.notify(MODEL_PROFILE_CHANGE, name, self.profiles) + + def delete_profile(self, name): + notify = name in self.profiles + del self.profiles[name] + if notify: + self.notify(MODEL_PROFILE_CHANGE, '', self.profiles) + + def load_profile(self, settings): + self.settings.update(settings) + self.notify(MODEL_VALUE_CHANGE, self.settings) + + def load_named_profile(self, name): + notify = True + if name == DEFAULT_PROFILENAME: + self.settings.update(DEFAULT_DICT) + elif name in self.profiles: + self.settings.update(self.profiles[name]) + else: + notify = False + if notify: + self.notify(MODEL_VALUE_CHANGE, self.settings) - def GetConfig(self): - i = (j for j, r in enumerate(self.radios) if r.get_active()).next() - config = [MODES[i]] - for s in self.sliders: - config.append(str(int(s.value))) - return config + def store_profiles(self, filename): + with open(filename, 'w') as f: + for name in sorted(self.profiles.keys()): + f.write('%s\n%s\n' % (name, ProfileToString(self.profiles[name]))) class NubConfig(object): @@ -105,151 +175,191 @@ class NubConfig(object): os.path.join(os.path.dirname(__file__), GUI_DESCRIPTION)) builder.connect_signals(self) - self.leftnub = Nub(builder, "LeftRadio_%s", "l%s") - self.rightnub = Nub(builder, "RightRadio_%s", "r%s") + # slider widgets, more specifically: their Adjustment objects + self.widgets = {} + for s in DEFAULT_DICT: + w = builder.get_object(s) + if w is not None: + w.connect('value-changed', self.on_slider_changed, s) + self.widgets[s] = w + + # radio buttons + for c in '01': + group = [] + for m in MODES: + w = builder.get_object('R%s%s' % (m,c)) + w.connect('clicked', self.on_radio_changed, 'mode'+c, m) + group.append(w) + self.widgets['mode'+c] = group + + self.statusbar = builder.get_object('statusbar') + self.contextid = self.statusbar.get_context_id('') - self.profiles = gtk.ListStore(str, str, str) - self.profiles.append(DEFAULT_PROFILE) - with open(PROFILES) as f: - c = map(str.rstrip, f.readlines()) - for p in zip(*(c[i::3] for i in range(3))): - self.profiles.append(p) + self.model = NubModel(self) - self.statusbar = builder.get_object("statusbar") - self.contextid = self.statusbar.get_context_id('') + self.profiles = gtk.ListStore(str) + self.profiles.append([DEFAULT_PROFILENAME]) + for name in self.model.profiles: + self.profiles.append([name]) - self.comboentry = builder.get_object("ProfileComboEntry") + self.comboentry = builder.get_object('ProfileComboEntry') self.comboentry.set_model(self.profiles) self.comboentry.set_text_column(0) + self.comboentry.connect('changed', self.on_combo_changed) self.entry = self.comboentry.get_child() - self.entry.connect('changed', self.on_combo_changed) + self.entry.connect('activate', self.on_LoadProfile_clicked) - self.load = builder.get_object('LoadProfile') + # we need a reference to change their sensitive + # setting according to the profile + self.delete = builder.get_object('DeleteProfile') self.save = builder.get_object('SaveProfile') + # this also changes sensitive of self.save & self.load self.comboentry.set_active(0) - # read current config: - self.on_UndoChanges_clicked(None) + # read current config + self.model.load_profile(ReadProc()) self.window = builder.get_object("window") - self.window.connect("destroy", self.on_window_destroy) self.window.show_all() accelgrp = gtk.AccelGroup() key, mod = gtk.accelerator_parse('Q') accelgrp.connect_group(key, mod, 0, self.on_window_destroy) - self.window.add_accel_group(accelgrp) - - def write_profiles_to_file(self): - with open(PROFILES, 'w') as f: - for name,left,right in self.profiles: - if name != DEFAULT_PROFILENAME: - f.write("%s\n%s\n%s\n" % (name, left, right)) + self.window.add_accel_group(accelgrp) + + def _update_profile_list(self, new, profiles): + self.profiles.clear() + self.profiles.append([DEFAULT_PROFILENAME]) + activate = 0 + for i, key in enumerate(sorted(profiles.keys())): + self.profiles.append([key]) + if key == new: + activate = i + self.comboentry.set_active(activate) + + def _update_widgets(self, settings): + for key, value in settings.iteritems(): + if 'mode' in key: + for w, m in zip(self.widgets[key], MODES): + w.set_active(m == value) + else: + self.widgets[key].value = int(value) + + def update_view(self, reason, *data): + if reason == MODEL_PROFILE_CHANGE: + self._update_profile_list(*data) + elif reason == MODEL_VALUE_CHANGE: + self._update_widgets(*data) + + def _lose_active(self): + # Forces the comboboxentry to lose its active selection + # such that it also generates a changed signal when we + # select the same value. + temp = self.entry.get_text() + self.entry.set_text('') + self.entry.set_text(temp) def Notify(self, message): self.statusbar.pop(self.contextid) self.statusbar.push(self.contextid, message) + def on_slider_changed(self, widget, key): + self.model.settings[key] = str(int(widget.value)) + + def on_radio_changed(self, widget, key, mode): + self.model.settings[key] = mode + def on_combo_changed(self, widget, *data): - profileid = self.comboentry.get_active() - self.load.set_sensitive(profileid != -1) - self.save.set_sensitive(profileid != 0 and self.entry.get_text() != '') - - def on_ResetLeft_clicked(self, widget, *data): - self.Notify("Resetting left nub...") - os.system(RESET_CMD_LEFT % 1) - time.sleep(1) - os.system(RESET_CMD_LEFT % 0) - self.Notify("Left nub reset") - - def on_ResetRight_clicked(self, widget, *data): - self.Notify("Resetting right nub...") - os.system(RESET_CMD_RIGHT % 1) - time.sleep(1) - os.system(RESET_CMD_RIGHT % 0) - self.Notify("Right nub reset") - - def on_UndoChanges_clicked(self, widget, *data): - self.leftnub.SetConfig(ReadConfigFromProc(LEFT_NUB_CONFIG)) - self.rightnub.SetConfig(ReadConfigFromProc(RIGHT_NUB_CONFIG)) + name = self.entry.get_text() + self.delete.set_sensitive(name in self.model.profiles) + self.save.set_sensitive(name != '' and name != DEFAULT_PROFILENAME) + if self.comboentry.get_active() != -1: + self.on_LoadProfile_clicked(None) + + def on_ResetNubs_clicked(self, widget, *data): + self.Notify("Resetting the nubs...") + os.system(RESET_CMD) + self.Notify("Nubs reset.") + + def on_ReadNubConfig_clicked(self, widget, *data): + self.model.load_profile(ReadProc()) self.Notify("Active nub configuration loaded.") - def on_ApplyChanges_clicked(self, widget, *data): - StoreConfigToProc(LEFT_NUB_CONFIG, self.leftnub.GetConfig()) - StoreConfigToProc(RIGHT_NUB_CONFIG, self.rightnub.GetConfig()) + def on_WriteNubConfig_clicked(self, widget, *data): + StoreProc(self.model.settings) self.Notify("Nub configuration updated.") def on_SaveProfile_clicked(self, widget, *data): - name = self.entry.get_text().replace(' ', '_') + name = self.entry.get_text() if name == '' or name == DEFAULT_PROFILENAME: self.Notify("Invalid profile name") else: - left = ' '.join(self.leftnub.GetConfig()) - right = ' '.join(self.rightnub.GetConfig()) - for profileid, row in enumerate(self.profiles): - if row[0] == name: - row[1] = left - row[2] = right - self.comboentry.set_active(profileid) - break - else: - self.profiles.append([name, left, right]) - self.entry.set_text(name) + self.model.set_profile(name, self.model.settings) self.Notify("Profile saved as: %s" % name) def on_LoadProfile_clicked(self, widget, *data): - profileid = self.comboentry.get_active() - if profileid == -1: + self._lose_active() # force widget to always emit changed signals, + name = self.entry.get_text() # ok since we always lookup by text value anyway + if not name: + return + elif name not in self.model.profiles and name != DEFAULT_PROFILENAME: self.Notify("Cannot load profile, please select an existing profile.") else: - name, left, right = self.profiles[profileid] - self.leftnub.SetConfig(left.split(' ')) - self.rightnub.SetConfig(right.split(' ')) - self.Notify("Profile loaded, hit 'Apply configuration' to make it active") + self.model.load_named_profile(name) + self.Notify("Profile loaded, hit 'Write nub settings' to make it active") + + def on_DeleteProfile_clicked(self, widget, *data): + name = self.entry.get_text() + if name not in self.model.profiles or name == DEFAULT_PROFILENAME: + self.model("Cannot remove profile, please select an existing non-default profile.") + else: + dialog = gtk.MessageDialog(flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_WARNING, + buttons=gtk.BUTTONS_YES_NO, + message_format="Are you sure you want to delete profile %s?" % name) + if dialog.run() == gtk.RESPONSE_YES: + self.model.delete_profile(name) + dialog.destroy() def on_window_destroy(self, widget, *data): self.Notify("Storing profiles...") - self.write_profiles_to_file() + self.model.store_profiles(PROFILES) gtk.main_quit() +# ================================================================ + if __name__ == '__main__': parser = optparse.OptionParser() - parser.add_option('--reset', default='', - help="Reset specified nub(s). Format: left,right") - parser.add_option('-l', '--left_nub', default='', - help="Configure left nub. Format: %s. E.g. mouse,150,20,20,7,7" % ' '.join(FILES).replace(' ', ',')) - parser.add_option('-r', '--right_nub', default='', - help="Configure right nub. Format: %s. E.g. mbuttons,150,20,20,7,7" % ' '.join(FILES).replace(' ', ',')) - parser.add_option('-s', '--save_profile', default='', - help="Store current configuration as specified profile (no spaces allowed)") - parser.add_option('-p', '--load_profile', default='', - help="Load specified nub configuration profile") + parser.add_option('--reset', default=False, action='store_true', help=HELP_RESET) + parser.add_option('-l', '--left_nub', default='', help=HELP_LEFT_NUB) + parser.add_option('-r', '--right_nub', default='', help=HELP_RIGHT_NUB) + parser.add_option('-s', '--save_profile', default='', help=HELP_SAVE) + parser.add_option('-a', '--apply', default=False, action='store_true', help=HELP_APPLY) + parser.add_option('-p', '--load_profile', default='', help=HELP_LOAD) + parser.add_option('-d', '--remove_profile', default='', help=HELP_DEL) options, args = parser.parse_args() - app = NubConfig() - if len(sys.argv) == 1: # run gui app + if len(sys.argv) == 1: # no params: run gui app + app = NubConfig() gtk.main() else: # run command line app - if 'left' in options.reset: - app.on_ResetLeft_clicked(None) - if 'right' in options.reset: - app.on_ResetRight_clicked(None) - ok, values = Validate(options.left_nub) - if ok: - StoreConfigToProc(LEFT_NUB_CONFIG, values) - ok, values = Validate(options.right_nub) - if ok: - StoreConfigToProc(RIGHT_NUB_CONFIG, values) - if options.save_profile: - app.on_UndoChanges_clicked(None) - app.entry.set_text(options.save_profile) - app.on_SaveProfile_clicked(None) - app.write_profiles_to_file() + model = NubModel() + + if options.reset: + os.system(RESET_CMD) + for key, value in Validate(options.left_nub).iteritems(): + model.settings[key+'0'] = value + for key, value in Validate(options.right_nub).iteritems(): + model.settings[key+'1'] = value + if options.save_profile and options.save_profile != DEFAULT_PROFILENAME: + model.set_profile(options.save_profile, model.settings) + if options.apply: + StoreProc(model.settings) if options.load_profile: - for profileid, row in enumerate(app.profiles): - if row[0] == options.load_profile: - app.comboentry.set_active(profileid) - app.on_LoadProfile_clicked(None) - app.on_ApplyChanges_clicked(None) + model.load_named_profile(options.load_profile) + StoreProc(model.settings) + if options.remove_profile: + model.delete_profile(options.remove_profile) + if options.save_profile or options.remove_profile: + model.store_profiles(PROFILES) diff --git a/recipes/pandora-system/pandora-scripts/pndlogo.png b/recipes/pandora-system/pandora-scripts/pndlogo.png index 4fb50c18958279b15585539943c90cf0889f1313..365573ef5a8b5491670d7adfc594bfba70970394 100755 GIT binary patch delta 1914 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 /sys/bus/i2c/drivers/vsense/3-0066/reset +sleep 1 +echo 0 > /sys/bus/i2c/drivers/vsense/3-0066/reset diff --git a/recipes/pandora-system/pandora-sudoers.bb b/recipes/pandora-system/pandora-sudoers.bb index bde1b56..304c328 100644 --- a/recipes/pandora-system/pandora-sudoers.bb +++ b/recipes/pandora-system/pandora-sudoers.bb @@ -5,7 +5,7 @@ COMPATIBLE_MACHINE = "omap3-pandora" RDEPENDS = "sudo" -PR = "r13" +PR = "r14" SRC_URI = " \ file://50_openpandora \ diff --git a/recipes/pandora-system/pandora-sudoers/50_openpandora b/recipes/pandora-system/pandora-sudoers/50_openpandora index 7b4324d..d7b5698 100755 --- a/recipes/pandora-system/pandora-sudoers/50_openpandora +++ b/recipes/pandora-system/pandora-sudoers/50_openpandora @@ -23,7 +23,7 @@ %wheel ALL=(ALL) NOPASSWD: /usr/pandora/scripts/op_cpusettings.sh %wheel ALL=(ALL) NOPASSWD: /usr/pandora/scripts/op_lcdrate.sh %wheel ALL=(ALL) NOPASSWD: /usr/pandora/scripts/op_videofir.sh -%wheel ALL=(ALL) NOPASSWD: /usr/pandora/scripts/op_nubmode.py +%wheel ALL=(ALL) NOPASSWD: /usr/pandora/scripts/reset_nubs.sh %wheel ALL=(ALL) NOPASSWD: /usr/pandora/scripts/op_storage.sh %wheel ALL=(ALL) NOPASSWD: /usr/pandora/scripts/op_tvout.sh %wheel ALL=(ALL) NOPASSWD: /usr/pandora/scripts/pnd_run.sh -- 2.39.2