vim line
[drlaunch] / src / portrait.py.orig
1 # -*- coding: utf-8 -*-
2 #
3 # gPodder - A media aggregator and podcast client
4 # Copyright (c) 2005-2010 Thomas Perl and the gPodder Team
5 #
6 # gPodder is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # gPodder is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 #
19
20 import dbus
21 import dbus.glib
22
23 import hildon
24 import osso
25
26 # Replace this with your own gettext() functionality
27 import gpodder
28 _ = gpodder.gettext
29
30
31 class FremantleRotation(object):
32     """thp's screen rotation for Maemo 5
33
34     Simply instantiate an object of this class and let it auto-rotate
35     your StackableWindows depending on the device orientation.
36
37     If you need to relayout a window, connect to its "configure-event"
38     signal and measure the ratio of width/height and relayout for that.
39
40     You can set the mode for rotation to AUTOMATIC (default), NEVER or
41     ALWAYS with the set_mode() method.
42     """
43     AUTOMATIC, NEVER, ALWAYS = range(3)
44
45     # Human-readable captions for the above constants
46     MODE_CAPTIONS = (_('Automatic'), _('Landscape'), _('Portrait'))
47
48     # Privately-used constants
49     _PORTRAIT, _LANDSCAPE = ('portrait', 'landscape')
50     _ENABLE_ACCEL = 'req_accelerometer_enable'
51     _DISABLE_ACCEL = 'req_accelerometer_disable'
52
53     # Defined in mce/dbus-names.h
54     _MCE_SERVICE = 'com.nokia.mce'
55     _MCE_REQUEST_PATH = '/com/nokia/mce/request'
56     _MCE_REQUEST_IF = 'com.nokia.mce.request'
57
58     # sysfs device name for the keyboard slider switch
59     KBD_SLIDER = '/sys/devices/platform/gpio-switch/slide/state'
60     _KBD_OPEN = 'open'
61     _KBD_CLOSED = 'closed'
62
63     def __init__(self, app_name, main_window=None, version='1.0', mode=0):
64         """Create a new rotation manager
65
66         app_name    ... The name of your application (for osso.Context)
67         main_window ... The root window (optional, hildon.StackableWindow)
68         version     ... The version of your application (optional, string)
69         mode        ... Initial mode for this manager (default: AUTOMATIC)
70         """
71         self._orientation = None
72         self._main_window = main_window
73         self._stack = hildon.WindowStack.get_default()
74         self._mode = -1
75         self._last_dbus_orientation = None
76         self._keyboard_state = self._get_keyboard_state()
77         app_id = '-'.join((app_name, self.__class__.__name__))
78         self._osso_context = osso.Context(app_id, version, False)
79         program = hildon.Program.get_instance()
80         program.connect('notify::is-topmost', self._on_topmost_changed)
81         system_bus = dbus.Bus.get_system()
82         system_bus.add_signal_receiver(self._on_orientation_signal, \
83                 signal_name='sig_device_orientation_ind', \
84                 dbus_interface='com.nokia.mce.signal', \
85                 path='/com/nokia/mce/signal')
86         system_bus.add_signal_receiver(self._on_keyboard_signal, \
87                 signal_name='Condition', \
88                 dbus_interface='org.freedesktop.Hal.Device', \
89                 path='/org/freedesktop/Hal/devices/platform_slide')
90         self.set_mode(mode)
91
92     def get_mode(self):
93         """Get the currently-set rotation mode
94
95         This will return one of three values: AUTOMATIC, ALWAYS or NEVER.
96         """
97         return self._mode
98
99     def set_mode(self, new_mode):
100         """Set the rotation mode
101
102         You can set the rotation mode to AUTOMATIC (use hardware rotation
103         info), ALWAYS (force portrait) and NEVER (force landscape).
104         """
105         if new_mode not in (self.AUTOMATIC, self.ALWAYS, self.NEVER):
106             raise ValueError('Unknown rotation mode')
107
108         if self._mode != new_mode:
109             if self._mode == self.AUTOMATIC:
110                 # Remember the current "automatic" orientation for later
111                 self._last_dbus_orientation = self._orientation
112                 # Tell MCE that we don't need the accelerometer anymore
113                 self._send_mce_request(self._DISABLE_ACCEL)
114
115             if new_mode == self.NEVER:
116                 self._orientation_changed(self._LANDSCAPE)
117             elif new_mode == self.ALWAYS and \
118                     self._keyboard_state != self._KBD_OPEN:
119                 self._orientation_changed(self._PORTRAIT)
120             elif new_mode == self.AUTOMATIC:
121                 # Restore the last-known "automatic" orientation
122                 self._orientation_changed(self._last_dbus_orientation)
123                 # Tell MCE that we need the accelerometer again
124                 self._send_mce_request(self._ENABLE_ACCEL)
125
126             self._mode = new_mode
127
128     def _send_mce_request(self, request):
129         rpc = osso.Rpc(self._osso_context)
130         rpc.rpc_run(self._MCE_SERVICE, \
131                     self._MCE_REQUEST_PATH, \
132                     self._MCE_REQUEST_IF, \
133                     request, \
134                     use_system_bus=True)
135
136     def _on_topmost_changed(self, program, property_spec):
137         # XXX: This seems to never get called on Fremantle(?)
138         if self._mode == self.AUTOMATIC:
139             if program.get_is_topmost():
140                 self._send_mce_request(self._ENABLE_ACCEL)
141             else:
142                 self._send_mce_request(self._DISABLE_ACCEL)
143
144     def _get_main_window(self):
145         if self._main_window:
146             # If we have gotten the main window as parameter, return it and
147             # don't try "harder" to find another window using the stack
148             return self._main_window
149         else:
150             # The main window is at the "bottom" of the window stack, and as
151             # the list we get with get_windows() is sorted "topmost first", we
152             # simply take the last item of the list to get our main window
153             windows = self._stack.get_windows()
154             if windows:
155                 return windows[-1]
156             else:
157                 return None
158
159     def _orientation_changed(self, orientation):
160         if self._orientation == orientation:
161             # Ignore repeated requests
162             return
163
164         flags = 0
165
166         if orientation != self._LANDSCAPE:
167             flags |= hildon.PORTRAIT_MODE_SUPPORT
168
169         if orientation == self._PORTRAIT:
170             flags |= hildon.PORTRAIT_MODE_REQUEST
171
172         window = self._get_main_window()
173         if window is not None:
174             hildon.hildon_gtk_window_set_portrait_flags(window, flags)
175
176         self._orientation = orientation
177
178     def _get_keyboard_state(self):
179         # For sbox, if the device does not exist assume that it's closed
180         try:
181             return open(self.KBD_SLIDER).read().strip()
182         except IOError:
183             return self._KBD_CLOSED
184
185     def _keyboard_state_changed(self):
186         state = self._get_keyboard_state()
187
188         if state == self._KBD_OPEN:
189             self._orientation_changed(self._LANDSCAPE)
190         elif state == self._KBD_CLOSED:
191             if self._mode == self.AUTOMATIC:
192                 self._orientation_changed(self._last_dbus_orientation)
193             elif self._mode == self.ALWAYS:
194                 self._orientation_changed(self._PORTRAIT)
195
196         self._keyboard_state = state
197
198     def _on_keyboard_signal(self, condition, button_name):
199         if condition == 'ButtonPressed' and button_name == 'cover':
200             self._keyboard_state_changed()
201
202     def _on_orientation_signal(self, orientation, stand, face, x, y, z):
203         if orientation in (self._PORTRAIT, self._LANDSCAPE):
204             if self._mode == self.AUTOMATIC and \
205                     self._keyboard_state != self._KBD_OPEN:
206                 # Automatically set the rotation based on hardware orientation
207                 self._orientation_changed(orientation)
208
209             # Save the current orientation for "automatic" mode later on
210             self._last_dbus_orientation = orientation
211