Bug fix (thanks to qwerty12 @ talk.maemo.org)
[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 #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
44     AUTOMATIC, NEVER, ALWAYS = range(3)
45
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._mode = -1
74         self._last_dbus_orientation = None
75         self._keyboard_state = self._get_keyboard_state()
76         app_id = '-'.join((app_name, self.__class__.__name__))
77         self._osso_context = osso.Context(app_id, version, False)
78         program = hildon.Program.get_instance()
79         program.connect('notify::is-topmost', self._on_topmost_changed)
80         system_bus = dbus.Bus.get_system()
81         system_bus.add_signal_receiver(self._on_orientation_signal, \
82                 signal_name='sig_device_orientation_ind', \
83                 dbus_interface='com.nokia.mce.signal', \
84                 path='/com/nokia/mce/signal')
85         system_bus.add_signal_receiver(self._on_keyboard_signal, \
86                 signal_name='Condition', \
87                 dbus_interface='org.freedesktop.Hal.Device', \
88                 path='/org/freedesktop/Hal/devices/platform_slide')
89         self.set_mode(mode)
90
91         self._send_mce_request(self._ENABLE_ACCEL)
92
93     def set_mode(self, new_mode):
94         self._mode=new_mode
95
96     def get_mode(self):
97         """Get the currently-set rotation mode
98
99         This will return one of three values: AUTOMATIC, ALWAYS or NEVER.
100         """
101         return self._mode
102
103     def _send_mce_request(self, request):
104         rpc = osso.Rpc(self._osso_context)
105         rpc.rpc_run(self._MCE_SERVICE, \
106                     self._MCE_REQUEST_PATH, \
107                     self._MCE_REQUEST_IF, \
108                     request, \
109                     use_system_bus=True)
110
111     def _on_topmost_changed(self, program, property_spec):
112         # XXX: This seems to never get called on Fremantle(?)
113         if self._mode == self.AUTOMATIC:
114             if program.get_is_topmost():
115                 self._send_mce_request(self._ENABLE_ACCEL)
116             else:
117                 self._send_mce_request(self._DISABLE_ACCEL)
118
119     def NO_get_main_window(self):
120         if self._main_window:
121             # If we have gotten the main window as parameter, return it and
122             # don't try "harder" to find another window using the stack
123             return self._main_window
124         else:
125             # The main window is at the "bottom" of the window stack, and as
126             # the list we get with get_windows() is sorted "topmost first", we
127             # simply take the last item of the list to get our main window
128             windows = self._stack.get_windows()
129             if windows:
130                 return windows[-1]
131             else:
132                 return None
133
134     def _orientation_changed(self, orientation):
135         if self._orientation == orientation:
136             # Ignore repeated requests
137             return
138
139         if orientation == None:
140             return
141
142         flags = 0
143
144         if orientation != self._LANDSCAPE:
145             flags |= hildon.PORTRAIT_MODE_SUPPORT
146
147         if orientation == self._PORTRAIT:
148             flags |= hildon.PORTRAIT_MODE_REQUEST
149
150 #        window = self._get_main_window()
151 #        if window is not None:
152 #            hildon.hildon_gtk_window_set_portrait_flags(window, flags)
153
154         self._orientation = orientation
155
156         self.on_orientation_changed(orientation)
157
158     def _get_keyboard_state(self):
159         # For sbox, if the device does not exist assume that it's closed
160         try:
161             return open(self.KBD_SLIDER).read().strip()
162         except IOError:
163             return self._KBD_CLOSED
164
165     def _keyboard_state_changed(self):
166         state = self._get_keyboard_state()
167
168         if state == self._KBD_OPEN:
169             self._orientation_changed(self._LANDSCAPE)
170         elif state == self._KBD_CLOSED:
171             if self._mode == self.AUTOMATIC:
172                 self._orientation_changed(self._last_dbus_orientation)
173             elif self._mode == self.ALWAYS:
174                 self._orientation_changed(self._PORTRAIT)
175
176         self._keyboard_state = state
177
178     def _on_keyboard_signal(self, condition, button_name):
179         if condition == 'ButtonPressed' and button_name == 'cover':
180             self._keyboard_state_changed()
181
182     def _on_orientation_signal(self, orientation, stand, face, x, y, z):
183         if orientation in (self._PORTRAIT, self._LANDSCAPE):
184             if self._mode == self.AUTOMATIC and \
185                     self._keyboard_state != self._KBD_OPEN:
186                 # Automatically set the rotation based on hardware orientation
187                 self._orientation_changed(orientation)
188
189             # Save the current orientation for "automatic" mode later on
190             self._last_dbus_orientation = orientation
191