ea58c4b88db60e45f68a77a6e3a60bfdd0e80459
[drlaunch] / src / portrait.py
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 _ = str
28
29
30 class FremantleRotation(object):
31     """thp's screen rotation for Maemo 5
32
33     Simply instantiate an object of this class and let it auto-rotate
34     your StackableWindows depending on the device orientation.
35
36     If you need to relayout a window, connect to its "configure-event"
37     signal and measure the ratio of width/height and relayout for that.
38
39     You can set the mode for rotation to AUTOMATIC (default), NEVER or
40     ALWAYS with the set_mode() method.
41     """
42     AUTOMATIC, NEVER, ALWAYS = range(3)
43
44     # Human-readable captions for the above constants
45     MODE_CAPTIONS = (_('Automatic'), _('Landscape'), _('Portrait'))
46
47     # Privately-used constants
48     _PORTRAIT, _LANDSCAPE = ('portrait', 'landscape')
49     _ENABLE_ACCEL = 'req_accelerometer_enable'
50     _DISABLE_ACCEL = 'req_accelerometer_disable'
51
52     # Defined in mce/dbus-names.h
53     _MCE_SERVICE = 'com.nokia.mce'
54     _MCE_REQUEST_PATH = '/com/nokia/mce/request'
55     _MCE_REQUEST_IF = 'com.nokia.mce.request'
56
57     # sysfs device name for the keyboard slider switch
58     KBD_SLIDER = '/sys/devices/platform/gpio-switch/slide/state'
59     _KBD_OPEN = 'open'
60     _KBD_CLOSED = 'closed'
61
62     def __init__(self, app_name, main_window=None, version='1.0', mode=0,
63         dontrotate=False):
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         dontrotate  ... Don't rotate the window. (def: False)
71         """
72         self.dontrotate = dontrotate # V13
73         self._orientation = None
74         self._main_window = main_window
75         self._stack = hildon.WindowStack.get_default()
76         self._mode = -1
77         self._last_dbus_orientation = None
78         self._keyboard_state = self._get_keyboard_state()
79         app_id = '-'.join((app_name, self.__class__.__name__))
80         self._osso_context = osso.Context(app_id, version, False)
81         program = hildon.Program.get_instance()
82         program.connect('notify::is-topmost', self._on_topmost_changed)
83         system_bus = dbus.Bus.get_system()
84         system_bus.add_signal_receiver(self._on_orientation_signal, \
85                 signal_name='sig_device_orientation_ind', \
86                 dbus_interface='com.nokia.mce.signal', \
87                 path='/com/nokia/mce/signal')
88         system_bus.add_signal_receiver(self._on_keyboard_signal, \
89                 signal_name='Condition', \
90                 dbus_interface='org.freedesktop.Hal.Device', \
91                 path='/org/freedesktop/Hal/devices/platform_slide')
92         self.set_mode(mode)
93
94     def get_mode(self):
95         """Get the currently-set rotation mode
96
97         This will return one of three values: AUTOMATIC, ALWAYS or NEVER.
98         """
99         return self._mode
100
101     def set_mode(self, new_mode):
102         """Set the rotation mode
103
104         You can set the rotation mode to AUTOMATIC (use hardware rotation
105         info), ALWAYS (force portrait) and NEVER (force landscape).
106         """
107         if new_mode not in (self.AUTOMATIC, self.ALWAYS, self.NEVER):
108             raise ValueError('Unknown rotation mode')
109
110         if self._mode != new_mode:
111             if self._mode == self.AUTOMATIC:
112                 # Remember the current "automatic" orientation for later
113                 self._last_dbus_orientation = self._orientation
114                 # Tell MCE that we don't need the accelerometer anymore
115                 self._send_mce_request(self._DISABLE_ACCEL)
116
117             if new_mode == self.NEVER:
118                 self._orientation_changed(self._LANDSCAPE)
119             elif new_mode == self.ALWAYS and \
120                     self._keyboard_state != self._KBD_OPEN:
121                 self._orientation_changed(self._PORTRAIT)
122             elif new_mode == self.AUTOMATIC:
123                 # Restore the last-known "automatic" orientation
124                 self._orientation_changed(self._last_dbus_orientation)
125                 # Tell MCE that we need the accelerometer again
126                 self._send_mce_request(self._ENABLE_ACCEL)
127
128             self._mode = new_mode
129
130     def _send_mce_request(self, request):
131         rpc = osso.Rpc(self._osso_context)
132         rpc.rpc_run(self._MCE_SERVICE, \
133                     self._MCE_REQUEST_PATH, \
134                     self._MCE_REQUEST_IF, \
135                     request, \
136                     use_system_bus=True)
137
138     def _on_topmost_changed(self, program, property_spec):
139         # XXX: This seems to never get called on Fremantle(?)
140         if self._mode == self.AUTOMATIC:
141             if program.get_is_topmost():
142                 self._send_mce_request(self._ENABLE_ACCEL)
143             else:
144                 self._send_mce_request(self._DISABLE_ACCEL)
145
146     def _get_main_window(self):
147         if self._main_window:
148             # If we have gotten the main window as parameter, return it and
149             # don't try "harder" to find another window using the stack
150             return self._main_window
151         else:
152             # The main window is at the "bottom" of the window stack, and as
153             # the list we get with get_windows() is sorted "topmost first", we
154             # simply take the last item of the list to get our main window
155             windows = self._stack.get_windows()
156             if windows:
157                 return windows[-1]
158             else:
159                 return None
160
161     def _orientation_changed(self, orientation):
162         if self._orientation == orientation:
163             # Ignore repeated requests
164             return
165
166         flags = 0
167
168         if orientation != self._LANDSCAPE:
169             flags |= hildon.PORTRAIT_MODE_SUPPORT
170
171         if orientation == self._PORTRAIT:
172             flags |= hildon.PORTRAIT_MODE_REQUEST
173
174         window = self._get_main_window()
175         if window is not None and self.dontrotate==False:
176             hildon.hildon_gtk_window_set_portrait_flags(window, flags)
177
178         self._orientation = orientation
179
180         self.on_orientation_changed(orientation)
181
182     def on_orientation_changed(self, orientation):
183         pass
184
185     def _get_keyboard_state(self):
186         # For sbox, if the device does not exist assume that it's closed
187         try:
188             return open(self.KBD_SLIDER).read().strip()
189         except IOError:
190             return self._KBD_CLOSED
191
192     def _keyboard_state_changed(self):
193         state = self._get_keyboard_state()
194
195         if state == self._KBD_OPEN:
196             self._orientation_changed(self._LANDSCAPE)
197         elif state == self._KBD_CLOSED:
198             if self._mode == self.AUTOMATIC:
199                 self._orientation_changed(self._last_dbus_orientation)
200             elif self._mode == self.ALWAYS:
201                 self._orientation_changed(self._PORTRAIT)
202
203         self._keyboard_state = state
204
205     def _on_keyboard_signal(self, condition, button_name):
206         if condition == 'ButtonPressed' and button_name == 'cover':
207             self._keyboard_state_changed()
208
209     def _on_orientation_signal(self, orientation, stand, face, x, y, z):
210         if orientation in (self._PORTRAIT, self._LANDSCAPE):
211             if self._mode == self.AUTOMATIC and \
212                     self._keyboard_state != self._KBD_OPEN:
213                 # Automatically set the rotation based on hardware orientation
214                 self._orientation_changed(orientation)
215
216             # Save the current orientation for "automatic" mode later on
217             self._last_dbus_orientation = orientation
218