1 # -*- coding: utf-8 -*-
3 # gPodder - A media aggregator and podcast client
4 # Copyright (c) 2005-2010 Thomas Perl and the gPodder Team
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.
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.
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/>.
22 from dbus.mainloop.glib import DBusGMainLoop
27 # Replace this with your own gettext() functionality
31 class FremantleRotation(object):
32 """thp's screen rotation for Maemo 5
34 Simply instantiate an object of this class and let it auto-rotate
35 your StackableWindows depending on the device orientation.
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.
40 You can set the mode for rotation to AUTOMATIC (default), NEVER or
41 ALWAYS with the set_mode() method.
43 AUTOMATIC, NEVER, ALWAYS = range(3)
45 # Human-readable captions for the above constants
46 MODE_CAPTIONS = (_('Automatic'), _('Landscape'), _('Portrait'))
48 # Privately-used constants
49 _PORTRAIT, _LANDSCAPE = ('portrait', 'landscape')
50 _ENABLE_ACCEL = 'req_accelerometer_enable'
51 _DISABLE_ACCEL = 'req_accelerometer_disable'
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'
58 # sysfs device name for the keyboard slider switch
59 KBD_SLIDER = '/sys/devices/platform/gpio-switch/slide/state'
61 _KBD_CLOSED = 'closed'
63 def __init__(self, app_name, main_window=None, version='1.0', mode=0,
65 """Create a new rotation manager
67 app_name ... The name of your application (for osso.Context)
68 main_window ... The root window (optional, hildon.StackableWindow)
69 version ... The version of your application (optional, string)
70 mode ... Initial mode for this manager (default: AUTOMATIC)
71 dontrotate ... Don't rotate the window. (def: False)
73 self.dontrotate = dontrotate # V13
74 self._orientation = None
75 self._main_window = main_window
76 self._stack = hildon.WindowStack.get_default()
78 self._last_dbus_orientation = None
79 self._keyboard_state = self._get_keyboard_state()
80 app_id = '-'.join((app_name, self.__class__.__name__))
81 self._osso_context = osso.Context(app_id, version, False)
82 program = hildon.Program.get_instance()
83 program.connect('notify::is-topmost', self._on_topmost_changed)
86 # https://garage.maemo.org/pipermail/pymaemo-developers/2010-April/001445.html
87 # https://garage.maemo.org/pipermail/pymaemo-developers/2010-April/001454.html
88 # https://garage.maemo.org/pipermail/pymaemo-developers/2010-April/thread.html
89 # https://bugs.maemo.org/show_bug.cgi?id=8611
91 # If we use dbus.Bus.get_system() or dbus.SystemBus() then the
92 # program fails whenever bluezwitch is installed. This could
93 # also happen whenever another widget is using System Bus (sure?).
95 #V13 system_bus = dbus.Bus.get_system()
96 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
97 busaddress='unix:path=/var/run/dbus/system_bus_socket'
98 system_bus=dbus.bus.BusConnection(busaddress)
99 self.system_bus=system_bus
101 system_bus.add_signal_receiver(self._on_orientation_signal, \
102 signal_name='sig_device_orientation_ind', \
103 dbus_interface='com.nokia.mce.signal', \
104 path='/com/nokia/mce/signal')
105 system_bus.add_signal_receiver(self._on_keyboard_signal, \
106 signal_name='Condition', \
107 dbus_interface='org.freedesktop.Hal.Device', \
108 path='/org/freedesktop/Hal/devices/platform_slide')
112 """Get the currently-set rotation mode
114 This will return one of three values: AUTOMATIC, ALWAYS or NEVER.
118 def set_mode(self, new_mode):
119 """Set the rotation mode
121 You can set the rotation mode to AUTOMATIC (use hardware rotation
122 info), ALWAYS (force portrait) and NEVER (force landscape).
124 if new_mode not in (self.AUTOMATIC, self.ALWAYS, self.NEVER):
125 raise ValueError('Unknown rotation mode')
127 if self._mode != new_mode:
128 if self._mode == self.AUTOMATIC:
129 # Remember the current "automatic" orientation for later
130 self._last_dbus_orientation = self._orientation
131 # Tell MCE that we don't need the accelerometer anymore
132 self._send_mce_request(self._DISABLE_ACCEL)
134 if new_mode == self.NEVER:
135 self._orientation_changed(self._LANDSCAPE)
136 elif new_mode == self.ALWAYS and \
137 self._keyboard_state != self._KBD_OPEN:
138 self._orientation_changed(self._PORTRAIT)
139 elif new_mode == self.AUTOMATIC:
140 # Restore the last-known "automatic" orientation
141 self._orientation_changed(self._last_dbus_orientation)
142 # Tell MCE that we need the accelerometer again
143 self._send_mce_request(self._ENABLE_ACCEL)
145 self._mode = new_mode
147 def reset_mode(self):
148 if self._mode==self.AUTOMATIC:
149 self._send_mce_request(self._ENABLE_ACCEL)
151 self._send_mce_request(self._DISABLE_ACCEL)
153 def _send_mce_request(self, request):
154 rpc = osso.Rpc(self._osso_context)
155 rpc.rpc_run(self._MCE_SERVICE, \
156 self._MCE_REQUEST_PATH, \
157 self._MCE_REQUEST_IF, \
161 def _on_topmost_changed(self, program, property_spec):
162 # XXX: This seems to never get called on Fremantle(?)
163 if self._mode == self.AUTOMATIC:
164 if program.get_is_topmost():
165 self._send_mce_request(self._ENABLE_ACCEL)
167 self._send_mce_request(self._DISABLE_ACCEL)
169 def _get_main_window(self):
170 if self._main_window:
171 # If we have gotten the main window as parameter, return it and
172 # don't try "harder" to find another window using the stack
173 return self._main_window
175 # The main window is at the "bottom" of the window stack, and as
176 # the list we get with get_windows() is sorted "topmost first", we
177 # simply take the last item of the list to get our main window
178 windows = self._stack.get_windows()
184 def _orientation_changed(self, orientation):
185 if self._orientation == orientation:
186 # Ignore repeated requests
191 if orientation != self._LANDSCAPE:
192 flags |= hildon.PORTRAIT_MODE_SUPPORT
194 if orientation == self._PORTRAIT:
195 flags |= hildon.PORTRAIT_MODE_REQUEST
197 window = self._get_main_window()
198 if window is not None and self.dontrotate==False:
199 hildon.hildon_gtk_window_set_portrait_flags(window, flags)
201 self._orientation = orientation
203 self.on_orientation_changed(orientation)
205 def on_orientation_changed(self, orientation):
208 def _get_keyboard_state(self):
209 # For sbox, if the device does not exist assume that it's closed
211 return open(self.KBD_SLIDER).read().strip()
213 return self._KBD_CLOSED
215 def _keyboard_state_changed(self):
216 state = self._get_keyboard_state()
218 if state == self._KBD_OPEN:
219 self._orientation_changed(self._LANDSCAPE)
220 elif state == self._KBD_CLOSED:
221 if self._mode == self.AUTOMATIC:
222 self._orientation_changed(self._last_dbus_orientation)
223 elif self._mode == self.ALWAYS:
224 self._orientation_changed(self._PORTRAIT)
226 self._keyboard_state = state
228 def _on_keyboard_signal(self, condition, button_name):
229 if condition == 'ButtonPressed' and button_name == 'cover':
230 self._keyboard_state_changed()
232 def _on_orientation_signal(self, orientation, stand, face, x, y, z):
233 if orientation in (self._PORTRAIT, self._LANDSCAPE):
234 if self._mode == self.AUTOMATIC and \
235 self._keyboard_state != self._KBD_OPEN:
236 # Automatically set the rotation based on hardware orientation
237 self._orientation_changed(orientation)
239 # Save the current orientation for "automatic" mode later on
240 self._last_dbus_orientation = orientation