You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
4.7 KiB
Python

# Copyright (C) 2014, Sugarlabs
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
"""
The power module provides an interface to *powerd*, a daemon that
manages the aggressive suspend and wakeup policies for early OLPC
laptops.
The module does nothing if *powerd* is not present. *powerd* is not
required on laptops other than OLPC XO-1, XO-1.5, XO-1.75 and XO-4.
Distributions of Sugar on other hardware need not include the *powerd*
package.
"""
import os
import logging
_POWERD_INHIBIT_DIR = '/var/run/powerd-inhibit-suspend'
_power_manager = None
def get_power_manager():
"""
Get the power manager instance. Only one instance exists, and
will always be returned.
Returns:
an instance of :class:`sugar3.power.PowerManager`.
"""
global _power_manager
if _power_manager is None:
_power_manager = PowerManager()
return _power_manager
class PowerManager():
"""
Control of automatic idle suspend, with reference counting.
:class:`sugar3.activity.activity.Activity` calls
:py:meth:`inhibit_suspend` before speaking text, or when an
activity collaboration begins.
Activities may call :py:meth:`inhibit_suspend` before playing
music, video, speaking large amounts of text, collaborating, or
waiting for response to network operations.
As an example, the Clock activity inhibits automatic idle suspend
while it is active, so that the displayed clock-face continues to
change. Otherwise it would freeze.
:class:`sugar3.activity.activity.Activity` calls
:py:meth:`shutdown` as an activity terminates, in case the
activity has failed to call :py:meth:`restore_suspend`.
While automatic idle suspend is inhibited, *powerd* will
continue to dim and blank the display.
Both the :py:meth:`inhibit_suspend` and :py:meth:`restore_suspend`
methods are reference counted; automatic idle suspend is not
restored until the same number of calls to restore are made.
*powerd* is resilient against failure to restore automatic idle
suspend; it verifies an inhibit request and deletes it if the
requesting process has terminated.
"""
def __init__(self):
self._suspend_inhibit_counter = 0
if os.path.exists(_POWERD_INHIBIT_DIR):
self._path = os.path.join(_POWERD_INHIBIT_DIR, str(os.getpid()))
else:
self._path = None
def __del__(self):
self._remove_flag_file()
def suspend_breaks_collaboration(self):
"""
Does automatic idle suspend break collaboration with this
toolkit? Yes. For future use by a toolkit with more
resilient collaboration.
Returns:
True
"""
return True
def inhibit_suspend(self):
"""
Inhibit automatic idle suspend until restored.
"""
if self._path and self._suspend_inhibit_counter == 0:
try:
with open(self._path, 'w') as flag_file:
flag_file.write('')
except IOError:
logging.error("Inhibit Suspend: Could not create file %s",
self._path)
self._suspend_inhibit_counter += 1
def restore_suspend(self):
"""
Possibly restore automatic idle suspend.
"""
self._suspend_inhibit_counter -= 1
if self._suspend_inhibit_counter > 0:
return
self._remove_flag_file()
def is_suspend_inhibited(self):
"""
Check if automatic idle suspend is inhibited.
Returns:
inhibited (bool): whether automatic idle suspend is inhibited.
"""
return self._suspend_inhibit_counter > 0
def shutdown(self):
"""
Shutdown the power manager.
Restores automatic idle suspend regardless of reference counting.
"""
self._remove_flag_file()
def _remove_flag_file(self):
if self._path:
try:
os.unlink(self._path)
except OSError:
pass
self._suspend_inhibit_counter = 0