pandora-scripts: Added new version of op_nubmode and added that to sudoers
authorMichael Mrozek <EvilDragon@openpandora.de>
Sat, 11 Jun 2011 07:17:00 +0000 (09:17 +0200)
committerMichael Mrozek <EvilDragon@openpandora.de>
Sat, 11 Jun 2011 07:17:00 +0000 (09:17 +0200)
recipes/pandora-system/pandora-scripts.bb
recipes/pandora-system/pandora-scripts/nubmode.glade
recipes/pandora-system/pandora-scripts/op_nubmode.py
recipes/pandora-system/pandora-scripts/pndlogo.png
recipes/pandora-system/pandora-scripts/reset_nubs.sh [new file with mode: 0755]
recipes/pandora-system/pandora-sudoers.bb
recipes/pandora-system/pandora-sudoers/50_openpandora

index b16b4b1..a80a15d 100644 (file)
@@ -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/ 
 
 
 
index a77be2b..8bbd937 100755 (executable)
@@ -5,6 +5,7 @@
   <object class="GtkWindow" id="window">
     <property name="title" translatable="yes">Nub configuration</property>
     <property name="window_position">center</property>
+    <signal name="destroy" handler="on_window_destroy"/>
     <child>
       <object class="GtkTable" id="table">
         <property name="visible">True</property>
@@ -24,7 +25,7 @@
                   <object class="GtkVBox" id="vbox1">
                     <property name="visible">True</property>
                     <child>
-                      <object class="GtkRadioButton" id="LeftRadio_mouse">
+                      <object class="GtkRadioButton" id="Rmouse0">
                         <property name="label" translatable="yes">mouse movement</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkRadioButton" id="LeftRadio_mbuttons">
+                      <object class="GtkRadioButton" id="Rmbuttons0">
                         <property name="label" translatable="yes">mouse buttons</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
-                        <property name="group">LeftRadio_mouse</property>
+                        <property name="group">Rmouse0</property>
                       </object>
                       <packing>
                         <property name="position">1</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkRadioButton" id="LeftRadio_scroll">
+                      <object class="GtkRadioButton" id="Rscroll0">
                         <property name="label" translatable="yes">scrolling</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
-                        <property name="group">LeftRadio_mouse</property>
+                        <property name="group">Rmouse0</property>
                       </object>
                       <packing>
                         <property name="position">2</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkRadioButton" id="LeftRadio_absolute">
+                      <object class="GtkRadioButton" id="Rabsolute0">
                         <property name="label" translatable="yes">joystick</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
-                        <property name="group">LeftRadio_mouse</property>
+                        <property name="group">Rmouse0</property>
                       </object>
                       <packing>
                         <property name="position">3</property>
                   <object class="GtkVBox" id="vbox2">
                     <property name="visible">True</property>
                     <child>
-                      <object class="GtkRadioButton" id="RightRadio_mouse">
+                      <object class="GtkRadioButton" id="Rmouse1">
                         <property name="label" translatable="yes">mouse movement</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkRadioButton" id="RightRadio_mbuttons">
+                      <object class="GtkRadioButton" id="Rmbuttons1">
                         <property name="label" translatable="yes">mouse buttons</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
-                        <property name="group">RightRadio_mouse</property>
+                        <property name="group">Rmouse1</property>
                       </object>
                       <packing>
                         <property name="position">1</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkRadioButton" id="RightRadio_scroll">
+                      <object class="GtkRadioButton" id="Rscroll1">
                         <property name="label" translatable="yes">scrolling</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
-                        <property name="group">RightRadio_mouse</property>
+                        <property name="group">Rmouse1</property>
                       </object>
                       <packing>
                         <property name="position">2</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkRadioButton" id="RightRadio_absolute">
+                      <object class="GtkRadioButton" id="Rabsolute1">
                         <property name="label" translatable="yes">joystick</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">False</property>
                         <property name="active">True</property>
                         <property name="draw_indicator">True</property>
-                        <property name="group">RightRadio_mouse</property>
+                        <property name="group">Rmouse1</property>
                       </object>
                       <packing>
                         <property name="position">3</property>
           <object class="GtkHScale" id="LeftButtonThreshold">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="adjustment">lbutton</property>
+            <property name="adjustment">button0</property>
             <property name="digits">0</property>
           </object>
           <packing>
           <object class="GtkHScale" id="LeftMouseSpeed">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="adjustment">lmouse</property>
+            <property name="adjustment">mouse0</property>
             <property name="digits">0</property>
           </object>
           <packing>
           <object class="GtkHScale" id="LeftScrollRate">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="adjustment">lscroll</property>
+            <property name="adjustment">rate0</property>
             <property name="digits">0</property>
           </object>
           <packing>
           <object class="GtkHScale" id="LeftScrollX">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="adjustment">lscrollx</property>
+            <property name="adjustment">scrollx0</property>
             <property name="digits">0</property>
           </object>
           <packing>
           <object class="GtkHScale" id="LeftScrollY">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="adjustment">lscrolly</property>
+            <property name="adjustment">scrolly0</property>
             <property name="digits">0</property>
           </object>
           <packing>
           <object class="GtkHScale" id="RightMouseSpeed">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="adjustment">rmouse</property>
+            <property name="adjustment">mouse1</property>
             <property name="digits">0</property>
           </object>
           <packing>
           <object class="GtkHScale" id="RightButtonThreshold">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="adjustment">rbutton</property>
+            <property name="adjustment">button1</property>
             <property name="digits">0</property>
           </object>
           <packing>
           <object class="GtkHScale" id="RightScrollRate">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="adjustment">rscroll</property>
+            <property name="adjustment">rate1</property>
             <property name="digits">0</property>
           </object>
           <packing>
           <object class="GtkHScale" id="RightScrollX">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="adjustment">rscrollx</property>
+            <property name="adjustment">scrollx1</property>
             <property name="digits">0</property>
           </object>
           <packing>
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="update_policy">delayed</property>
-            <property name="adjustment">rscrolly</property>
+            <property name="adjustment">scrolly1</property>
             <property name="digits">0</property>
           </object>
           <packing>
           </packing>
         </child>
         <child>
-          <object class="GtkButton" id="ResetLeft">
-            <property name="label" translatable="yes">Reset left nub</property>
+          <object class="GtkComboBoxEntry" id="ProfileComboEntry">
             <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <signal name="clicked" handler="on_ResetLeft_clicked"/>
           </object>
           <packing>
-            <property name="top_attach">8</property>
-            <property name="bottom_attach">9</property>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="top_attach">9</property>
+            <property name="bottom_attach">10</property>
             <property name="y_padding">2</property>
           </packing>
         </child>
         <child>
-          <object class="GtkButton" id="ResetRight">
-            <property name="label" translatable="yes">Reset right nub</property>
+          <object class="GtkStatusbar" id="statusbar">
             <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <signal name="clicked" handler="on_ResetRight_clicked"/>
+            <property name="spacing">2</property>
           </object>
           <packing>
-            <property name="left_attach">2</property>
             <property name="right_attach">3</property>
-            <property name="top_attach">8</property>
-            <property name="bottom_attach">9</property>
-            <property name="y_padding">2</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkButton" id="UndoChanges">
-            <property name="label" translatable="yes">Scan nub state</property>
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <property name="image_position">top</property>
-            <signal name="clicked" handler="on_UndoChanges_clicked"/>
-          </object>
-          <packing>
-            <property name="left_attach">1</property>
-            <property name="right_attach">2</property>
-            <property name="top_attach">8</property>
-            <property name="bottom_attach">9</property>
-            <property name="y_padding">2</property>
+            <property name="top_attach">10</property>
+            <property name="bottom_attach">11</property>
           </packing>
         </child>
         <child>
-          <object class="GtkVBox" id="vbox3">
+          <object class="GtkImage" id="LogoImage">
+            <property name="height_request">84</property>
             <property name="visible">True</property>
-            <child>
-              <object class="GtkImage" id="LogoImage">
-                <property name="height_request">84</property>
-                <property name="visible">True</property>
-                <property name="pixbuf">pndlogo.png</property>
-              </object>
-              <packing>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkButton" id="ApplyChanges">
-                <property name="label" translatable="yes">Apply configuration</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <signal name="clicked" handler="on_ApplyChanges_clicked"/>
-              </object>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
+            <property name="pixbuf">pndlogo.png</property>
           </object>
           <packing>
             <property name="left_attach">1</property>
             <signal name="clicked" handler="on_SaveProfile_clicked"/>
           </object>
           <packing>
+            <property name="left_attach">2</property>
+            <property name="right_attach">3</property>
             <property name="top_attach">9</property>
             <property name="bottom_attach">10</property>
             <property name="y_padding">2</property>
           </packing>
         </child>
         <child>
-          <object class="GtkComboBoxEntry" id="ProfileComboEntry">
+          <object class="GtkButton" id="DeleteProfile">
+            <property name="label" translatable="yes">Delete profile</property>
             <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <signal name="clicked" handler="on_DeleteProfile_clicked"/>
           </object>
           <packing>
-            <property name="left_attach">1</property>
-            <property name="right_attach">2</property>
             <property name="top_attach">9</property>
             <property name="bottom_attach">10</property>
             <property name="y_padding">2</property>
           </packing>
         </child>
         <child>
-          <object class="GtkButton" id="LoadProfile">
-            <property name="label" translatable="yes">Load profile</property>
+          <object class="GtkButton" id="WriteNubConfig">
+            <property name="label" translatable="yes">Write nub settings</property>
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="receives_default">True</property>
-            <signal name="clicked" handler="on_LoadProfile_clicked"/>
+            <signal name="clicked" handler="on_WriteNubConfig_clicked"/>
           </object>
           <packing>
             <property name="left_attach">2</property>
             <property name="right_attach">3</property>
-            <property name="top_attach">9</property>
-            <property name="bottom_attach">10</property>
+            <property name="top_attach">8</property>
+            <property name="bottom_attach">9</property>
             <property name="y_padding">2</property>
           </packing>
         </child>
         <child>
-          <object class="GtkStatusbar" id="statusbar">
+          <object class="GtkButton" id="ReadNubConfig">
+            <property name="label" translatable="yes">Read nub settings</property>
             <property name="visible">True</property>
-            <property name="spacing">2</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="image_position">top</property>
+            <signal name="clicked" handler="on_ReadNubConfig_clicked"/>
           </object>
           <packing>
-            <property name="right_attach">3</property>
-            <property name="top_attach">10</property>
-            <property name="bottom_attach">11</property>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="top_attach">8</property>
+            <property name="bottom_attach">9</property>
+            <property name="y_padding">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="ResetNubs">
+            <property name="label" translatable="yes">Reset nubs...</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <signal name="clicked" handler="on_ResetNubs_clicked"/>
+          </object>
+          <packing>
+            <property name="top_attach">8</property>
+            <property name="bottom_attach">9</property>
+            <property name="y_padding">2</property>
           </packing>
         </child>
       </object>
     </child>
   </object>
-  <object class="GtkAdjustment" id="lmouse">
+  <object class="GtkAdjustment" id="mouse0">
     <property name="value">150</property>
     <property name="lower">50</property>
     <property name="upper">300</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="rmouse">
+  <object class="GtkAdjustment" id="mouse1">
     <property name="value">150</property>
     <property name="lower">50</property>
     <property name="upper">300</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="lbutton">
+  <object class="GtkAdjustment" id="button0">
     <property name="value">20</property>
     <property name="lower">1</property>
     <property name="upper">40</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="rbutton">
+  <object class="GtkAdjustment" id="button1">
     <property name="value">20</property>
     <property name="lower">1</property>
     <property name="upper">40</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="lscroll">
+  <object class="GtkAdjustment" id="rate0">
     <property name="value">20</property>
     <property name="lower">1</property>
     <property name="upper">40</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="rscroll">
+  <object class="GtkAdjustment" id="rate1">
     <property name="value">20</property>
     <property name="lower">1</property>
     <property name="upper">40</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="lscrollx">
+  <object class="GtkAdjustment" id="scrollx0">
     <property name="value">7</property>
     <property name="lower">-32</property>
     <property name="upper">32</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="rscrollx">
+  <object class="GtkAdjustment" id="scrollx1">
     <property name="value">7</property>
     <property name="lower">-32</property>
     <property name="upper">32</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="lscrolly">
+  <object class="GtkAdjustment" id="scrolly0">
     <property name="value">7</property>
     <property name="lower">-32</property>
     <property name="upper">32</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
-  <object class="GtkAdjustment" id="rscrolly">
+  <object class="GtkAdjustment" id="scrolly1">
     <property name="value">7</property>
     <property name="lower">-32</property>
     <property name="upper">32</property>
index f20dfda..7b858ff 100755 (executable)
@@ -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<x> (<x>: 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('<Control>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)
index 4fb50c1..365573e 100755 (executable)
Binary files a/recipes/pandora-system/pandora-scripts/pndlogo.png and b/recipes/pandora-system/pandora-scripts/pndlogo.png differ
diff --git a/recipes/pandora-system/pandora-scripts/reset_nubs.sh b/recipes/pandora-system/pandora-scripts/reset_nubs.sh
new file mode 100755 (executable)
index 0000000..e30ae83
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/bash
+echo 1 > /sys/bus/i2c/drivers/vsense/3-0066/reset
+sleep 1
+echo 0 > /sys/bus/i2c/drivers/vsense/3-0066/reset
index bde1b56..304c328 100644 (file)
@@ -5,7 +5,7 @@ COMPATIBLE_MACHINE = "omap3-pandora"
 
 RDEPENDS = "sudo"
 
-PR = "r13"
+PR = "r14"
 
 SRC_URI = " \
           file://50_openpandora \
index 7b4324d..d7b5698 100755 (executable)
@@ -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