Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar
Conflicts: NEWS
This commit is contained in:
commit
4391d4777a
7
NEWS
7
NEWS
@ -1,5 +1,12 @@
|
|||||||
* #4503: Do some standard Tubes boilerplate in sugar.presence, so activities
|
* #4503: Do some standard Tubes boilerplate in sugar.presence, so activities
|
||||||
don't have to (smcv)
|
don't have to (smcv)
|
||||||
|
* #4428 Revert to the trial-3 frame behavior (marco)
|
||||||
|
* Initial push for the sugar control panel (erikos)
|
||||||
|
* #4358: pydoc strings for sugar.activity.Activity
|
||||||
|
|
||||||
|
Snapshot 176262f2e9
|
||||||
|
|
||||||
|
* Added morgs NotifyAlert (timed one button alert) to the alert api (erikos)
|
||||||
|
|
||||||
Snapshot fdb4e49b14
|
Snapshot fdb4e49b14
|
||||||
|
|
||||||
|
18
configure.ac
18
configure.ac
@ -45,7 +45,6 @@ AC_OUTPUT([
|
|||||||
Makefile
|
Makefile
|
||||||
bin/Makefile
|
bin/Makefile
|
||||||
data/Makefile
|
data/Makefile
|
||||||
data/icons/Makefile
|
|
||||||
lib/Makefile
|
lib/Makefile
|
||||||
lib/sugar/Makefile
|
lib/sugar/Makefile
|
||||||
lib/sugar/activity/Makefile
|
lib/sugar/activity/Makefile
|
||||||
@ -67,22 +66,5 @@ shell/view/home/Makefile
|
|||||||
shell/model/Makefile
|
shell/model/Makefile
|
||||||
shell/model/devices/Makefile
|
shell/model/devices/Makefile
|
||||||
shell/model/devices/network/Makefile
|
shell/model/devices/network/Makefile
|
||||||
services/console/lib/Makefile
|
|
||||||
services/console/lib/graphics/Makefile
|
|
||||||
services/console/lib/procmem/Makefile
|
|
||||||
services/console/lib/net/Makefile
|
|
||||||
services/console/lib/ui/Makefile
|
|
||||||
services/console/Makefile
|
|
||||||
services/console/interface/Makefile
|
|
||||||
services/console/interface/xo/Makefile
|
|
||||||
services/console/interface/memphis/plugins/clean_size/Makefile
|
|
||||||
services/console/interface/memphis/plugins/smaps/Makefile
|
|
||||||
services/console/interface/memphis/plugins/Makefile
|
|
||||||
services/console/interface/memphis/plugins/memphis_init/Makefile
|
|
||||||
services/console/interface/memphis/plugins/cpu/Makefile
|
|
||||||
services/console/interface/memphis/Makefile
|
|
||||||
services/console/interface/network/Makefile
|
|
||||||
services/console/interface/logviewer/Makefile
|
|
||||||
services/console/interface/xserver/Makefile
|
|
||||||
po/Makefile.in
|
po/Makefile.in
|
||||||
])
|
])
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
SUBDIRS = icons
|
|
||||||
|
|
||||||
sugar.gtkrc: gtkrc.em
|
sugar.gtkrc: gtkrc.em
|
||||||
$(srcdir)/em.py -D theme=\'sugar\' $(srcdir)/gtkrc.em > \
|
$(srcdir)/em.py -D theme=\'sugar\' $(srcdir)/gtkrc.em > \
|
||||||
$(top_builddir)/data/sugar.gtkrc
|
$(top_builddir)/data/sugar.gtkrc
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
iconsdir = $(pkgdatadir)/data/icons
|
|
||||||
icons_DATA = \
|
|
||||||
arrow_NE.svg \
|
|
||||||
arrow_NW.svg \
|
|
||||||
arrow_SE.svg \
|
|
||||||
arrow_SW.svg
|
|
||||||
|
|
||||||
EXTRA_DIST = \
|
|
||||||
$(icons_DATA)
|
|
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="arrow_x5F_NE">
|
|
||||||
<g display="inline">
|
|
||||||
<g>
|
|
||||||
<path d="M20.154,15.718c-0.896,0-1.794,0.344-2.481,1.028c-1.373,1.372-1.373,3.598,0,4.969c0.686,0.686,1.585,1.026,2.481,1.026 h7.137L16.745,33.29c-0.633,0.635-1.026,1.514-1.029,2.482c0.003,1.939,1.576,3.512,3.514,3.512c0.972,0,1.848-0.395,2.483-1.028 l10.546-10.545v7.136c0,0.898,0.342,1.799,1.029,2.482c1.369,1.373,3.595,1.371,4.965,0c0.687-0.686,1.028-1.588,1.031-2.482 V15.716L20.154,15.718z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g></svg>
|
|
Before Width: | Height: | Size: 839 B |
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="arrow_x5F_NW">
|
|
||||||
<g display="inline">
|
|
||||||
<g>
|
|
||||||
<path d="M38.255,33.287L27.71,22.741h7.136c0.898,0,1.799-0.342,2.482-1.029c1.373-1.369,1.371-3.595,0-4.965 c-0.686-0.687-1.588-1.029-2.482-1.031H15.715l0.002,19.13c0,0.896,0.344,1.794,1.028,2.481c1.372,1.373,3.598,1.373,4.969,0 c0.686-0.686,1.026-1.585,1.026-2.481v-7.137L33.29,38.255c0.635,0.633,1.514,1.026,2.482,1.029 c1.939-0.003,3.512-1.576,3.512-3.514C39.284,34.799,38.889,33.923,38.255,33.287z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g></svg>
|
|
Before Width: | Height: | Size: 845 B |
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="arrow_x5F_SE">
|
|
||||||
<g display="inline">
|
|
||||||
<g>
|
|
||||||
<path d="M39.282,20.154c0-0.896-0.344-1.794-1.028-2.481c-1.372-1.373-3.598-1.373-4.969,0c-0.686,0.686-1.026,1.585-1.026,2.481 v7.137L21.709,16.745c-0.635-0.633-1.514-1.026-2.482-1.029c-1.939,0.003-3.512,1.576-3.512,3.514 c0,0.972,0.395,1.848,1.028,2.483l10.545,10.546h-7.136c-0.898,0-1.799,0.342-2.482,1.029c-1.373,1.369-1.371,3.595,0,4.965 c0.686,0.687,1.588,1.028,2.482,1.031h19.131L39.282,20.154z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g></svg>
|
|
Before Width: | Height: | Size: 844 B |
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="arrow_x5F_SW">
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path d="M34.845,32.259h-7.137L38.254,21.71c0.633-0.635,1.026-1.514,1.029-2.482c-0.003-1.939-1.576-3.512-3.514-3.512 c-0.972,0-1.848,0.395-2.483,1.028L22.741,27.289v-7.136c0-0.898-0.342-1.799-1.029-2.482c-1.369-1.373-3.595-1.371-4.965,0 c-0.687,0.686-1.029,1.588-1.031,2.482v19.131l19.13-0.002c0.896,0,1.794-0.344,2.481-1.028c1.373-1.372,1.373-3.598,0-4.969 C36.641,32.6,35.742,32.259,34.845,32.259z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g></svg>
|
|
Before Width: | Height: | Size: 827 B |
@ -1,7 +1,31 @@
|
|||||||
"""Base class for Python-coded activities
|
"""Base class for activities written in Python
|
||||||
|
|
||||||
This is currently the only reference for what an
|
This is currently the only definitive reference for what an
|
||||||
activity must do to participate in the Sugar desktop.
|
activity must do to participate in the Sugar desktop.
|
||||||
|
|
||||||
|
A Basic Activity
|
||||||
|
|
||||||
|
All activities must implement a class derived from 'Activity' in this class.
|
||||||
|
The convention is to call it ActivitynameActivity, but this is not required as
|
||||||
|
the activity.info file associated with your activity will tell the sugar-shell
|
||||||
|
which class to start.
|
||||||
|
|
||||||
|
For example the most minimal Activity:
|
||||||
|
|
||||||
|
|
||||||
|
from sugar.activity import activity
|
||||||
|
|
||||||
|
class ReadActivity(activity.Activity):
|
||||||
|
pass
|
||||||
|
|
||||||
|
To get a real, working activity, you will at least have to implement:
|
||||||
|
__init__(), read_file() and write_file()
|
||||||
|
|
||||||
|
Aditionally, you will probably need a at least a Toolbar so you can have some
|
||||||
|
interesting buttons for the user, like for example 'exit activity'
|
||||||
|
|
||||||
|
See the methods of the Activity class below for more information on what you
|
||||||
|
will need for a real activity.
|
||||||
"""
|
"""
|
||||||
# Copyright (C) 2006-2007 Red Hat, Inc.
|
# Copyright (C) 2006-2007 Red Hat, Inc.
|
||||||
#
|
#
|
||||||
@ -49,6 +73,11 @@ SCOPE_INVITE_ONLY = "invite" # shouldn't be shown in UI, it's implicit when you
|
|||||||
SCOPE_NEIGHBORHOOD = "public"
|
SCOPE_NEIGHBORHOOD = "public"
|
||||||
|
|
||||||
class ActivityToolbar(gtk.Toolbar):
|
class ActivityToolbar(gtk.Toolbar):
|
||||||
|
"""The Activity toolbar with the Journal entry title, sharing, Keep and Stop buttons
|
||||||
|
|
||||||
|
All activities should have this toolbar. It is easiest to add it to your
|
||||||
|
Activity by using the ActivityToolbox.
|
||||||
|
"""
|
||||||
def __init__(self, activity):
|
def __init__(self, activity):
|
||||||
gtk.Toolbar.__init__(self)
|
gtk.Toolbar.__init__(self)
|
||||||
|
|
||||||
@ -169,6 +198,38 @@ class ActivityToolbar(gtk.Toolbar):
|
|||||||
self._update_share()
|
self._update_share()
|
||||||
|
|
||||||
class EditToolbar(gtk.Toolbar):
|
class EditToolbar(gtk.Toolbar):
|
||||||
|
"""Provides the standard edit toolbar for Activities.
|
||||||
|
|
||||||
|
Members:
|
||||||
|
undo -- the undo button
|
||||||
|
redo -- the redo button
|
||||||
|
copy -- the copy button
|
||||||
|
paste -- the paste button
|
||||||
|
separator -- A separator between undo/redo and copy/paste
|
||||||
|
|
||||||
|
This class only provides the 'edit' buttons in a standard layout, your activity
|
||||||
|
will need to either hide buttons which make no sense for your Activity, or you
|
||||||
|
need to connect the button events to your own callbacks:
|
||||||
|
|
||||||
|
## Example from Read.activity:
|
||||||
|
# Create the edit toolbar:
|
||||||
|
self._edit_toolbar = EditToolbar(self._view)
|
||||||
|
# Hide undo and redo, they're not needed
|
||||||
|
self._edit_toolbar.undo.props.visible = False
|
||||||
|
self._edit_toolbar.redo.props.visible = False
|
||||||
|
# Hide the separator too:
|
||||||
|
self._edit_toolbar.separator.props.visible = False
|
||||||
|
|
||||||
|
# As long as nothing is selected, copy needs to be insensitive:
|
||||||
|
self._edit_toolbar.copy.set_sensitive(False)
|
||||||
|
# When the user clicks the button, call _edit_toolbar_copy_cb()
|
||||||
|
self._edit_toolbar.copy.connect('clicked', self._edit_toolbar_copy_cb)
|
||||||
|
|
||||||
|
# Add the edit toolbar:
|
||||||
|
toolbox.add_toolbar(_('Edit'), self._edit_toolbar)
|
||||||
|
# And make it visible:
|
||||||
|
self._edit_toolbar.show()
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
gtk.Toolbar.__init__(self)
|
gtk.Toolbar.__init__(self)
|
||||||
|
|
||||||
@ -198,6 +259,23 @@ class EditToolbar(gtk.Toolbar):
|
|||||||
self.paste.show()
|
self.paste.show()
|
||||||
|
|
||||||
class ActivityToolbox(Toolbox):
|
class ActivityToolbox(Toolbox):
|
||||||
|
"""Creates the Toolbox for the Activity
|
||||||
|
|
||||||
|
By default, the toolbox contains only the ActivityToolbar. After creating the
|
||||||
|
toolbox, you can add your activity specific toolbars, for example the
|
||||||
|
EditToolbar.
|
||||||
|
|
||||||
|
To add the ActivityToolbox to your Activity in MyActivity.__init__() do:
|
||||||
|
|
||||||
|
# Create the Toolbar with the ActivityToolbar:
|
||||||
|
toolbox = activity.ActivityToolbox(self)
|
||||||
|
... your code, inserting all other toolbars you need, like EditToolbar ...
|
||||||
|
|
||||||
|
# Add the toolbox to the activity frame:
|
||||||
|
self.set_toolbox(toolbox)
|
||||||
|
# And make it visible:
|
||||||
|
toolbox.show()
|
||||||
|
"""
|
||||||
def __init__(self, activity):
|
def __init__(self, activity):
|
||||||
Toolbox.__init__(self)
|
Toolbox.__init__(self)
|
||||||
|
|
||||||
@ -209,7 +287,71 @@ class ActivityToolbox(Toolbox):
|
|||||||
return self._activity_toolbar
|
return self._activity_toolbar
|
||||||
|
|
||||||
class Activity(Window, gtk.Container):
|
class Activity(Window, gtk.Container):
|
||||||
"""Base Activity class that all other Activities derive from."""
|
"""This is the base Activity class that all other Activities derive from. This is where your activity starts.
|
||||||
|
|
||||||
|
To get a working Activity:
|
||||||
|
0. Derive your Activity from this class:
|
||||||
|
class MyActivity(activity.Activity):
|
||||||
|
...
|
||||||
|
|
||||||
|
1. implement an __init__() method for your Activity class.
|
||||||
|
|
||||||
|
Use your init method to create your own ActivityToolbar which will
|
||||||
|
contain some standard buttons:
|
||||||
|
toolbox = activity.ActivityToolbox(self)
|
||||||
|
|
||||||
|
Add extra Toolbars to your toolbox.
|
||||||
|
|
||||||
|
You should setup Activity sharing here too.
|
||||||
|
|
||||||
|
Finaly, your Activity may need some resources which you can claim
|
||||||
|
here too.
|
||||||
|
|
||||||
|
The __init__() method is also used to make the distinction between
|
||||||
|
being resumed from the Journal, or starting with a blank document.
|
||||||
|
|
||||||
|
2. Implement read_file() and write_file()
|
||||||
|
Most activities revolve around creating and storing Journal entries.
|
||||||
|
For example, Write: You create a document, it is saved to the Journal
|
||||||
|
and then later you resume working on the document.
|
||||||
|
|
||||||
|
read_file() and write_file() will be called by sugar to tell your
|
||||||
|
Activity that it should load or save the document the user is working
|
||||||
|
on.
|
||||||
|
|
||||||
|
3. Implement our Activity Toolbars.
|
||||||
|
The Toolbars are added to your Activity in step 1 (the toolbox), but
|
||||||
|
you need to implement them somewhere. Now is a good time.
|
||||||
|
|
||||||
|
There are a number of standard Toolbars. The most basic one, the one
|
||||||
|
your almost absolutely MUST have is the ActivityToolbar. Without
|
||||||
|
this, you're not really making a proper Sugar Activity (which may be
|
||||||
|
okay, but you should really stop and think about why not!) You do
|
||||||
|
this with the ActivityToolbox(self) call in step 1.
|
||||||
|
|
||||||
|
Usually, you will also need the standard EditToolbar. This is the one
|
||||||
|
which has the standard copy and paste buttons. You need to derive
|
||||||
|
your own EditToolbar class from sugar.EditToolbar:
|
||||||
|
class EditToolbar(activity.EditToolbar):
|
||||||
|
...
|
||||||
|
|
||||||
|
See EditToolbar for the methods you should implement in your class.
|
||||||
|
|
||||||
|
Finaly, your Activity will very likely need some activity specific
|
||||||
|
buttons and options you can create your own toolbars by deriving a
|
||||||
|
class from gtk.Toolbar:
|
||||||
|
class MySpecialToolbar(gtk.Toolbar):
|
||||||
|
...
|
||||||
|
|
||||||
|
4. Use your creativity. Make your Activity something special and share
|
||||||
|
it with your friends!
|
||||||
|
|
||||||
|
Read through the methods of the Activity class below, to learn more about
|
||||||
|
how to make an Activity work.
|
||||||
|
|
||||||
|
Hint: A good and simple Activity to learn from is the Read activity. To
|
||||||
|
create your own activity, you may want to copy it and use it as a template.
|
||||||
|
"""
|
||||||
__gtype_name__ = 'SugarActivity'
|
__gtype_name__ = 'SugarActivity'
|
||||||
|
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
@ -248,6 +390,11 @@ class Activity(Window, gtk.Container):
|
|||||||
|
|
||||||
Creates an ActivityService (self._bus) servicing
|
Creates an ActivityService (self._bus) servicing
|
||||||
this application.
|
this application.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
If your Activity implements __init__(), it should call
|
||||||
|
the base class __init()__ before doing Activity specific things.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Window.__init__(self)
|
Window.__init__(self)
|
||||||
|
|
||||||
@ -360,12 +507,25 @@ class Activity(Window, gtk.Container):
|
|||||||
return self._max_participants
|
return self._max_participants
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
|
"""Returns the activity id of the current instance of your activity.
|
||||||
|
|
||||||
|
The activity id is sort-of-like the unix process id (PID). However,
|
||||||
|
unlike PIDs it is only different for each new instance (with
|
||||||
|
create_jobject = True set) and stays the same everytime a user
|
||||||
|
resumes an activity. This is also the identity of your Activity to other
|
||||||
|
XOs for use when sharing.
|
||||||
|
"""
|
||||||
return self._activity_id
|
return self._activity_id
|
||||||
|
|
||||||
def get_bundle_id(self):
|
def get_bundle_id(self):
|
||||||
|
"""Returns the bundle_id from the activity.info file"""
|
||||||
return os.environ['SUGAR_BUNDLE_ID']
|
return os.environ['SUGAR_BUNDLE_ID']
|
||||||
|
|
||||||
def set_canvas(self, canvas):
|
def set_canvas(self, canvas):
|
||||||
|
"""Sets the 'work area' of your activity with the canvas of your choice.
|
||||||
|
|
||||||
|
One commonly used canvas is gtk.ScrolledWindow
|
||||||
|
"""
|
||||||
Window.set_canvas(self, canvas)
|
Window.set_canvas(self, canvas)
|
||||||
canvas.connect('map', self.__canvas_map_cb)
|
canvas.connect('map', self.__canvas_map_cb)
|
||||||
|
|
||||||
@ -380,10 +540,17 @@ class Activity(Window, gtk.Container):
|
|||||||
logging.debug("Error creating activity datastore object: %s" % err)
|
logging.debug("Error creating activity datastore object: %s" % err)
|
||||||
|
|
||||||
def get_activity_root(self):
|
def get_activity_root(self):
|
||||||
"""
|
"""Returns a path for saving Activity specific preferences, etc.
|
||||||
Return the appropriate location in the fs where to store activity related
|
|
||||||
data that doesn't pertain to the current execution of the activity and
|
Returns a path to the location in the filesystem where the activity can
|
||||||
thus cannot go into the DataStore.
|
store activity related data that doesn't pertain to the current
|
||||||
|
execution of the activity and thus cannot go into the DataStore.
|
||||||
|
|
||||||
|
Currently, this will return something like ~/.sugar/default/MyActivityName/
|
||||||
|
|
||||||
|
Activities should ONLY save settings, user preferences and other data
|
||||||
|
which isn't specific to a journal item here. If (meta-)data is in anyway
|
||||||
|
specific to a journal entry, it MUST be stored in the DataStore.
|
||||||
"""
|
"""
|
||||||
if os.environ.has_key('SUGAR_ACTIVITY_ROOT') and \
|
if os.environ.has_key('SUGAR_ACTIVITY_ROOT') and \
|
||||||
os.environ['SUGAR_ACTIVITY_ROOT']:
|
os.environ['SUGAR_ACTIVITY_ROOT']:
|
||||||
@ -395,6 +562,17 @@ class Activity(Window, gtk.Container):
|
|||||||
"""
|
"""
|
||||||
Subclasses implement this method if they support resuming objects from
|
Subclasses implement this method if they support resuming objects from
|
||||||
the journal. 'file_path' is the file to read from.
|
the journal. 'file_path' is the file to read from.
|
||||||
|
|
||||||
|
You should immediately open the file from the file_path, because the
|
||||||
|
file_name will be deleted immediately after returning from read_file().
|
||||||
|
Once the file has been opened, you do not have to read it immediately:
|
||||||
|
After you have opened it, the file will only be really gone when you
|
||||||
|
close it.
|
||||||
|
|
||||||
|
Although not required, this is also a good time to read all meta-data:
|
||||||
|
the file itself cannot be changed externally, but the title, description
|
||||||
|
and other metadata['tags'] may change. So if it is important for you to
|
||||||
|
notice changes, this is the time to record the originals.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@ -402,6 +580,17 @@ class Activity(Window, gtk.Container):
|
|||||||
"""
|
"""
|
||||||
Subclasses implement this method if they support saving data to objects
|
Subclasses implement this method if they support saving data to objects
|
||||||
in the journal. 'file_path' is the file to write to.
|
in the journal. 'file_path' is the file to write to.
|
||||||
|
|
||||||
|
If the user did make changes, you should create the file_path and save
|
||||||
|
all document data to it.
|
||||||
|
|
||||||
|
Additionally, you should also write any metadata needed to resume your
|
||||||
|
activity. For example, the Read activity saves the current page and zoom
|
||||||
|
level, so it can display the page.
|
||||||
|
|
||||||
|
Note: Currently, the file_path *WILL* be different from the one you
|
||||||
|
received in file_read(). Even if you kept the file_path from file_read()
|
||||||
|
open until now, you must still write the entire file to this file_path.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@ -466,7 +655,13 @@ class Activity(Window, gtk.Container):
|
|||||||
self._preview = self._get_preview()
|
self._preview = self._get_preview()
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""Request that the activity is saved to the Journal."""
|
"""Request that the activity is saved to the Journal.
|
||||||
|
|
||||||
|
This method is called by the close() method below. In general,
|
||||||
|
activities should not override this method. This method is part of the
|
||||||
|
public API of an Acivity, and should behave in standard ways. Use your
|
||||||
|
own implementation of write_file() to save your Activity specific data.
|
||||||
|
"""
|
||||||
|
|
||||||
logging.debug('Activity.save: %r' % self._jobject.object_id)
|
logging.debug('Activity.save: %r' % self._jobject.object_id)
|
||||||
|
|
||||||
@ -507,6 +702,11 @@ class Activity(Window, gtk.Container):
|
|||||||
error_handler=self.__save_error_cb)
|
error_handler=self.__save_error_cb)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
|
"""Request that the activity 'Keep in Journal' the current state of the activity.
|
||||||
|
|
||||||
|
Activities should not override this method. Instead, like save() do any
|
||||||
|
copy work that needs to be done in write_file()
|
||||||
|
"""
|
||||||
logging.debug('Activity.copy: %r' % self._jobject.object_id)
|
logging.debug('Activity.copy: %r' % self._jobject.object_id)
|
||||||
self._preview = self._get_preview()
|
self._preview = self._get_preview()
|
||||||
self.save()
|
self.save()
|
||||||
@ -570,6 +770,12 @@ class Activity(Window, gtk.Container):
|
|||||||
logging.error('Cannot invite %s, no such buddy.' % buddy_key)
|
logging.error('Cannot invite %s, no such buddy.' % buddy_key)
|
||||||
|
|
||||||
def invite(self, buddy_key):
|
def invite(self, buddy_key):
|
||||||
|
"""Invite a buddy to join this Activity.
|
||||||
|
|
||||||
|
Side Effects:
|
||||||
|
Calls self.share(True) to privately share the activity if it wasn't
|
||||||
|
shared before.
|
||||||
|
"""
|
||||||
self._invites_queue.append(buddy_key)
|
self._invites_queue.append(buddy_key)
|
||||||
|
|
||||||
if (self._shared_activity is None
|
if (self._shared_activity is None
|
||||||
@ -598,6 +804,11 @@ class Activity(Window, gtk.Container):
|
|||||||
self._pservice.share_activity(self, private=private)
|
self._pservice.share_activity(self, private=private)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""Request that the activity be stopped and saved to the Journal
|
||||||
|
|
||||||
|
Activities should not override this method, but should implement write_file() to
|
||||||
|
do any state saving instead.
|
||||||
|
"""
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
if self._shared_activity:
|
if self._shared_activity:
|
||||||
@ -617,6 +828,17 @@ class Activity(Window, gtk.Container):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def get_metadata(self):
|
def get_metadata(self):
|
||||||
|
"""Returns the jobject metadata or None if there is no jobject.
|
||||||
|
|
||||||
|
Activities can set metadata in write_file() using:
|
||||||
|
self.metadata['MyKey'] = "Something"
|
||||||
|
|
||||||
|
and retrieve metadata in read_file() using:
|
||||||
|
self.metadata.get('MyKey', 'aDefaultValue')
|
||||||
|
|
||||||
|
Note: Make sure your activity works properly if one or more of the
|
||||||
|
metadata items is missing. Never assume they will all be present.
|
||||||
|
"""
|
||||||
if self._jobject:
|
if self._jobject:
|
||||||
return self._jobject.metadata
|
return self._jobject.metadata
|
||||||
else:
|
else:
|
||||||
@ -625,12 +847,10 @@ class Activity(Window, gtk.Container):
|
|||||||
metadata = property(get_metadata, None)
|
metadata = property(get_metadata, None)
|
||||||
|
|
||||||
def get_bundle_name():
|
def get_bundle_name():
|
||||||
"""Return the bundle name for the current process' bundle
|
"""Return the bundle name for the current process' bundle"""
|
||||||
"""
|
|
||||||
return os.environ['SUGAR_BUNDLE_NAME']
|
return os.environ['SUGAR_BUNDLE_NAME']
|
||||||
|
|
||||||
def get_bundle_path():
|
def get_bundle_path():
|
||||||
"""Return the bundle path for the current process' bundle
|
"""Return the bundle path for the current process' bundle"""
|
||||||
"""
|
|
||||||
return os.environ['SUGAR_BUNDLE_PATH']
|
return os.environ['SUGAR_BUNDLE_PATH']
|
||||||
|
|
||||||
|
@ -348,22 +348,6 @@ def cmd_release(bundle_name, manifest):
|
|||||||
print 'Creating the bundle...'
|
print 'Creating the bundle...'
|
||||||
cmd_dist(bundle_name, manifest)
|
cmd_dist(bundle_name, manifest)
|
||||||
|
|
||||||
if os.environ.has_key('ACTIVITIES_REPOSITORY'):
|
|
||||||
print 'Uploading to the activities repository...'
|
|
||||||
repo = os.environ['ACTIVITIES_REPOSITORY']
|
|
||||||
|
|
||||||
server, path = repo.split(':')
|
|
||||||
retcode = subprocess.call(['ssh', server, 'rm',
|
|
||||||
'%s/%s*' % (path, bundle_name)])
|
|
||||||
if retcode:
|
|
||||||
print 'ERROR - cannot remove old bundles from the repository.'
|
|
||||||
|
|
||||||
bundle_path = os.path.join(_get_source_path(),
|
|
||||||
_get_package_name(bundle_name))
|
|
||||||
retcode = subprocess.call(['scp', bundle_path, repo])
|
|
||||||
if retcode:
|
|
||||||
print 'ERROR - cannot upload the bundle to the repository.'
|
|
||||||
|
|
||||||
print 'Done.'
|
print 'Done.'
|
||||||
|
|
||||||
def cmd_clean():
|
def cmd_clean():
|
||||||
|
@ -32,9 +32,8 @@ class Alert(gtk.EventBox, gobject.GObject):
|
|||||||
Alerts are used inside the activity window instead of being a
|
Alerts are used inside the activity window instead of being a
|
||||||
separate popup window. They do not hide canvas content. You can
|
separate popup window. They do not hide canvas content. You can
|
||||||
use add_alert(widget) and remove_alert(widget) inside your activity
|
use add_alert(widget) and remove_alert(widget) inside your activity
|
||||||
to add and remove the alert. You can set the position (bottom=-1,
|
to add and remove the alert. The position of the alert is below the
|
||||||
top=0,1) for alerts global for the window by changing alert_position,
|
toolbox or top in fullscreen mode.
|
||||||
default is bottom.
|
|
||||||
|
|
||||||
Properties:
|
Properties:
|
||||||
'title': the title of the alert,
|
'title': the title of the alert,
|
||||||
@ -225,3 +224,31 @@ class TimeoutAlert(Alert):
|
|||||||
self._response(gtk.RESPONSE_OK)
|
self._response(gtk.RESPONSE_OK)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class NotifyAlert(Alert):
|
||||||
|
"""Timeout alert with only an "OK" button - just for notifications"""
|
||||||
|
|
||||||
|
def __init__(self, timeout=5, **kwargs):
|
||||||
|
Alert.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
self._timeout = timeout
|
||||||
|
|
||||||
|
self._timeout_text = _TimeoutIcon(
|
||||||
|
text=self._timeout,
|
||||||
|
color=style.COLOR_BUTTON_GREY.get_int(),
|
||||||
|
background_color=style.COLOR_WHITE.get_int())
|
||||||
|
canvas = hippo.Canvas()
|
||||||
|
canvas.set_root(self._timeout_text)
|
||||||
|
canvas.show()
|
||||||
|
self.add_button(gtk.RESPONSE_OK, _('OK'), canvas)
|
||||||
|
|
||||||
|
gobject.timeout_add(1000, self.__timeout)
|
||||||
|
|
||||||
|
def __timeout(self):
|
||||||
|
self._timeout -= 1
|
||||||
|
self._timeout_text.props.text = self._timeout
|
||||||
|
if self._timeout == 0:
|
||||||
|
self._response(gtk.RESPONSE_OK)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
@ -1 +1 @@
|
|||||||
SUBDIRS = shell console
|
SUBDIRS = shell
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
SUBDIRS = interface lib
|
|
||||||
|
|
||||||
servicedir = $(datadir)/dbus-1/services
|
|
||||||
service_in_files = org.laptop.sugar.Console.service.in
|
|
||||||
service_DATA = $(service_in_files:.service.in=.service)
|
|
||||||
|
|
||||||
$(service_DATA): $(service_in_files) Makefile
|
|
||||||
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
|
|
||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
console.py \
|
|
||||||
label.py
|
|
||||||
|
|
||||||
bin_SCRIPTS = sugar-console
|
|
||||||
|
|
||||||
DISTCLEANFILES = $(service_DATA)
|
|
||||||
|
|
||||||
EXTRA_DIST = $(service_in_files) $(bin_SCRIPTS)
|
|
@ -1,12 +0,0 @@
|
|||||||
Defining new tabs in the developer console
|
|
||||||
==========================================
|
|
||||||
|
|
||||||
The tabs are top-level packages inside 'interface/'.
|
|
||||||
|
|
||||||
Each package used as a tab must have a class Interface, instantiatable
|
|
||||||
with no arguments, with an attribute 'widget' that is a Gtk widget to be
|
|
||||||
placed in the tab. That's it.
|
|
||||||
|
|
||||||
Tabs are automatically run under the GLib main loop, dbus-python is set up
|
|
||||||
to use it, and the shared dbus-python session-bus connection is expected to
|
|
||||||
exist.
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,94 +0,0 @@
|
|||||||
# Copyright (C) 2006, Eduardo Silva (edsiper@gmail.com).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import dbus
|
|
||||||
import dbus.glib
|
|
||||||
import dbus.service
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import gtk
|
|
||||||
import gobject
|
|
||||||
|
|
||||||
sys.path.append(os.path.dirname(__file__))
|
|
||||||
sys.path.append(os.path.dirname(__file__) + '/lib')
|
|
||||||
sys.path.append(os.path.dirname(__file__) + '/interface')
|
|
||||||
|
|
||||||
CONSOLE_BUS = 'org.laptop.sugar.Console'
|
|
||||||
CONSOLE_PATH = '/org/laptop/sugar/Console'
|
|
||||||
CONSOLE_IFACE = 'org.laptop.sugar.Console'
|
|
||||||
|
|
||||||
class Console:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
# Main Window
|
|
||||||
self.window = gtk.Window()
|
|
||||||
self.window.set_title('Developer console')
|
|
||||||
self.window.connect("delete-event", self._delete_event_cb)
|
|
||||||
|
|
||||||
self.default_width = gtk.gdk.screen_width() * 95 / 100
|
|
||||||
self.default_height = gtk.gdk.screen_height() * 95 / 100
|
|
||||||
|
|
||||||
self.window.set_default_size(self.default_width, self.default_height)
|
|
||||||
|
|
||||||
self.window.realize()
|
|
||||||
self.window.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
|
|
||||||
|
|
||||||
# Notebook
|
|
||||||
self.notebook = gtk.Notebook()
|
|
||||||
|
|
||||||
self._load_interface('xo', 'XO Resources')
|
|
||||||
self._load_interface('network', 'Network')
|
|
||||||
self._load_interface('xserver', 'X Server')
|
|
||||||
self._load_interface('memphis', 'Memphis')
|
|
||||||
self._load_interface('logviewer', 'Log Viewer')
|
|
||||||
self._load_interface('ps_watcher', 'Presence')
|
|
||||||
|
|
||||||
main_hbox = gtk.HBox()
|
|
||||||
main_hbox.pack_start(self.notebook, True, True, 0)
|
|
||||||
main_hbox.show()
|
|
||||||
|
|
||||||
self.notebook.show()
|
|
||||||
self.window.add(main_hbox)
|
|
||||||
|
|
||||||
def _load_interface(self, interface, label):
|
|
||||||
mod = __import__(interface)
|
|
||||||
widget = mod.Interface().widget
|
|
||||||
widget.show()
|
|
||||||
|
|
||||||
self.notebook.append_page(widget, gtk.Label(label))
|
|
||||||
|
|
||||||
def _delete_event_cb(self, window, gdkevent):
|
|
||||||
gtk.main_quit()
|
|
||||||
|
|
||||||
class Service(dbus.service.Object):
|
|
||||||
def __init__(self, bus, object_path=CONSOLE_PATH):
|
|
||||||
dbus.service.Object.__init__(self, bus, object_path)
|
|
||||||
self._console = Console()
|
|
||||||
|
|
||||||
@dbus.service.method(CONSOLE_IFACE)
|
|
||||||
def ToggleVisibility(self):
|
|
||||||
window = self._console.window
|
|
||||||
if not window.props.visible:
|
|
||||||
window.present()
|
|
||||||
else:
|
|
||||||
window.hide()
|
|
||||||
|
|
||||||
bus = dbus.SessionBus()
|
|
||||||
name = dbus.service.BusName(CONSOLE_BUS, bus)
|
|
||||||
|
|
||||||
obj = Service(name)
|
|
||||||
|
|
||||||
gtk.main()
|
|
@ -1,6 +0,0 @@
|
|||||||
SUBDIRS = memphis network logviewer xo xserver
|
|
||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/interface
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
ps_watcher.py
|
|
@ -1,4 +0,0 @@
|
|||||||
sugardir = $(pkgdatadir)/services/console/interface/logviewer
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
logviewer.py
|
|
@ -1 +0,0 @@
|
|||||||
from logviewer import Interface
|
|
@ -1,228 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
|
|
||||||
# Rewritten by Eduardo Silva, edsiper@gmail.com
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
import pygtk
|
|
||||||
import gtk
|
|
||||||
import gobject
|
|
||||||
import pango
|
|
||||||
import gnomevfs
|
|
||||||
|
|
||||||
from sugar import env
|
|
||||||
|
|
||||||
class MultiLogView(gtk.VBox):
|
|
||||||
def __init__(self, path, extra_files):
|
|
||||||
self._logs_path = path
|
|
||||||
self._active_log = None
|
|
||||||
self._extra_files = extra_files
|
|
||||||
|
|
||||||
# Creating Main treeview with Actitivities list
|
|
||||||
self._tv_menu = gtk.TreeView()
|
|
||||||
self._tv_menu.connect('cursor-changed', self._load_log)
|
|
||||||
self._tv_menu.set_rules_hint(True)
|
|
||||||
|
|
||||||
# Set width
|
|
||||||
box_width = gtk.gdk.screen_width() * 80 / 100
|
|
||||||
self._tv_menu.set_size_request(box_width*25/100, 0)
|
|
||||||
|
|
||||||
self._store_menu = gtk.TreeStore(str)
|
|
||||||
self._tv_menu.set_model(self._store_menu)
|
|
||||||
|
|
||||||
self._add_column(self._tv_menu, 'Sugar logs', 0)
|
|
||||||
self._logs = {}
|
|
||||||
|
|
||||||
# Activities menu
|
|
||||||
self.hbox = gtk.HBox(False, 3)
|
|
||||||
self.hbox.pack_start(self._tv_menu, True, True, 0)
|
|
||||||
|
|
||||||
# Activity log, set width
|
|
||||||
self._view = LogView()
|
|
||||||
self._view.set_size_request(box_width*75/100, 0)
|
|
||||||
|
|
||||||
self.hbox.pack_start(self._view, True, True, 0)
|
|
||||||
self.hbox.show_all()
|
|
||||||
self._configure_watcher()
|
|
||||||
self._create_log_view()
|
|
||||||
|
|
||||||
|
|
||||||
def _configure_watcher(self):
|
|
||||||
# Setting where gnomeVFS will be watching
|
|
||||||
gnomevfs.monitor_add('file://' + self._logs_path,
|
|
||||||
gnomevfs.MONITOR_DIRECTORY,
|
|
||||||
self._log_file_changed_cb)
|
|
||||||
|
|
||||||
for f in self._extra_files:
|
|
||||||
gnomevfs.monitor_add('file://' + f,
|
|
||||||
gnomevfs.MONITOR_FILE,
|
|
||||||
self._log_file_changed_cb)
|
|
||||||
|
|
||||||
def _log_file_changed_cb(self, monitor_uri, info_uri, event):
|
|
||||||
path = info_uri.split('file://')[-1]
|
|
||||||
filename = self._get_filename_from_path(path)
|
|
||||||
|
|
||||||
if event == gnomevfs.MONITOR_EVENT_CHANGED:
|
|
||||||
self._logs[filename].update()
|
|
||||||
elif event == gnomevfs.MONITOR_EVENT_DELETED:
|
|
||||||
self._delete_log_file_view(filename)
|
|
||||||
elif event == gnomevfs.MONITOR_EVENT_CREATED:
|
|
||||||
self._add_log_file(path)
|
|
||||||
|
|
||||||
# Load the log information in View (textview)
|
|
||||||
def _load_log(self, treeview):
|
|
||||||
treeselection = treeview.get_selection()
|
|
||||||
treestore, iter = treeselection.get_selected()
|
|
||||||
|
|
||||||
# Get current selection
|
|
||||||
act_log = self._store_menu.get_value(iter, 0)
|
|
||||||
|
|
||||||
# Set buffer and scroll down
|
|
||||||
self._view.textview.set_buffer(self._logs[act_log])
|
|
||||||
self._view.textview.scroll_to_mark(self._logs[act_log].get_insert(), 0)
|
|
||||||
self._active_log = act_log
|
|
||||||
|
|
||||||
def _create_log_view(self):
|
|
||||||
# Searching log files
|
|
||||||
for logfile in os.listdir(self._logs_path):
|
|
||||||
full_log_path = os.path.join(self._logs_path, logfile)
|
|
||||||
self._add_log_file(full_log_path)
|
|
||||||
|
|
||||||
for ext in self._extra_files:
|
|
||||||
self._add_log_file(ext)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _delete_log_file_view(self, logkey):
|
|
||||||
self._store_menu.remove(self._logs[logkey].iter)
|
|
||||||
del self._logs[logkey]
|
|
||||||
|
|
||||||
def _get_filename_from_path(self, path):
|
|
||||||
return path.split('/')[-1]
|
|
||||||
|
|
||||||
def _add_log_file(self, path):
|
|
||||||
if os.path.isdir(path):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not os.path.exists(path):
|
|
||||||
print "ERROR: %s don't exists"
|
|
||||||
return False
|
|
||||||
|
|
||||||
logfile = self._get_filename_from_path(path)
|
|
||||||
|
|
||||||
if not self._logs.has_key(logfile):
|
|
||||||
iter = self._add_log_row(logfile)
|
|
||||||
model = LogBuffer(path, iter)
|
|
||||||
self._logs[logfile] = model
|
|
||||||
|
|
||||||
self._logs[logfile].update()
|
|
||||||
written = self._logs[logfile]._written
|
|
||||||
|
|
||||||
# Load the first iter
|
|
||||||
if self._active_log == None:
|
|
||||||
self._active_log = logfile
|
|
||||||
iter = self._tv_menu.get_model().get_iter_root()
|
|
||||||
self._tv_menu.get_selection().select_iter(iter)
|
|
||||||
self._load_log(self._tv_menu)
|
|
||||||
|
|
||||||
if written > 0 and self._active_log == logfile:
|
|
||||||
self._view.textview.scroll_to_mark(self._logs[logfile].get_insert(), 0)
|
|
||||||
|
|
||||||
|
|
||||||
def _add_log_row(self, name):
|
|
||||||
return self._insert_row(self._store_menu, None, name)
|
|
||||||
|
|
||||||
# Add a new column to the main treeview, (code from Memphis)
|
|
||||||
def _add_column(self, treeview, column_name, index):
|
|
||||||
cell = gtk.CellRendererText()
|
|
||||||
col_tv = gtk.TreeViewColumn(column_name, cell, text=index)
|
|
||||||
col_tv.set_resizable(True)
|
|
||||||
col_tv.set_property('clickable', True)
|
|
||||||
|
|
||||||
treeview.append_column(col_tv)
|
|
||||||
|
|
||||||
# Set the last column index added
|
|
||||||
self.last_col_index = index
|
|
||||||
|
|
||||||
# Insert a Row in our TreeView
|
|
||||||
def _insert_row(self, store, parent, name):
|
|
||||||
iter = store.insert_before(parent, None)
|
|
||||||
index = 0
|
|
||||||
store.set_value(iter, index , name)
|
|
||||||
|
|
||||||
return iter
|
|
||||||
|
|
||||||
class LogBuffer(gtk.TextBuffer):
|
|
||||||
def __init__(self, logfile, iter=None):
|
|
||||||
gtk.TextBuffer.__init__(self)
|
|
||||||
|
|
||||||
self._logfile = logfile
|
|
||||||
self._pos = 0
|
|
||||||
self.iter = iter
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
try:
|
|
||||||
f = open(self._logfile, 'r')
|
|
||||||
init_pos = self._pos
|
|
||||||
|
|
||||||
f.seek(self._pos)
|
|
||||||
self.insert(self.get_end_iter(), f.read())
|
|
||||||
self._pos = f.tell()
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
self._written = (self._pos - init_pos)
|
|
||||||
except:
|
|
||||||
self.insert(self.get_end_iter(), "Console error: can't open the file\n")
|
|
||||||
self._written = 0
|
|
||||||
|
|
||||||
class LogView(gtk.ScrolledWindow):
|
|
||||||
def __init__(self):
|
|
||||||
gtk.ScrolledWindow.__init__(self)
|
|
||||||
|
|
||||||
self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
|
||||||
|
|
||||||
self.textview = gtk.TextView()
|
|
||||||
self.textview.set_wrap_mode(gtk.WRAP_WORD)
|
|
||||||
|
|
||||||
font = pango.FontDescription('Sans 8')
|
|
||||||
font.set_weight(pango.WEIGHT_LIGHT)
|
|
||||||
self.textview.modify_font(font)
|
|
||||||
|
|
||||||
# Set background color
|
|
||||||
bgcolor = gtk.gdk.color_parse("#FFFFFF")
|
|
||||||
self.textview.modify_base(gtk.STATE_NORMAL, bgcolor)
|
|
||||||
|
|
||||||
self.textview.set_editable(False)
|
|
||||||
|
|
||||||
self.add(self.textview)
|
|
||||||
self.textview.show()
|
|
||||||
|
|
||||||
class Interface:
|
|
||||||
def __init__(self):
|
|
||||||
# Main path to watch: ~/.sugar/someuser/logs...
|
|
||||||
main_path = os.path.join(env.get_profile_path(), 'logs')
|
|
||||||
|
|
||||||
# extra files to watch in logviewer
|
|
||||||
ext_files = []
|
|
||||||
ext_files.append("/var/log/Xorg.0.log")
|
|
||||||
ext_files.append("/var/log/syslog")
|
|
||||||
ext_files.append("/var/log/messages")
|
|
||||||
|
|
||||||
viewer = MultiLogView(main_path, ext_files)
|
|
||||||
self.widget = viewer.hbox
|
|
@ -1,8 +0,0 @@
|
|||||||
SUBDIRS = plugins
|
|
||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/interface/memphis
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
memphis.py \
|
|
||||||
plugin.py
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
from memphis import Interface
|
|
@ -1,235 +0,0 @@
|
|||||||
# Copyright (C) 2006, Eduardo Silva (edsiper@gmail.com).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import string
|
|
||||||
import wnck
|
|
||||||
import plugin
|
|
||||||
|
|
||||||
from procmem import proc
|
|
||||||
|
|
||||||
try:
|
|
||||||
import gtk
|
|
||||||
import gtk.gdk
|
|
||||||
import gobject
|
|
||||||
except:
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
class Interface:
|
|
||||||
|
|
||||||
store_data_types = []
|
|
||||||
store_data_types_details = []
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
|
|
||||||
# Our GtkTree (Treeview)
|
|
||||||
self.treeview = gtk.TreeView()
|
|
||||||
self.treeview.show()
|
|
||||||
|
|
||||||
self.button_start = gtk.Button('Start Memphis')
|
|
||||||
self.button_stop = gtk.Button('Stop Memphis')
|
|
||||||
|
|
||||||
fixed = gtk.Fixed()
|
|
||||||
fixed.add(self.button_start)
|
|
||||||
fixed.add(self.button_stop)
|
|
||||||
|
|
||||||
vbox = gtk.VBox(False)
|
|
||||||
vbox.set_border_width(5)
|
|
||||||
vbox.pack_start(fixed, True, True, 0)
|
|
||||||
|
|
||||||
# Our GtkTree (Treeview)
|
|
||||||
self.treeview = gtk.TreeView()
|
|
||||||
t_width = gtk.gdk.screen_width()
|
|
||||||
t_height = gtk.gdk.screen_height() * 83 / 100
|
|
||||||
|
|
||||||
self.treeview.set_size_request(t_width, t_height)
|
|
||||||
vbox.pack_start(self.treeview, True, True, 0)
|
|
||||||
vbox.show_all()
|
|
||||||
self.widget = vbox
|
|
||||||
|
|
||||||
# Loading plugins
|
|
||||||
self.plg = plugin.Plugin()
|
|
||||||
|
|
||||||
# TOP data types (columns)
|
|
||||||
self.store_data_types = []
|
|
||||||
|
|
||||||
for plg in self.plg.list:
|
|
||||||
plg_data = plg.INTERNALS
|
|
||||||
|
|
||||||
# Give plugin object to plugin
|
|
||||||
plg.INTERNALS['Plg'] = self.plg
|
|
||||||
|
|
||||||
# Creating a store model and loading process data to Treeview
|
|
||||||
# self.store_data_types, ex [int, str, str, str, int,...]
|
|
||||||
#self.store = gtk.TreeStore(*self.store_data_types)
|
|
||||||
self.data = Data(self, self.treeview, self.plg.list)
|
|
||||||
|
|
||||||
self.button_stop.hide()
|
|
||||||
self.button_start.connect('clicked', self.data._start_memphis)
|
|
||||||
self.button_stop.connect('clicked', self.data._stop_memphis)
|
|
||||||
|
|
||||||
class Data:
|
|
||||||
|
|
||||||
last_col_index = 0
|
|
||||||
|
|
||||||
store_data_cols = []
|
|
||||||
store_data_types = []
|
|
||||||
store_data_types_details = []
|
|
||||||
|
|
||||||
_running_status = False
|
|
||||||
|
|
||||||
def __init__(self, interface, treeview, plg_list):
|
|
||||||
|
|
||||||
self.interface = interface
|
|
||||||
|
|
||||||
# Top data types
|
|
||||||
self.plg_list = plg_list
|
|
||||||
|
|
||||||
for plg in self.plg_list:
|
|
||||||
|
|
||||||
if plg.INTERNALS['top_data'] != None:
|
|
||||||
last_dt = len(self.store_data_types)
|
|
||||||
|
|
||||||
if last_dt > 0:
|
|
||||||
last_dt -= 1
|
|
||||||
|
|
||||||
len_dt = len(plg.INTERNALS['top_data'])
|
|
||||||
|
|
||||||
self.store_data_types_details.append({"plugin": plg, "init": last_dt, "end": last_dt + len_dt})
|
|
||||||
|
|
||||||
for dt in plg.INTERNALS['top_data']:
|
|
||||||
self.store_data_types.append(dt)
|
|
||||||
|
|
||||||
for col in plg.INTERNALS['top_cols']:
|
|
||||||
self.store_data_cols.append(col)
|
|
||||||
|
|
||||||
# Set global treeview
|
|
||||||
self.treeview = treeview
|
|
||||||
|
|
||||||
# Basic columns
|
|
||||||
index = 0
|
|
||||||
for column_name in self.store_data_cols:
|
|
||||||
self.add_column(column_name, index)
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
self.store = gtk.TreeStore(*self.store_data_types)
|
|
||||||
treeview.set_model(self.store)
|
|
||||||
|
|
||||||
def _start_memphis(self, button):
|
|
||||||
# Update information every 1.5 second
|
|
||||||
button.hide()
|
|
||||||
self.interface.button_stop.show()
|
|
||||||
self._running_status = True
|
|
||||||
self._gid = gobject.timeout_add(1500, self.load_data, self.treeview)
|
|
||||||
|
|
||||||
def _stop_memphis(self, button):
|
|
||||||
gobject.source_remove(self._gid)
|
|
||||||
self._running_status = False
|
|
||||||
button.hide()
|
|
||||||
self.interface.button_start.show()
|
|
||||||
|
|
||||||
# Add a new column to the main treeview
|
|
||||||
def add_column(self, column_name, index):
|
|
||||||
cell = gtk.CellRendererText()
|
|
||||||
col_tv = gtk.TreeViewColumn(column_name, cell, text=index)
|
|
||||||
col_tv.set_resizable(True)
|
|
||||||
col_tv.connect('clicked', self.sort_column_clicked)
|
|
||||||
col_tv.set_property('clickable', True)
|
|
||||||
|
|
||||||
self.treeview.append_column(col_tv)
|
|
||||||
|
|
||||||
# Set the last column index added
|
|
||||||
self.last_col_index = index
|
|
||||||
|
|
||||||
# Sorting
|
|
||||||
def sort_column_clicked(self, TreeViewColumn):
|
|
||||||
cols = self.treeview.get_columns()
|
|
||||||
|
|
||||||
# Searching column index
|
|
||||||
index = 0
|
|
||||||
for col in cols:
|
|
||||||
if col == TreeViewColumn:
|
|
||||||
break
|
|
||||||
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
self.store.set_sort_column_id(index, gtk.SORT_DESCENDING)
|
|
||||||
|
|
||||||
def load_data(self, treeview):
|
|
||||||
self.store.clear()
|
|
||||||
|
|
||||||
# Getting procfs data
|
|
||||||
self.procdata = proc.ProcInfo()
|
|
||||||
self.process_list = []
|
|
||||||
|
|
||||||
pids = []
|
|
||||||
screen = wnck.screen_get_default()
|
|
||||||
windows = screen.get_windows()
|
|
||||||
|
|
||||||
current_pid = os.getpid()
|
|
||||||
|
|
||||||
for win in windows:
|
|
||||||
pid = int(win.get_pid())
|
|
||||||
if current_pid != pid:
|
|
||||||
pids.append(pid)
|
|
||||||
|
|
||||||
self.process_list = set(pids)
|
|
||||||
|
|
||||||
# Sort rows using pid
|
|
||||||
#self.process_list.sort(key=operator.itemgetter('pid'))
|
|
||||||
self.process_iter = []
|
|
||||||
|
|
||||||
for pid in self.process_list:
|
|
||||||
pi = self.build_row(self.store, None, self.procdata, pid)
|
|
||||||
self.process_iter.append(pi)
|
|
||||||
|
|
||||||
treeview.set_rules_hint(True)
|
|
||||||
treeview.expand_all()
|
|
||||||
|
|
||||||
return self._running_status
|
|
||||||
|
|
||||||
def build_row(self, store, parent_iter, proc_data, pid):
|
|
||||||
data = []
|
|
||||||
|
|
||||||
pinfo = proc_data.MemoryInfo(pid)
|
|
||||||
|
|
||||||
# Look for plugins that need to update the top data treeview
|
|
||||||
for plg in self.plg_list:
|
|
||||||
plg_data = []
|
|
||||||
|
|
||||||
if plg.INTERNALS['top_data'] != None:
|
|
||||||
# data = [xxx, yyy,zzz,...]
|
|
||||||
plg_data = plg.info.plg_on_top_data_refresh(plg, pinfo)
|
|
||||||
|
|
||||||
for field in plg_data:
|
|
||||||
data.append(field)
|
|
||||||
|
|
||||||
pi = self.insert_row(store, parent_iter, data)
|
|
||||||
|
|
||||||
return pi
|
|
||||||
|
|
||||||
# Insert a Row in our TreeView
|
|
||||||
def insert_row(self, store, parent, row_data):
|
|
||||||
iter = store.insert_after(parent, None)
|
|
||||||
|
|
||||||
index = 0
|
|
||||||
|
|
||||||
for data in row_data:
|
|
||||||
store.set_value(iter, index , data)
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
return iter
|
|
@ -1,48 +0,0 @@
|
|||||||
###############################################
|
|
||||||
# Memphis Plugin Support
|
|
||||||
###############################################
|
|
||||||
|
|
||||||
import sys, os
|
|
||||||
from procmem import proc, proc_smaps, analysis
|
|
||||||
|
|
||||||
class Plugin:
|
|
||||||
|
|
||||||
# Plugin list
|
|
||||||
list = []
|
|
||||||
proc = proc.ProcInfo()
|
|
||||||
|
|
||||||
internal_plugin = "memphis_init"
|
|
||||||
plg_path = os.path.dirname(os.path.abspath(__file__)) + "/plugins"
|
|
||||||
|
|
||||||
# Frequency timer, managed by main program
|
|
||||||
freq_timer = 0
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
|
|
||||||
sys.path.insert(0, self.plg_path)
|
|
||||||
|
|
||||||
# Including memphis plugin
|
|
||||||
self.list.append(__import__(self.internal_plugin))
|
|
||||||
|
|
||||||
if os.path.isdir(self.plg_path):
|
|
||||||
# around dir entries
|
|
||||||
for plg in os.listdir(self.plg_path):
|
|
||||||
|
|
||||||
if plg == self.internal_plugin:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if os.path.isdir(self.plg_path + "/" + plg):
|
|
||||||
p = __import__(plg)
|
|
||||||
self.list.append(__import__(plg))
|
|
||||||
|
|
||||||
# Parse /proc/PID/smaps information
|
|
||||||
def proc_get_smaps(self, pid):
|
|
||||||
return proc_smaps.ProcSmaps(pid)
|
|
||||||
|
|
||||||
# Parse /proc/PID/maps information
|
|
||||||
def proc_get_maps(self, pid):
|
|
||||||
return proc_smaps.ProcMaps(pid)
|
|
||||||
|
|
||||||
def proc_analysis(self, pid):
|
|
||||||
return analysis.Analysis(pid)
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
SUBDIRS = clean_size cpu smaps memphis_init
|
|
||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/interface/memphis/plugins
|
|
||||||
sugar_PYTHON =
|
|
@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/interface/memphis/plugins/clean_size
|
|
||||||
sugar_PYTHON = \
|
|
||||||
README \
|
|
||||||
__init__.py \
|
|
||||||
info.py
|
|
@ -1,2 +0,0 @@
|
|||||||
This plugin give support to get the clean size memory usage
|
|
||||||
by process using the /proc/PID/maps file.
|
|
@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
import info
|
|
||||||
|
|
||||||
INTERNALS = {
|
|
||||||
# Basic information
|
|
||||||
'PLGNAME': "Clean Size",
|
|
||||||
'TABNAME': None,
|
|
||||||
'AUTHOR': "Eduardo Silva",
|
|
||||||
'DESC': "Print the approx real memory usage",
|
|
||||||
|
|
||||||
# Plugin API
|
|
||||||
'Plg': None, # Plugin object
|
|
||||||
|
|
||||||
'top_data': [int], # Top data types needed by memphis core plugin
|
|
||||||
'top_cols': ["Approx Real Usage (kb)"]
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
###########################################################
|
|
||||||
# Main function:
|
|
||||||
# -----------------
|
|
||||||
# self: self plugin object
|
|
||||||
# mself: memphis object / principal class
|
|
||||||
# pinfo: row with information about current tracing process
|
|
||||||
############################################################
|
|
||||||
|
|
||||||
def plg_on_top_data_refresh(self, pinfo):
|
|
||||||
|
|
||||||
# Get clean size
|
|
||||||
maps = self.INTERNALS['Plg'].proc_get_maps(pinfo['pid'])
|
|
||||||
|
|
||||||
size = (maps.clean_size/1024)
|
|
||||||
return [size]
|
|
@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/interface/memphis/plugins/cpu
|
|
||||||
sugar_PYTHON = \
|
|
||||||
README \
|
|
||||||
__init__.py \
|
|
||||||
info.py
|
|
@ -1,2 +0,0 @@
|
|||||||
This plugin give support to draw the Virtual Memory Size
|
|
||||||
usage by the current tracing process.
|
|
@ -1,23 +0,0 @@
|
|||||||
import os
|
|
||||||
import info
|
|
||||||
|
|
||||||
INTERNALS = {
|
|
||||||
'PLGNAME': "cpu",
|
|
||||||
'TABNAME': None,
|
|
||||||
'AUTHOR': "Eduardo Silva",
|
|
||||||
'DESC': "Print CPU usage",
|
|
||||||
|
|
||||||
# Plugin API
|
|
||||||
'Plg': None, # Plugin object
|
|
||||||
'current_plg': None, # Current plugin object
|
|
||||||
'current_page': None, # Current page number
|
|
||||||
|
|
||||||
# Top process view requirements
|
|
||||||
'top_data': [int], # Top data types needed by memphis core plugin
|
|
||||||
'top_cols': ["%CPU "] # Column names
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get CPU frequency
|
|
||||||
cpu_hz = os.sysconf(2)
|
|
||||||
|
|
||||||
pids_ujiffies = {}
|
|
@ -1,48 +0,0 @@
|
|||||||
###########################################################
|
|
||||||
# Main function:
|
|
||||||
# -----------------
|
|
||||||
# self: self plugin object
|
|
||||||
# mself: memphis object / principal class
|
|
||||||
# pinfo: row with information about current tracing process
|
|
||||||
############################################################
|
|
||||||
|
|
||||||
def plg_on_top_data_refresh(self, pinfo):
|
|
||||||
PI = self.INTERNALS['Plg'].proc
|
|
||||||
|
|
||||||
pid = pinfo['pid']
|
|
||||||
|
|
||||||
# Get JIFFIES CPU usage
|
|
||||||
used_jiffies = pinfo['utime'] + pinfo['stime']
|
|
||||||
last_ujiffies = get_pid_ujiffies(self, pid)
|
|
||||||
|
|
||||||
cpu_usage = PI.get_CPU_usage(self.cpu_hz, used_jiffies, pinfo['start_time'])
|
|
||||||
|
|
||||||
# Get PERCENT CPU usage
|
|
||||||
if last_ujiffies == 0.0:
|
|
||||||
pcpu = 0.0
|
|
||||||
set_pid_ujiffies(self, pid, cpu_usage['used_jiffies'])
|
|
||||||
data = [int(pcpu)]
|
|
||||||
return data
|
|
||||||
|
|
||||||
used_jiffies = cpu_usage['used_jiffies'] - last_ujiffies
|
|
||||||
|
|
||||||
# Available jiffies are
|
|
||||||
avail_jiffies = (500/1000.0)*self.cpu_hz # 500 = 0.5 second
|
|
||||||
pcpu = ((used_jiffies*100)/avail_jiffies)
|
|
||||||
|
|
||||||
set_pid_ujiffies(self, pid, cpu_usage['used_jiffies'])
|
|
||||||
|
|
||||||
data = [int(pcpu)]
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_pid_ujiffies(self, pid):
|
|
||||||
|
|
||||||
if pid in self.pids_ujiffies:
|
|
||||||
return self.pids_ujiffies[pid]
|
|
||||||
else:
|
|
||||||
set_pid_ujiffies(self, pid, 0)
|
|
||||||
return self.pids_ujiffies[pid]
|
|
||||||
|
|
||||||
def set_pid_ujiffies(self, pid, ujiffies):
|
|
||||||
self.pids_ujiffies[pid] = ujiffies
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/interface/memphis/plugins/memphis_init
|
|
||||||
sugar_PYTHON = \
|
|
||||||
README \
|
|
||||||
__init__.py \
|
|
||||||
info.py
|
|
@ -1,2 +0,0 @@
|
|||||||
This plugin give support to draw the Virtual Memory Size
|
|
||||||
usage by the current tracing process.
|
|
@ -1,15 +0,0 @@
|
|||||||
import info
|
|
||||||
|
|
||||||
INTERNALS = {
|
|
||||||
'PLGNAME': "memphis",
|
|
||||||
'TABNAME': None,
|
|
||||||
'AUTHOR': "Eduardo Silva",
|
|
||||||
'DESC': "Print basic process information",
|
|
||||||
|
|
||||||
# Plugin API
|
|
||||||
'Plg': None, # Plugin object
|
|
||||||
|
|
||||||
# Top process view requirements
|
|
||||||
'top_data': [int, str, str], # Top data types needed by memphis core plugin
|
|
||||||
'top_cols': ["PID", "Process Name", "Status"] # Column names
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
###########################################################
|
|
||||||
# Main function:
|
|
||||||
# -----------------
|
|
||||||
# self: self plugin object
|
|
||||||
# mself: memphis object / principal class
|
|
||||||
# pinfo: row with information about current tracing process
|
|
||||||
############################################################
|
|
||||||
|
|
||||||
def plg_on_top_data_refresh(self, ppinfo):
|
|
||||||
|
|
||||||
data = [ppinfo['pid'], ppinfo['name'], ppinfo['state_name']]
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/interface/memphis/plugins/dirty_size
|
|
||||||
sugar_PYTHON = \
|
|
||||||
README \
|
|
||||||
__init__.py \
|
|
||||||
info.py
|
|
@ -1,2 +0,0 @@
|
|||||||
This plugin give support to get the public and shared dirty memory usage
|
|
||||||
by process using the /proc/PID/smaps file.
|
|
@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
import info
|
|
||||||
|
|
||||||
|
|
||||||
INTERNALS = {
|
|
||||||
# Basic information
|
|
||||||
'PLGNAME': "SMaps",
|
|
||||||
'TABNAME': None, # No tabbed plugin
|
|
||||||
'AUTHOR': "Eduardo Silva",
|
|
||||||
'DESC': "Get dirty size and reference memory usage",
|
|
||||||
|
|
||||||
# Plugin API
|
|
||||||
'Plg': None, # Plugin object
|
|
||||||
|
|
||||||
'top_data': [int, int], # Top data types needed by memphis core plugin
|
|
||||||
'top_cols': ["PDRSS (kb)", "Referenced (kb)"]
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
###########################################################
|
|
||||||
# Main function:
|
|
||||||
# -----------------
|
|
||||||
# self: self plugin object
|
|
||||||
# mself: memphis object / principal class
|
|
||||||
# pinfo: row with information about current tracing process
|
|
||||||
############################################################
|
|
||||||
|
|
||||||
|
|
||||||
def plg_on_top_data_refresh(self, ppinfo):
|
|
||||||
smaps = get_data(self, ppinfo['pid'])
|
|
||||||
|
|
||||||
# memphis need an array
|
|
||||||
return [smaps['private_dirty'], smaps['referenced']]
|
|
||||||
|
|
||||||
def get_data(pself, pid):
|
|
||||||
ProcAnalysis = pself.INTERNALS['Plg'].proc_analysis(pid)
|
|
||||||
|
|
||||||
return ProcAnalysis.SMaps()
|
|
@ -1,4 +0,0 @@
|
|||||||
sugardir = $(pkgdatadir)/services/console/interface/network
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
network.py
|
|
@ -1 +0,0 @@
|
|||||||
from network import Interface
|
|
@ -1,103 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
import gobject
|
|
||||||
from net.device import Device
|
|
||||||
from ui.treeview import TreeView
|
|
||||||
|
|
||||||
class NetworkView(TreeView):
|
|
||||||
def __init__(self):
|
|
||||||
col_names = []
|
|
||||||
col_names.append({'index': 0, 'name': 'Interface'})
|
|
||||||
col_names.append({'index': 1, 'name': 'IP Address'})
|
|
||||||
col_names.append({'index': 2, 'name': 'NetMask'})
|
|
||||||
col_names.append({'index': 3, 'name': 'MAC Address'})
|
|
||||||
col_names.append({'index': 4, 'name': 'Bytes Recv'})
|
|
||||||
col_names.append({'index': 5, 'name': 'Bytes Sent'})
|
|
||||||
col_names.append({'index': 6, 'name': 'Packets Recv'})
|
|
||||||
col_names.append({'index': 7, 'name': 'Packets Sent'})
|
|
||||||
|
|
||||||
self._iface_iter = []
|
|
||||||
cols_type = [str, str, str, str, str, str, str, str]
|
|
||||||
TreeView.__init__(self, cols_type, col_names)
|
|
||||||
|
|
||||||
self._dev = Device()
|
|
||||||
self.show_all()
|
|
||||||
gobject.timeout_add(1500, self._update_data)
|
|
||||||
|
|
||||||
def _update_data(self):
|
|
||||||
interfaces = self._dev.get_interfaces()
|
|
||||||
for iface in interfaces:
|
|
||||||
info = self._dev.get_iface_info(iface['interface'])
|
|
||||||
row = []
|
|
||||||
row.append({'index':0, 'info': iface['interface']})
|
|
||||||
|
|
||||||
if info[0]:
|
|
||||||
row.append({'index':1, 'info': info[0]})
|
|
||||||
if info[1]:
|
|
||||||
row.append({'index':2, 'info': info[1]})
|
|
||||||
if info[2]:
|
|
||||||
row.append({'index':3, 'info': info[2]})
|
|
||||||
|
|
||||||
row.append({'index': 4, 'info': iface['bytes_sent']})
|
|
||||||
row.append({'index': 5, 'info': iface['packets_sent']})
|
|
||||||
row.append({'index': 6, 'info': iface['bytes_recv']})
|
|
||||||
row.append({'index': 7, 'info': iface['packets_recv']})
|
|
||||||
|
|
||||||
iter = self._get_iface_iter(iface['interface'])
|
|
||||||
if not iter:
|
|
||||||
iter = self.add_row(row)
|
|
||||||
self._set_iface_iter(iter, iface['interface'])
|
|
||||||
else:
|
|
||||||
self.update_row(iter, row)
|
|
||||||
|
|
||||||
self._clear_down_interfaces(interfaces)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _set_iface_iter(self, iter, iface):
|
|
||||||
self._iface_iter.append([iter, iface])
|
|
||||||
|
|
||||||
def _remove_iface_iter(self, search_iter):
|
|
||||||
i = 0
|
|
||||||
for [iter, interface] in self._iface_iter:
|
|
||||||
if iter == search_iter:
|
|
||||||
del self._iface_iter[i]
|
|
||||||
return
|
|
||||||
i+=1
|
|
||||||
|
|
||||||
def _get_iface_iter(self, iface):
|
|
||||||
for [iter, interface] in self._iface_iter:
|
|
||||||
if iface == interface:
|
|
||||||
return iter
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _clear_down_interfaces(self, interfaces):
|
|
||||||
for [iter, iface] in self._iface_iter:
|
|
||||||
found = False
|
|
||||||
for dev in interfaces:
|
|
||||||
if dev['interface']==iface:
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not found:
|
|
||||||
self.remove_row(iter)
|
|
||||||
self._remove_iface_iter(iter)
|
|
||||||
|
|
||||||
class Interface(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.widget = NetworkView()
|
|
@ -1,729 +0,0 @@
|
|||||||
# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import logging
|
|
||||||
from hashlib import sha1
|
|
||||||
|
|
||||||
import dbus
|
|
||||||
from gtk import VBox, Label, TreeView, Expander, ListStore, CellRendererText,\
|
|
||||||
ScrolledWindow, CellRendererToggle, TextView, VPaned
|
|
||||||
from gobject import timeout_add
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('ps_watcher')
|
|
||||||
#logging.basicConfig(filename='/tmp/ps_watcher.log')
|
|
||||||
#logging.getLogger().setLevel(1)
|
|
||||||
|
|
||||||
|
|
||||||
PS_NAME = 'org.laptop.Sugar.Presence'
|
|
||||||
PS_PATH = '/org/laptop/Sugar/Presence'
|
|
||||||
PS_IFACE = PS_NAME
|
|
||||||
ACTIVITY_IFACE = PS_IFACE + '.Activity'
|
|
||||||
BUDDY_IFACE = PS_IFACE + '.Buddy'
|
|
||||||
|
|
||||||
# keep these in sync with the calls to ListStore()
|
|
||||||
ACT_COL_PATH = 0
|
|
||||||
ACT_COL_WEIGHT = 1
|
|
||||||
ACT_COL_STRIKE = 2
|
|
||||||
ACT_COL_ID = 3
|
|
||||||
ACT_COL_COLOR = 4
|
|
||||||
ACT_COL_TYPE = 5
|
|
||||||
ACT_COL_NAME = 6
|
|
||||||
ACT_COL_CONN = 7
|
|
||||||
ACT_COL_CHANNELS = 8
|
|
||||||
ACT_COL_BUDDIES = 9
|
|
||||||
BUDDY_COL_PATH = 0
|
|
||||||
BUDDY_COL_WEIGHT = 1
|
|
||||||
BUDDY_COL_STRIKE = 2
|
|
||||||
BUDDY_COL_NICK = 3
|
|
||||||
BUDDY_COL_OWNER = 4
|
|
||||||
BUDDY_COL_COLOR = 5
|
|
||||||
BUDDY_COL_IP4 = 6
|
|
||||||
BUDDY_COL_CUR_ACT = 7
|
|
||||||
BUDDY_COL_KEY_ID = 8
|
|
||||||
BUDDY_COL_ACTIVITIES = 9
|
|
||||||
BUDDY_COL_HANDLES = 10
|
|
||||||
|
|
||||||
|
|
||||||
class ActivityWatcher(object):
|
|
||||||
|
|
||||||
def __init__(self, ps_watcher, object_path):
|
|
||||||
self.ps_watcher = ps_watcher
|
|
||||||
self.bus = ps_watcher.bus
|
|
||||||
self.proxy = self.bus.get_object(self.ps_watcher.unique_name,
|
|
||||||
object_path)
|
|
||||||
self.iface = dbus.Interface(self.proxy, ACTIVITY_IFACE)
|
|
||||||
self.object_path = object_path
|
|
||||||
self.appearing = True
|
|
||||||
self.disappearing = False
|
|
||||||
timeout_add(5000, self._finish_appearing)
|
|
||||||
|
|
||||||
self.id = '?'
|
|
||||||
self.color = '?'
|
|
||||||
self.type = '?'
|
|
||||||
self.name = '?'
|
|
||||||
self.conn = '?'
|
|
||||||
self.channels = None
|
|
||||||
self.buddies = None
|
|
||||||
|
|
||||||
self.iter = self.ps_watcher.add_activity(self)
|
|
||||||
|
|
||||||
self.iface.GetId(reply_handler=self._on_get_id_success,
|
|
||||||
error_handler=self._on_get_id_failure)
|
|
||||||
|
|
||||||
self.iface.GetColor(reply_handler=self._on_get_color_success,
|
|
||||||
error_handler=self._on_get_color_failure)
|
|
||||||
|
|
||||||
self.iface.GetType(reply_handler=self._on_get_type_success,
|
|
||||||
error_handler=self._on_get_type_failure)
|
|
||||||
|
|
||||||
self.iface.GetName(reply_handler=self._on_get_name_success,
|
|
||||||
error_handler=self._on_get_name_failure)
|
|
||||||
|
|
||||||
self.iface.connect_to_signal('NewChannel', self._on_new_channel)
|
|
||||||
self.iface.GetChannels(reply_handler=self._on_get_channels_success,
|
|
||||||
error_handler=self._on_get_channels_failure)
|
|
||||||
|
|
||||||
self.iface.connect_to_signal('BuddyJoined', self._on_buddy_joined)
|
|
||||||
self.iface.connect_to_signal('BuddyLeft', self._on_buddy_left)
|
|
||||||
self.iface.GetJoinedBuddies(reply_handler=self._on_get_buddies_success,
|
|
||||||
error_handler=self._on_get_buddies_failure)
|
|
||||||
|
|
||||||
def _on_buddy_joined(self, buddy):
|
|
||||||
if self.buddies is None:
|
|
||||||
return
|
|
||||||
if buddy.startswith('/org/laptop/Sugar/Presence/Buddies/'):
|
|
||||||
buddy = '.../' + buddy[35:]
|
|
||||||
self.ps_watcher.log('INFO: Activity %s emitted BuddyJoined("%s") '
|
|
||||||
'or mentioned the buddy in GetJoinedBuddies',
|
|
||||||
self.object_path, buddy)
|
|
||||||
self.buddies.append(buddy)
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_BUDDIES,
|
|
||||||
' '.join(self.buddies))
|
|
||||||
|
|
||||||
def _on_buddy_left(self, buddy):
|
|
||||||
if self.buddies is None:
|
|
||||||
return
|
|
||||||
if buddy.startswith('/org/laptop/Sugar/Presence/Buddies/'):
|
|
||||||
buddy = '.../' + buddy[35:]
|
|
||||||
self.ps_watcher.log('INFO: Activity %s emitted BuddyLeft("%s")',
|
|
||||||
self.object_path, buddy)
|
|
||||||
try:
|
|
||||||
self.buddies.remove(buddy)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_BUDDIES,
|
|
||||||
' '.join(self.buddies))
|
|
||||||
|
|
||||||
def _on_get_buddies_success(self, buddies):
|
|
||||||
self.buddies = []
|
|
||||||
for buddy in buddies:
|
|
||||||
self._on_buddy_joined(buddy)
|
|
||||||
|
|
||||||
def _on_get_buddies_failure(self, e):
|
|
||||||
self.log('ERROR: <Activity %s>.GetJoinedBuddies(): %s',
|
|
||||||
self.object_path, e)
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_BUDDIES,
|
|
||||||
'!')
|
|
||||||
|
|
||||||
def _on_new_channel(self, channel):
|
|
||||||
if self.channels is None:
|
|
||||||
return
|
|
||||||
if channel.startswith(self.full_conn):
|
|
||||||
channel = '...' + channel[len(self.full_conn):]
|
|
||||||
self.ps_watcher.log('INFO: Activity %s emitted NewChannel("%s") '
|
|
||||||
'or mentioned the channel in GetChannels()',
|
|
||||||
self.object_path, channel)
|
|
||||||
self.channels.append(channel)
|
|
||||||
# FIXME: listen for Telepathy Closed signal!
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CHANNELS,
|
|
||||||
' '.join(self.channels))
|
|
||||||
|
|
||||||
def _on_get_channels_success(self, service, conn, channels):
|
|
||||||
self.full_conn = conn
|
|
||||||
if conn.startswith('/org/freedesktop/Telepathy/Connection/'):
|
|
||||||
self.conn = '.../' + conn[38:]
|
|
||||||
else:
|
|
||||||
self.conn = conn
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CONN,
|
|
||||||
self.conn)
|
|
||||||
self.channels = []
|
|
||||||
for channel in channels:
|
|
||||||
self._on_new_channel(channel)
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CHANNELS,
|
|
||||||
' '.join(self.channels))
|
|
||||||
|
|
||||||
def _on_get_channels_failure(self, e):
|
|
||||||
self.ps_watcher.log('ERROR: <Activity %s>.GetChannels(): %s',
|
|
||||||
self.object_path, e)
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CONN,
|
|
||||||
'!')
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CHANNELS,
|
|
||||||
'!')
|
|
||||||
|
|
||||||
def _on_get_id_success(self, ident):
|
|
||||||
self.id = ident
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_ID, ident)
|
|
||||||
|
|
||||||
def _on_get_id_failure(self, e):
|
|
||||||
self.ps_watcher.log('ERROR: <Activity %s>.GetId(): %s',
|
|
||||||
self.object_path, e)
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_ID,
|
|
||||||
'!')
|
|
||||||
|
|
||||||
def _on_get_color_success(self, color):
|
|
||||||
self.color = color
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_COLOR,
|
|
||||||
color)
|
|
||||||
|
|
||||||
def _on_get_color_failure(self, e):
|
|
||||||
self.ps_watcher.log('ERROR: <Activity %s>.GetColor(): %s',
|
|
||||||
self.object_path, e)
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_COLOR,
|
|
||||||
'!')
|
|
||||||
|
|
||||||
def _on_get_type_success(self, type_):
|
|
||||||
self.type = type_
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_TYPE,
|
|
||||||
type_)
|
|
||||||
|
|
||||||
def _on_get_type_failure(self, e):
|
|
||||||
self.ps_watcher.log('ERROR: <Activity %s>.GetType(): %s',
|
|
||||||
self.object_path, e)
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_TYPE,
|
|
||||||
'!')
|
|
||||||
|
|
||||||
def _on_get_name_success(self, name):
|
|
||||||
self.name = name
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_NAME,
|
|
||||||
name)
|
|
||||||
|
|
||||||
def _on_get_name_failure(self, e):
|
|
||||||
self.ps_watcher.log('ERROR: <Activity %s>.GetName(): %s',
|
|
||||||
self.object_path, e)
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_NAME,
|
|
||||||
'!')
|
|
||||||
|
|
||||||
def _finish_appearing(self):
|
|
||||||
self.appearing = False
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_WEIGHT,
|
|
||||||
400)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def disappear(self):
|
|
||||||
self.disappearing = True
|
|
||||||
self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_STRIKE,
|
|
||||||
True)
|
|
||||||
timeout_add(5000, self._finish_disappearing)
|
|
||||||
|
|
||||||
def _finish_disappearing(self):
|
|
||||||
self.ps_watcher.remove_activity(self)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class BuddyWatcher(object):
|
|
||||||
|
|
||||||
def __init__(self, ps_watcher, object_path):
|
|
||||||
self.ps_watcher = ps_watcher
|
|
||||||
self.bus = ps_watcher.bus
|
|
||||||
self.proxy = self.bus.get_object(self.ps_watcher.unique_name,
|
|
||||||
object_path)
|
|
||||||
self.iface = dbus.Interface(self.proxy, BUDDY_IFACE)
|
|
||||||
self.object_path = object_path
|
|
||||||
self.appearing = True
|
|
||||||
self.disappearing = False
|
|
||||||
timeout_add(5000, self._finish_appearing)
|
|
||||||
|
|
||||||
self.nick = '?'
|
|
||||||
self.owner = False
|
|
||||||
self.color = '?'
|
|
||||||
self.ipv4 = '?'
|
|
||||||
self.cur_act = '?'
|
|
||||||
self.keyid = '?'
|
|
||||||
self.activities = None
|
|
||||||
self.handles = None
|
|
||||||
|
|
||||||
self.iter = self.ps_watcher.add_buddy(self)
|
|
||||||
|
|
||||||
self.iface.connect_to_signal('PropertyChanged', self._on_props_changed,
|
|
||||||
byte_arrays=True)
|
|
||||||
self.ps_watcher.log('Calling <Buddy %s>.GetProperties()', object_path)
|
|
||||||
self.iface.GetProperties(reply_handler=self._on_get_props_success,
|
|
||||||
error_handler=self._on_get_props_failure,
|
|
||||||
byte_arrays=True)
|
|
||||||
|
|
||||||
self.iface.connect_to_signal('JoinedActivity', self._on_joined)
|
|
||||||
self.iface.connect_to_signal('LeftActivity', self._on_left)
|
|
||||||
self.ps_watcher.log('Calling <Buddy %s>.GetJoinedActivities()',
|
|
||||||
object_path)
|
|
||||||
self.iface.GetJoinedActivities(reply_handler=self._on_get_acts_success,
|
|
||||||
error_handler=self._on_get_acts_failure)
|
|
||||||
|
|
||||||
self.iface.connect_to_signal('TelepathyHandleAdded',
|
|
||||||
self._on_handle_added)
|
|
||||||
self.iface.connect_to_signal('TelepathyHandleRemoved',
|
|
||||||
self._on_handle_removed)
|
|
||||||
self.ps_watcher.log('Calling <Buddy %s>.GetTelepathyHandles()',
|
|
||||||
object_path)
|
|
||||||
self.iface.GetTelepathyHandles(
|
|
||||||
reply_handler=self._on_get_handles_success,
|
|
||||||
error_handler=self._on_get_handles_failure)
|
|
||||||
|
|
||||||
def _on_handle_added(self, service, conn, handle):
|
|
||||||
if self.handles is None:
|
|
||||||
return
|
|
||||||
self.ps_watcher.log('INFO: Buddy %s emitted Telepathy HandleAdded('
|
|
||||||
'"%s", "%s", %u) or mentioned the handle in '
|
|
||||||
'GetTelepathyHandles()',
|
|
||||||
self.object_path, service, conn, handle)
|
|
||||||
if conn.startswith('/org/freedesktop/Telepathy/Connection/'):
|
|
||||||
conn = '.../' + conn[38:]
|
|
||||||
self.handles.append('%u@%s' % (handle, conn))
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter,
|
|
||||||
BUDDY_COL_HANDLES,
|
|
||||||
' '.join(self.handles))
|
|
||||||
|
|
||||||
def _on_handle_removed(self, service, conn, handle):
|
|
||||||
if self.handles is None:
|
|
||||||
return
|
|
||||||
if conn.startswith('/org/freedesktop/Telepathy/Connection/'):
|
|
||||||
conn = '.../' + conn[38:]
|
|
||||||
self.ps_watcher.log('INFO: Buddy %s emitted HandleRemoved("%s", '
|
|
||||||
'"%s", %u)', self.object_path, service, conn,
|
|
||||||
handle)
|
|
||||||
try:
|
|
||||||
self.handles.remove('%u@%s' % (handle, conn))
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_HANDLES,
|
|
||||||
' '.join(self.handles))
|
|
||||||
|
|
||||||
def _on_get_handles_success(self, handles):
|
|
||||||
self.handles = []
|
|
||||||
for service, conn, handle in handles:
|
|
||||||
self._on_handle_added(service, conn, handle)
|
|
||||||
|
|
||||||
def _on_get_handles_failure(self, e):
|
|
||||||
self.ps_watcher.log('ERROR: <Buddy %s>.GetTelepathyHandles(): %s',
|
|
||||||
self.object_path, e)
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_HANDLES,
|
|
||||||
'!')
|
|
||||||
|
|
||||||
def _on_joined(self, act):
|
|
||||||
if self.activities is None:
|
|
||||||
return
|
|
||||||
if act.startswith('/org/laptop/Sugar/Presence/Activities/'):
|
|
||||||
act = '.../' + act[38:]
|
|
||||||
self.ps_watcher.log('INFO: Buddy %s emitted ActivityJoined("%s") '
|
|
||||||
'or mentioned it in GetJoinedActivities()',
|
|
||||||
self.object_path, act)
|
|
||||||
self.activities.append(act)
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter,
|
|
||||||
BUDDY_COL_ACTIVITIES,
|
|
||||||
' '.join(self.activities))
|
|
||||||
|
|
||||||
def _on_left(self, act):
|
|
||||||
if self.activities is None:
|
|
||||||
return
|
|
||||||
if act.startswith('/org/laptop/Sugar/Presence/Activities/'):
|
|
||||||
act = '.../' + act[38:]
|
|
||||||
self.ps_watcher.log('INFO: Buddy %s emitted ActivityLeft("%s")',
|
|
||||||
self.object_path, act)
|
|
||||||
try:
|
|
||||||
self.activities.remove(act)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_ACTIVITIES,
|
|
||||||
' '.join(self.activities))
|
|
||||||
|
|
||||||
def _on_get_acts_success(self, activities):
|
|
||||||
self.activities = []
|
|
||||||
for act in activities:
|
|
||||||
self._on_joined(act)
|
|
||||||
|
|
||||||
def _on_get_acts_failure(self, e):
|
|
||||||
self.ps_watcher.log('ERROR: <Buddy %s>.GetJoinedActivities(): %s',
|
|
||||||
self.object_path, e)
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_ACTIVITIES,
|
|
||||||
'!')
|
|
||||||
|
|
||||||
def _on_props_changed(self, props):
|
|
||||||
try:
|
|
||||||
logger.debug('PropertyChanged(%s, %s)', self, props)
|
|
||||||
self.ps_watcher.log('INFO: <Buddy %s> emitted PropertyChanged(%r)',
|
|
||||||
self.object_path, props)
|
|
||||||
self._props_changed(props)
|
|
||||||
except Exception, e:
|
|
||||||
self.ps_watcher.log('INTERNAL ERROR: %s', e)
|
|
||||||
|
|
||||||
def _on_get_props_success(self, props):
|
|
||||||
try:
|
|
||||||
logger.debug('GetProperties(%s, %s)', self, props)
|
|
||||||
self.ps_watcher.log('INFO: <Buddy %s>.GetProperties() -> %r',
|
|
||||||
self.object_path, props)
|
|
||||||
self._props_changed(props)
|
|
||||||
except Exception, e:
|
|
||||||
self.ps_watcher.log('INTERNAL ERROR: %s', e)
|
|
||||||
|
|
||||||
def _props_changed(self, props):
|
|
||||||
logger.debug('Begin _props_changed')
|
|
||||||
if 'nick' in props:
|
|
||||||
self.nick = props.get('nick', '?')
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_NICK,
|
|
||||||
self.nick)
|
|
||||||
if 'owner' in props:
|
|
||||||
self.owner = bool(props.get('owner', False))
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_OWNER,
|
|
||||||
self.owner)
|
|
||||||
if 'color' in props:
|
|
||||||
self.color = props.get('color', '?')
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_COLOR,
|
|
||||||
self.color)
|
|
||||||
if 'ip4-address' in props:
|
|
||||||
self.ipv4 = props.get('ip4-address', '?')
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_IP4,
|
|
||||||
self.ipv4)
|
|
||||||
if 'current-activity' in props:
|
|
||||||
self.cur_act = props.get('current-activity', '?')
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_CUR_ACT,
|
|
||||||
self.cur_act)
|
|
||||||
if 'key' in props:
|
|
||||||
key = props.get('key', None)
|
|
||||||
if key:
|
|
||||||
self.keyid = '%d bytes, sha1 %s' % (len(key),
|
|
||||||
sha1(key).hexdigest())
|
|
||||||
else:
|
|
||||||
# could be '' (present, empty value) or None (absent). Either way:
|
|
||||||
self.keyid = '?'
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_KEY_ID,
|
|
||||||
self.keyid)
|
|
||||||
logger.debug('End _props_changed')
|
|
||||||
|
|
||||||
def _on_get_props_failure(self, e):
|
|
||||||
self.ps_watcher.log('ERROR: <Buddy %s>.GetProperties(): %s',
|
|
||||||
self.object_path, e)
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_NICK, '!')
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_OWNER,
|
|
||||||
False)
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_COLOR, '!')
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_IP4, '!')
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_CUR_ACT,
|
|
||||||
'!')
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_KEY_ID,
|
|
||||||
'!')
|
|
||||||
|
|
||||||
def _finish_appearing(self):
|
|
||||||
self.appearing = False
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_WEIGHT,
|
|
||||||
400)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def disappear(self):
|
|
||||||
self.disappearing = True
|
|
||||||
self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_STRIKE,
|
|
||||||
True)
|
|
||||||
timeout_add(5000, self._finish_disappearing)
|
|
||||||
|
|
||||||
def _finish_disappearing(self):
|
|
||||||
self.ps_watcher.remove_buddy(self)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class PresenceServiceWatcher(VBox):
|
|
||||||
|
|
||||||
def __init__(self, bus, unique_name, log):
|
|
||||||
VBox.__init__(self)
|
|
||||||
|
|
||||||
self.bus = bus
|
|
||||||
self.unique_name = unique_name
|
|
||||||
self.proxy = bus.get_object(unique_name, PS_PATH)
|
|
||||||
self.iface = dbus.Interface(self.proxy, PS_IFACE)
|
|
||||||
self.log = log
|
|
||||||
|
|
||||||
self.activities = None
|
|
||||||
self.iface.connect_to_signal('ActivityAppeared',
|
|
||||||
self._on_activity_appeared)
|
|
||||||
self.iface.connect_to_signal('ActivityDisappeared',
|
|
||||||
self._on_activity_disappeared)
|
|
||||||
self.iface.GetActivities(reply_handler=self._on_get_activities_success,
|
|
||||||
error_handler=self._on_get_activities_failure)
|
|
||||||
|
|
||||||
self.buddies = None
|
|
||||||
self.iface.connect_to_signal('BuddyAppeared',
|
|
||||||
self._on_buddy_appeared)
|
|
||||||
self.iface.connect_to_signal('BuddyDisappeared',
|
|
||||||
self._on_buddy_disappeared)
|
|
||||||
self.iface.GetBuddies(reply_handler=self._on_get_buddies_success,
|
|
||||||
error_handler=self._on_get_buddies_failure)
|
|
||||||
|
|
||||||
# keep this in sync with the ACT_COL_ constants
|
|
||||||
self.activities_list_store = ListStore(str, # object path
|
|
||||||
int, # weight (bold if new)
|
|
||||||
bool, # strikethrough (dead)
|
|
||||||
str, # ID
|
|
||||||
str, # color
|
|
||||||
str, # type
|
|
||||||
str, # name
|
|
||||||
str, # conn
|
|
||||||
str, # channels
|
|
||||||
str, # buddies
|
|
||||||
)
|
|
||||||
|
|
||||||
self.pack_start(Label('Activities:'), False, False)
|
|
||||||
|
|
||||||
self.activities_list = TreeView(self.activities_list_store)
|
|
||||||
c = self.activities_list.insert_column_with_attributes(0,
|
|
||||||
'Object path', CellRendererText(), text=ACT_COL_PATH,
|
|
||||||
weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(ACT_COL_PATH)
|
|
||||||
c = self.activities_list.insert_column_with_attributes(1, 'ID',
|
|
||||||
CellRendererText(), text=ACT_COL_ID,
|
|
||||||
weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(ACT_COL_ID)
|
|
||||||
c = self.activities_list.insert_column_with_attributes(2, 'Color',
|
|
||||||
CellRendererText(), text=ACT_COL_COLOR,
|
|
||||||
weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(ACT_COL_COLOR)
|
|
||||||
c = self.activities_list.insert_column_with_attributes(3, 'Type',
|
|
||||||
CellRendererText(), text=ACT_COL_TYPE, weight=ACT_COL_WEIGHT,
|
|
||||||
strikethrough=ACT_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(ACT_COL_TYPE)
|
|
||||||
c = self.activities_list.insert_column_with_attributes(4, 'Name',
|
|
||||||
CellRendererText(), text=ACT_COL_NAME, weight=ACT_COL_WEIGHT,
|
|
||||||
strikethrough=ACT_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(ACT_COL_NAME)
|
|
||||||
c = self.activities_list.insert_column_with_attributes(5, 'Connection',
|
|
||||||
CellRendererText(), text=ACT_COL_CONN, weight=ACT_COL_WEIGHT,
|
|
||||||
strikethrough=ACT_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(ACT_COL_CONN)
|
|
||||||
c = self.activities_list.insert_column_with_attributes(6, 'Channels',
|
|
||||||
CellRendererText(), text=ACT_COL_CHANNELS, weight=ACT_COL_WEIGHT,
|
|
||||||
strikethrough=ACT_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c = self.activities_list.insert_column_with_attributes(7, 'Buddies',
|
|
||||||
CellRendererText(), text=ACT_COL_BUDDIES, weight=ACT_COL_WEIGHT,
|
|
||||||
strikethrough=ACT_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
|
|
||||||
scroller = ScrolledWindow()
|
|
||||||
scroller.add(self.activities_list)
|
|
||||||
self.pack_start(scroller)
|
|
||||||
|
|
||||||
# keep this in sync with the BUDDY_COL_ constants
|
|
||||||
self.buddies_list_store = ListStore(str, int, bool, str, bool,
|
|
||||||
str, str, str, str, str, str)
|
|
||||||
|
|
||||||
self.pack_start(Label('Buddies:'), False, False)
|
|
||||||
self.buddies_list = TreeView(self.buddies_list_store)
|
|
||||||
c = self.buddies_list.insert_column_with_attributes(0, 'Object path',
|
|
||||||
CellRendererText(), text=BUDDY_COL_PATH,
|
|
||||||
weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(BUDDY_COL_PATH)
|
|
||||||
c = self.buddies_list.insert_column_with_attributes(1, 'Pubkey',
|
|
||||||
CellRendererText(), text=BUDDY_COL_KEY_ID,
|
|
||||||
weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(BUDDY_COL_KEY_ID)
|
|
||||||
c = self.buddies_list.insert_column_with_attributes(2, 'Nick',
|
|
||||||
CellRendererText(), text=BUDDY_COL_NICK,
|
|
||||||
weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(BUDDY_COL_NICK)
|
|
||||||
c = self.buddies_list.insert_column_with_attributes(3, 'Owner',
|
|
||||||
CellRendererToggle(), active=BUDDY_COL_OWNER)
|
|
||||||
c = self.buddies_list.insert_column_with_attributes(4, 'Color',
|
|
||||||
CellRendererText(), text=BUDDY_COL_COLOR,
|
|
||||||
weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(BUDDY_COL_OWNER)
|
|
||||||
c = self.buddies_list.insert_column_with_attributes(5, 'IPv4',
|
|
||||||
CellRendererText(), text=BUDDY_COL_IP4,
|
|
||||||
weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(BUDDY_COL_IP4)
|
|
||||||
c = self.buddies_list.insert_column_with_attributes(6, 'CurAct',
|
|
||||||
CellRendererText(), text=BUDDY_COL_CUR_ACT,
|
|
||||||
weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(BUDDY_COL_CUR_ACT)
|
|
||||||
c = self.buddies_list.insert_column_with_attributes(7, 'Activities',
|
|
||||||
CellRendererText(), text=BUDDY_COL_ACTIVITIES,
|
|
||||||
weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(BUDDY_COL_ACTIVITIES)
|
|
||||||
c = self.buddies_list.insert_column_with_attributes(8, 'Handles',
|
|
||||||
CellRendererText(), text=BUDDY_COL_HANDLES,
|
|
||||||
weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE)
|
|
||||||
c.set_resizable(True)
|
|
||||||
c.set_sort_column_id(BUDDY_COL_HANDLES)
|
|
||||||
|
|
||||||
scroller = ScrolledWindow()
|
|
||||||
scroller.add(self.buddies_list)
|
|
||||||
self.pack_start(scroller)
|
|
||||||
|
|
||||||
self.iface.connect_to_signal('ActivityInvitation',
|
|
||||||
self._on_activity_invitation)
|
|
||||||
self.iface.connect_to_signal('PrivateInvitation',
|
|
||||||
self._on_private_invitation)
|
|
||||||
|
|
||||||
def _on_get_activities_success(self, paths):
|
|
||||||
self.log('INFO: PS GetActivities() returned %r', paths)
|
|
||||||
self.activities = {}
|
|
||||||
for path in paths:
|
|
||||||
self.activities[path] = ActivityWatcher(self, path)
|
|
||||||
|
|
||||||
def _on_get_activities_failure(self, e):
|
|
||||||
self.log('ERROR: PS GetActivities() failed with %s', e)
|
|
||||||
|
|
||||||
def add_activity(self, act):
|
|
||||||
path = act.object_path
|
|
||||||
if path.startswith('/org/laptop/Sugar/Presence/Activities/'):
|
|
||||||
path = '.../' + path[38:]
|
|
||||||
return self.activities_list_store.append((path, 700, False,
|
|
||||||
act.id, act.color, act.type, act.name, act.conn, '?', '?'))
|
|
||||||
|
|
||||||
def remove_activity(self, act):
|
|
||||||
self.activities.pop(act.object_path, None)
|
|
||||||
self.activities_list_store.remove(act.iter)
|
|
||||||
|
|
||||||
def _on_activity_appeared(self, path):
|
|
||||||
if self.activities is None:
|
|
||||||
return
|
|
||||||
self.log('INFO: PS emitted ActivityAppeared("%s")', path)
|
|
||||||
self.activities[path] = ActivityWatcher(self, path)
|
|
||||||
|
|
||||||
def _on_activity_disappeared(self, path):
|
|
||||||
if self.activities is None:
|
|
||||||
return
|
|
||||||
self.log('INFO: PS emitted ActivityDisappeared("%s")', path)
|
|
||||||
act = self.activities.get(path)
|
|
||||||
if act is None:
|
|
||||||
self.log('WARNING: Trying to remove activity "%s" which is '
|
|
||||||
'already absent', path)
|
|
||||||
else:
|
|
||||||
# we don't remove the activity straight away, just cross it out
|
|
||||||
act.disappear()
|
|
||||||
|
|
||||||
def _on_activity_invitation(self, path):
|
|
||||||
self.log('INFO: PS emitted ActivityInvitation("%s")', path)
|
|
||||||
|
|
||||||
def _on_private_invitation(self, bus_name, conn, channel):
|
|
||||||
self.log('INFO: PS emitted PrivateInvitation("%s", "%s", "%s")',
|
|
||||||
bus_name, conn, channel)
|
|
||||||
|
|
||||||
def _on_get_buddies_success(self, paths):
|
|
||||||
self.log('INFO: PS GetBuddies() returned %r', paths)
|
|
||||||
self.buddies = {}
|
|
||||||
for path in paths:
|
|
||||||
self.buddies[path] = BuddyWatcher(self, path)
|
|
||||||
|
|
||||||
def _on_get_buddies_failure(self, e):
|
|
||||||
self.log('ERROR: PS GetBuddies() failed with %s', e)
|
|
||||||
|
|
||||||
def add_buddy(self, b):
|
|
||||||
path = b.object_path
|
|
||||||
if path.startswith('/org/laptop/Sugar/Presence/Buddies/'):
|
|
||||||
path = '.../' + path[35:]
|
|
||||||
return self.buddies_list_store.append((path, 700, False,
|
|
||||||
b.nick, b.owner, b.color, b.ipv4, b.cur_act, b.keyid,
|
|
||||||
'?', '?'))
|
|
||||||
|
|
||||||
def remove_buddy(self, b):
|
|
||||||
self.buddies.pop(b.object_path, None)
|
|
||||||
self.buddies_list_store.remove(b.iter)
|
|
||||||
|
|
||||||
def _on_buddy_appeared(self, path):
|
|
||||||
if self.buddies is None:
|
|
||||||
return
|
|
||||||
self.log('INFO: PS emitted BuddyAppeared("%s")', path)
|
|
||||||
self.buddies[path] = BuddyWatcher(self, path)
|
|
||||||
|
|
||||||
def _on_buddy_disappeared(self, path):
|
|
||||||
if self.buddies is None:
|
|
||||||
return
|
|
||||||
self.log('INFO: PS emitted BuddyDisappeared("%s")', path)
|
|
||||||
b = self.buddies.get(path)
|
|
||||||
if b is None:
|
|
||||||
self.log('ERROR: Trying to remove buddy "%s" which is already '
|
|
||||||
'absent', path)
|
|
||||||
else:
|
|
||||||
# we don't remove the activity straight away, just cross it out
|
|
||||||
b.disappear()
|
|
||||||
|
|
||||||
|
|
||||||
class PresenceServiceNameWatcher(VBox):
|
|
||||||
|
|
||||||
def __init__(self, bus):
|
|
||||||
VBox.__init__(self)
|
|
||||||
|
|
||||||
self.bus = bus
|
|
||||||
|
|
||||||
logger.debug('Running...')
|
|
||||||
self.label = Label('Looking for Presence Service...')
|
|
||||||
self.errors = ListStore(str)
|
|
||||||
|
|
||||||
errors_tree = TreeView(model=self.errors)
|
|
||||||
errors_tree.insert_column_with_attributes(0, 'Log', CellRendererText(),
|
|
||||||
text=0)
|
|
||||||
scroller = ScrolledWindow()
|
|
||||||
scroller.add(errors_tree)
|
|
||||||
|
|
||||||
self.paned = VPaned()
|
|
||||||
self.paned.pack1(scroller)
|
|
||||||
|
|
||||||
self.pack_start(self.label, False, False)
|
|
||||||
self.pack_end(self.paned)
|
|
||||||
|
|
||||||
bus.watch_name_owner(PS_NAME, self.on_name_owner_change)
|
|
||||||
self.ps_watcher = Label('-')
|
|
||||||
self.paned.pack2(self.ps_watcher)
|
|
||||||
|
|
||||||
self.show_all()
|
|
||||||
|
|
||||||
def log(self, format, *args):
|
|
||||||
self.errors.append((format % args,))
|
|
||||||
|
|
||||||
def on_name_owner_change(self, owner):
|
|
||||||
try:
|
|
||||||
if owner:
|
|
||||||
self.label.set_text('Presence Service running: unique name %s'
|
|
||||||
% owner)
|
|
||||||
if self.ps_watcher is not None:
|
|
||||||
self.paned.remove(self.ps_watcher)
|
|
||||||
self.ps_watcher = PresenceServiceWatcher(self.bus, owner,
|
|
||||||
self.log)
|
|
||||||
self.paned.pack2(self.ps_watcher)
|
|
||||||
self.show_all()
|
|
||||||
else:
|
|
||||||
self.label.set_text('Presence Service not running')
|
|
||||||
if self.ps_watcher is not None:
|
|
||||||
self.paned.remove(self.ps_watcher)
|
|
||||||
self.ps_watcher = Label('-')
|
|
||||||
self.paned.pack2(self.ps_watcher)
|
|
||||||
except Exception, e:
|
|
||||||
self.log('ERROR: %s', e)
|
|
||||||
|
|
||||||
|
|
||||||
class Interface(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.widget = PresenceServiceNameWatcher(dbus.SessionBus())
|
|
@ -1,7 +0,0 @@
|
|||||||
sugardir = $(pkgdatadir)/services/console/interface/xo
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
xo.py \
|
|
||||||
cpu.py \
|
|
||||||
system.py \
|
|
||||||
nandflash.py
|
|
@ -1,2 +0,0 @@
|
|||||||
from xo import Interface
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import gtk
|
|
||||||
import string
|
|
||||||
import gobject
|
|
||||||
import cairo
|
|
||||||
import procmem
|
|
||||||
|
|
||||||
from graphics.frequency import HorizontalGraphic
|
|
||||||
|
|
||||||
class CPU_Usage:
|
|
||||||
_CPU_HZ = 0
|
|
||||||
_last_jiffies = 0
|
|
||||||
_times = 0
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._CPU_HZ = os.sysconf(2)
|
|
||||||
|
|
||||||
def _get_CPU_data(self):
|
|
||||||
# Uptime info
|
|
||||||
stat_file = "/proc/stat"
|
|
||||||
|
|
||||||
try:
|
|
||||||
infile = file(stat_file, "r")
|
|
||||||
except:
|
|
||||||
print "Error trying uptime file"
|
|
||||||
return -1
|
|
||||||
|
|
||||||
stat_line = infile.readline()
|
|
||||||
cpu_info = string.split(stat_line, ' ')
|
|
||||||
infile.close()
|
|
||||||
|
|
||||||
return cpu_info
|
|
||||||
|
|
||||||
def _get_CPU_usage(self):
|
|
||||||
|
|
||||||
cpu_info = self._get_CPU_data()
|
|
||||||
|
|
||||||
used_jiffies = (int(cpu_info[2]) + int(cpu_info[3]) + int(cpu_info[4]))
|
|
||||||
|
|
||||||
if self._times ==0:
|
|
||||||
self._last_jiffies = used_jiffies
|
|
||||||
self._times +=1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
new_ujiffies = (used_jiffies - self._last_jiffies)
|
|
||||||
new_ajiffies = ((self.frequency/1000) * self._CPU_HZ)
|
|
||||||
|
|
||||||
if new_ajiffies <= 0:
|
|
||||||
pcpu = 0.0
|
|
||||||
else:
|
|
||||||
pcpu = ((new_ujiffies*100)/new_ajiffies)
|
|
||||||
|
|
||||||
if pcpu >100:
|
|
||||||
pcpu = 100
|
|
||||||
|
|
||||||
self._times +=1
|
|
||||||
self._last_jiffies = used_jiffies
|
|
||||||
|
|
||||||
return pcpu
|
|
||||||
|
|
||||||
class XO_CPU(gtk.Frame):
|
|
||||||
_frequency_timer = 1
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
gtk.Frame.__init__(self, 'System CPU Usage')
|
|
||||||
self.set_border_width(10)
|
|
||||||
|
|
||||||
width = (gtk.gdk.screen_width() * 99 / 100) - 50
|
|
||||||
height = (gtk.gdk.screen_height() * 15 / 100) - 20
|
|
||||||
|
|
||||||
# Create graphic
|
|
||||||
self._graphic = HorizontalGraphic()
|
|
||||||
self._graphic.set_size_request(width, height)
|
|
||||||
|
|
||||||
fixed = gtk.Fixed()
|
|
||||||
fixed.set_border_width(10)
|
|
||||||
fixed.add(self._graphic)
|
|
||||||
|
|
||||||
self.add(fixed)
|
|
||||||
|
|
||||||
self._DRW_CPU = CPU_Usage()
|
|
||||||
self._DRW_CPU.frequency = 1000 # 1 Second
|
|
||||||
|
|
||||||
gobject.timeout_add(self._DRW_CPU.frequency, self._update_cpu_usage)
|
|
||||||
|
|
||||||
def _update_cpu_usage(self):
|
|
||||||
print "update XO CPU"
|
|
||||||
self._cpu = self._DRW_CPU._get_CPU_usage()
|
|
||||||
self.set_label('System CPU Usage: ' + str(self._cpu) + '%')
|
|
||||||
|
|
||||||
# Draw the value into the graphic
|
|
||||||
self._graphic.draw_value(self._cpu)
|
|
||||||
|
|
||||||
return True
|
|
@ -1,119 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
import gobject
|
|
||||||
|
|
||||||
from os import statvfs
|
|
||||||
from label import Label
|
|
||||||
from graphics.box import *
|
|
||||||
|
|
||||||
class XO_NandFlash(gtk.Fixed):
|
|
||||||
_MOUNT_POINT = '/'
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
gtk.Fixed.__init__(self)
|
|
||||||
|
|
||||||
self._frame_text = 'Nand Flash'
|
|
||||||
self._frame = gtk.Frame(self._frame_text)
|
|
||||||
self.set_border_width(10)
|
|
||||||
|
|
||||||
|
|
||||||
self._nandflash_box = BoxGraphic(color_mode=COLOR_MODE_REVERSE)
|
|
||||||
self._nandflash_box.set_size_request(70, 150)
|
|
||||||
|
|
||||||
fixed = gtk.Fixed();
|
|
||||||
fixed.set_border_width(10)
|
|
||||||
fixed.add(self._nandflash_box)
|
|
||||||
|
|
||||||
hbox = gtk.HBox(False, 0)
|
|
||||||
hbox.pack_start(fixed, False, False, 4)
|
|
||||||
|
|
||||||
# Battery info
|
|
||||||
table = gtk.Table(2, 3)
|
|
||||||
table.set_border_width(5)
|
|
||||||
table.set_col_spacings(7)
|
|
||||||
table.set_row_spacings(7)
|
|
||||||
|
|
||||||
label_total_size = Label('Total: ' , Label.DESCRIPTION)
|
|
||||||
self._label_total_value = Label('0 KB', Label.DESCRIPTION)
|
|
||||||
|
|
||||||
label_used_size = Label('Used: ' , Label.DESCRIPTION)
|
|
||||||
self._label_used_value = Label('0 KB', Label.DESCRIPTION)
|
|
||||||
|
|
||||||
label_free_size = Label('Free: ' , Label.DESCRIPTION)
|
|
||||||
self._label_free_value = Label('0 KB', Label.DESCRIPTION)
|
|
||||||
|
|
||||||
# Total
|
|
||||||
table.attach(label_total_size, 0, 1, 0, 1)
|
|
||||||
table.attach(self._label_total_value, 1,2, 0,1)
|
|
||||||
# Used
|
|
||||||
table.attach(label_used_size, 0, 2, 1, 2)
|
|
||||||
table.attach(self._label_used_value, 1,3, 1,2)
|
|
||||||
# Free
|
|
||||||
table.attach(label_free_size, 0, 3, 2, 3)
|
|
||||||
table.attach(self._label_free_value, 1,4, 2,3)
|
|
||||||
|
|
||||||
alignment = gtk.Alignment(0,0,0,0)
|
|
||||||
alignment.add(table)
|
|
||||||
|
|
||||||
hbox.pack_start(alignment, False, False, 0)
|
|
||||||
self._frame.add(hbox)
|
|
||||||
self.add(self._frame)
|
|
||||||
self.show()
|
|
||||||
self.update_status()
|
|
||||||
|
|
||||||
def update_status(self):
|
|
||||||
nand = StorageDevice(self._MOUNT_POINT)
|
|
||||||
|
|
||||||
# Byte values
|
|
||||||
total = (nand.f_bsize*nand.f_blocks)
|
|
||||||
free = (nand.f_bsize*nand.f_bavail)
|
|
||||||
used = (total - free)
|
|
||||||
|
|
||||||
self._label_total_value.set_label(str(total/1024) + ' KB')
|
|
||||||
self._label_used_value.set_label(str(used/1024) + ' KB')
|
|
||||||
self._label_free_value.set_label(str(free/1024) + ' KB')
|
|
||||||
self._usage_percent = ((used*100)/total)
|
|
||||||
|
|
||||||
frame_label = self._frame_text + ': ' + str(self._usage_percent) + '%'
|
|
||||||
self._frame.set_label(frame_label)
|
|
||||||
self._nandflash_box.set_capacity(self._usage_percent)
|
|
||||||
|
|
||||||
class StorageDevice:
|
|
||||||
f_bsize = 0
|
|
||||||
f_frsize = 0
|
|
||||||
f_blocks = 0
|
|
||||||
f_bfree = 0
|
|
||||||
f_bavail = 0
|
|
||||||
f_files = 0
|
|
||||||
f_ffree = 0
|
|
||||||
f_favail = 0
|
|
||||||
f_flag = 0
|
|
||||||
f_namemax = 0
|
|
||||||
|
|
||||||
def __init__(self, mount_point):
|
|
||||||
self.f_bsize, self.f_frsize, self.f_blocks, self.f_bfree, \
|
|
||||||
self.f_bavail, self.f_files, self.f_ffree, \
|
|
||||||
self.f_favail, self.f_flag, self.f_namemax = statvfs(mount_point)
|
|
||||||
|
|
||||||
"""
|
|
||||||
w = gtk.Window()
|
|
||||||
a = XO_NandFlash()
|
|
||||||
w.add(a)
|
|
||||||
w.show_all()
|
|
||||||
gtk.main()
|
|
||||||
"""
|
|
@ -1,91 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import os
|
|
||||||
import gtk
|
|
||||||
import pango
|
|
||||||
|
|
||||||
from label import Label
|
|
||||||
from label import Style
|
|
||||||
|
|
||||||
class XO_System(gtk.Fixed):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
gtk.Fixed.__init__(self)
|
|
||||||
self.set_border_width(12)
|
|
||||||
|
|
||||||
table = gtk.Table(2, 2)
|
|
||||||
table.set_border_width(15)
|
|
||||||
table.set_col_spacings(7)
|
|
||||||
table.set_row_spacings(7)
|
|
||||||
|
|
||||||
# BUILD
|
|
||||||
build = self._read_file('/boot/olpc_build')
|
|
||||||
label_build = Label('OLPC Build:', Label.DESCRIPTION)
|
|
||||||
label_build_value = Label(str(build), Label.DESCRIPTION)
|
|
||||||
|
|
||||||
# KERNEL
|
|
||||||
sysinfo = os.uname()
|
|
||||||
label_kernel = Label('Kernel Version:', Label.DESCRIPTION)
|
|
||||||
label_kernel_value = Label(sysinfo[0] + '-' + sysinfo[2],\
|
|
||||||
Label.DESCRIPTION)
|
|
||||||
|
|
||||||
# FIRMWARE
|
|
||||||
firmware = self._read_file('/ofw/openprom/model')
|
|
||||||
label_firmware = Label('XO Firmware:', Label.DESCRIPTION)
|
|
||||||
label_firmware_value = Label(firmware, Label.DESCRIPTION)
|
|
||||||
|
|
||||||
# SERIAL NUMBER
|
|
||||||
serial = self._read_file('/ofw/serial-number')
|
|
||||||
label_serial = Label('XO Serial Number:', Label.DESCRIPTION)
|
|
||||||
label_serial_value = Label(serial, Label.DESCRIPTION)
|
|
||||||
|
|
||||||
# OLPC Build
|
|
||||||
table.attach(label_build, 0, 1, 0, 1)
|
|
||||||
table.attach(label_build_value, 1,2, 0,1)
|
|
||||||
|
|
||||||
# Kernel Version
|
|
||||||
table.attach(label_kernel, 0, 1, 1, 2)
|
|
||||||
table.attach(label_kernel_value, 1, 2, 1, 2)
|
|
||||||
|
|
||||||
# XO Firmware
|
|
||||||
table.attach(label_firmware, 0, 1, 2, 3)
|
|
||||||
table.attach(label_firmware_value, 1, 2, 2, 3)
|
|
||||||
|
|
||||||
# XO Serial Number
|
|
||||||
table.attach(label_serial, 0, 1, 3, 4)
|
|
||||||
table.attach(label_serial_value, 1, 2, 3, 4)
|
|
||||||
|
|
||||||
frame = gtk.Frame('System Information')
|
|
||||||
style = Style()
|
|
||||||
style.set_title_font(frame);
|
|
||||||
frame.add(table)
|
|
||||||
|
|
||||||
self.add(frame)
|
|
||||||
self.show_all()
|
|
||||||
|
|
||||||
def _read_file(self, path):
|
|
||||||
try:
|
|
||||||
f = open(path, 'r')
|
|
||||||
value = f.read()
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
value = value.split('\n')[0]
|
|
||||||
if value[len(value) - 1] == '\x00':
|
|
||||||
value = value[:len(value) - 1]
|
|
||||||
return value
|
|
||||||
except:
|
|
||||||
return "None"
|
|
@ -1,57 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import os
|
|
||||||
import gtk
|
|
||||||
import gobject
|
|
||||||
import gtk.gdk
|
|
||||||
import cairo
|
|
||||||
import string
|
|
||||||
|
|
||||||
from cpu import XO_CPU
|
|
||||||
from system import XO_System
|
|
||||||
from nandflash import XO_NandFlash
|
|
||||||
|
|
||||||
class Interface:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.widget = self.vbox = gtk.VBox(False, 3)
|
|
||||||
|
|
||||||
# System information
|
|
||||||
xo_system = XO_System()
|
|
||||||
self.vbox.pack_start(xo_system, False, False, 0)
|
|
||||||
|
|
||||||
# CPU usage / Graph
|
|
||||||
xo_cpu = XO_CPU()
|
|
||||||
self.vbox.pack_start(xo_cpu, False, False, 0)
|
|
||||||
|
|
||||||
# Graphics: Battery Status, NandFlash
|
|
||||||
self._xo_nandflash = XO_NandFlash()
|
|
||||||
|
|
||||||
hbox = gtk.HBox(False, 2)
|
|
||||||
hbox.pack_start(self._xo_nandflash, False, False, 0)
|
|
||||||
|
|
||||||
self.vbox.pack_start(hbox, False, False, 0)
|
|
||||||
self.vbox.show_all()
|
|
||||||
|
|
||||||
# Update every 5 seconds
|
|
||||||
gobject.timeout_add(5000, self._update_components)
|
|
||||||
|
|
||||||
def _update_components(self):
|
|
||||||
self._xo_nandflash.update_status()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
sugardir = $(pkgdatadir)/services/console/interface/xserver
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
xserver.py
|
|
@ -1 +0,0 @@
|
|||||||
from xserver import Interface
|
|
@ -1,111 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
import gobject
|
|
||||||
from pyxres import XRes
|
|
||||||
from ui.treeview import TreeView
|
|
||||||
|
|
||||||
class XorgView(TreeView):
|
|
||||||
def __init__(self):
|
|
||||||
col_names = []
|
|
||||||
col_names.append({'index': 0, 'name': 'PID'})
|
|
||||||
col_names.append({'index': 1, 'name': 'Resource Base'})
|
|
||||||
col_names.append({'index': 2, 'name': 'Pixmap Bytes'})
|
|
||||||
col_names.append({'index': 3, 'name': 'Other'})
|
|
||||||
col_names.append({'index': 4, 'name': 'Total'})
|
|
||||||
col_names.append({'index': 5, 'name': 'Window Name'})
|
|
||||||
|
|
||||||
self._window_iter = []
|
|
||||||
|
|
||||||
cols_type = [str, str, str, str, str, str]
|
|
||||||
TreeView.__init__(self, cols_type, col_names)
|
|
||||||
|
|
||||||
self._xres = XRes()
|
|
||||||
self._display = self._xres.open_display()
|
|
||||||
self.show_all()
|
|
||||||
gobject.timeout_add(1500, self._update_data)
|
|
||||||
|
|
||||||
def _nice_bytes(self, bytes):
|
|
||||||
prefix = "B"
|
|
||||||
value = bytes
|
|
||||||
|
|
||||||
if bytes/1024:
|
|
||||||
prefix = "K"
|
|
||||||
value = (bytes/1024)
|
|
||||||
|
|
||||||
return "%s%s" % (value, prefix)
|
|
||||||
|
|
||||||
def _update_data(self):
|
|
||||||
windows = self._xres.get_windows(self._display)
|
|
||||||
print windows
|
|
||||||
for w in windows:
|
|
||||||
row = []
|
|
||||||
row.append({'index':0, 'info': w.pid})
|
|
||||||
|
|
||||||
bytes = self._nice_bytes(w.pixmap_bytes)
|
|
||||||
obytes = self._nice_bytes(w.other_bytes)
|
|
||||||
tbytes = self._nice_bytes(w.pixmap_bytes+w.other_bytes)
|
|
||||||
|
|
||||||
row.append({'index':1, 'info': hex(w.resource_base)})
|
|
||||||
row.append({'index':2, 'info': bytes})
|
|
||||||
row.append({'index':3, 'info': obytes})
|
|
||||||
row.append({'index':4, 'info': tbytes})
|
|
||||||
row.append({'index':5, 'info': w.wm_name})
|
|
||||||
|
|
||||||
iter = self._get_window_iter(w.pid)
|
|
||||||
if not iter:
|
|
||||||
iter = self.add_row(row)
|
|
||||||
self._set_window_iter(iter, w.pid)
|
|
||||||
else:
|
|
||||||
self.update_row(iter, row)
|
|
||||||
|
|
||||||
self._clear_down_windows(windows)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _set_window_iter(self, iter, pid):
|
|
||||||
self._window_iter.append([iter, pid])
|
|
||||||
|
|
||||||
def _remove_iface_iter(self, search_iter):
|
|
||||||
i = 0
|
|
||||||
for [iter, pid] in self._window_iter:
|
|
||||||
if iter == search_iter:
|
|
||||||
del self._window_iter[i]
|
|
||||||
return
|
|
||||||
i+=1
|
|
||||||
|
|
||||||
def _get_window_iter(self, wpid):
|
|
||||||
for [iter, pid] in self._window_iter:
|
|
||||||
if wpid == pid:
|
|
||||||
return iter
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _clear_down_windows(self, windows):
|
|
||||||
for [iter, pid] in self._window_iter:
|
|
||||||
found = False
|
|
||||||
for w in windows:
|
|
||||||
if w.pid == pid:
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not found:
|
|
||||||
self.remove_row(iter)
|
|
||||||
self._remove_window_iter(iter)
|
|
||||||
|
|
||||||
class Interface(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.widget = XorgView()
|
|
@ -1,51 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
import pango
|
|
||||||
|
|
||||||
class Label(gtk.Label):
|
|
||||||
TITLE = 0
|
|
||||||
DESCRIPTION = 1
|
|
||||||
|
|
||||||
def __init__(self, text, font_type):
|
|
||||||
gtk.Label.__init__(self)
|
|
||||||
|
|
||||||
self.set_text(text)
|
|
||||||
self.set_alignment(0.0, 0.5)
|
|
||||||
|
|
||||||
s = {
|
|
||||||
self.TITLE: self._set_title_font,
|
|
||||||
self.DESCRIPTION: self._set_description_font
|
|
||||||
}[font_type]()
|
|
||||||
|
|
||||||
def _set_title_font(self):
|
|
||||||
font = pango.FontDescription('Sans 12')
|
|
||||||
font.set_weight(pango.WEIGHT_NORMAL)
|
|
||||||
self.modify_font(font)
|
|
||||||
|
|
||||||
def _set_description_font(self):
|
|
||||||
font = pango.FontDescription('Sans 8')
|
|
||||||
font.set_weight(pango.WEIGHT_NORMAL)
|
|
||||||
self.modify_font(font)
|
|
||||||
|
|
||||||
class Style:
|
|
||||||
|
|
||||||
def set_title_font(self, object):
|
|
||||||
font = pango.FontDescription('Sans 20')
|
|
||||||
font.set_weight(pango.WEIGHT_NORMAL)
|
|
||||||
object.modify_font(font)
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
SUBDIRS = procmem graphics net ui
|
|
||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/lib
|
|
||||||
sugar_PYTHON = \
|
|
||||||
pyxres.py
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/lib/graphics
|
|
||||||
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
box.py \
|
|
||||||
frequency.py
|
|
@ -1,101 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
import gobject
|
|
||||||
import cairo
|
|
||||||
|
|
||||||
COLOR_MODE_NORMAL = 0
|
|
||||||
COLOR_MODE_REVERSE = 1
|
|
||||||
|
|
||||||
class BoxGraphic(gtk.DrawingArea):
|
|
||||||
__gtype_name__ = 'ConsoleBoxGraphic'
|
|
||||||
|
|
||||||
__gproperties__ = {
|
|
||||||
'color-mode': (gobject.TYPE_INT, None, None, 0, 1, COLOR_MODE_NORMAL,
|
|
||||||
gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_color_status_high = [0, 0, 0]
|
|
||||||
_color_status_medium = [0, 0, 0]
|
|
||||||
_color_status_low = [0, 0, 0]
|
|
||||||
|
|
||||||
_limit_high = 0
|
|
||||||
_limit_medium = 0
|
|
||||||
_limit_low = 0
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
gobject.GObject.__init__(self, **kwargs)
|
|
||||||
gtk.DrawingArea.__init__(self)
|
|
||||||
self.connect("expose-event", self.do_expose)
|
|
||||||
self.connect('size-allocate', self._change_size_cb)
|
|
||||||
|
|
||||||
def do_expose(self, widget, event):
|
|
||||||
context = widget.window.cairo_create()
|
|
||||||
context.rectangle(0, 0, self._width, self._height)
|
|
||||||
|
|
||||||
context.set_source_rgb (0,0,0)
|
|
||||||
context.fill_preserve()
|
|
||||||
context.stroke()
|
|
||||||
|
|
||||||
self._draw_content(context, self._percent)
|
|
||||||
|
|
||||||
def do_set_property(self, pspec, value):
|
|
||||||
if pspec.name == 'color-mode':
|
|
||||||
self._configure(mode=value)
|
|
||||||
else:
|
|
||||||
raise AssertionError
|
|
||||||
|
|
||||||
def set_capacity(self, percent):
|
|
||||||
self._percent = percent
|
|
||||||
self.queue_draw()
|
|
||||||
|
|
||||||
def _configure(self, mode):
|
|
||||||
# Normal mode configure the box as a battery
|
|
||||||
# full is good, empty is bad
|
|
||||||
if mode == COLOR_MODE_NORMAL:
|
|
||||||
self._color_status_high = [0, 1, 0]
|
|
||||||
self._color_status_medium = [1,1,0]
|
|
||||||
self._color_status_low = [1,0,0]
|
|
||||||
self._limit_high = 60
|
|
||||||
self._limit_medium = 10
|
|
||||||
# Reverse mode configure the box as a storage device
|
|
||||||
# full is bad, empty is good
|
|
||||||
elif mode == COLOR_MODE_REVERSE:
|
|
||||||
self._color_status_high = [1,0,0]
|
|
||||||
self._color_status_medium = [1,1,0]
|
|
||||||
self._color_status_low = [0, 1, 0]
|
|
||||||
self._limit_high = 85
|
|
||||||
self._limit_medium = 40
|
|
||||||
|
|
||||||
def _draw_content(self, context, percent):
|
|
||||||
usage_height = (percent*self._height)/100
|
|
||||||
context.rectangle(0, self._height - usage_height, self._width, self._height)
|
|
||||||
|
|
||||||
if self._percent > self._limit_high:
|
|
||||||
context.set_source_rgb(*self._color_status_high)
|
|
||||||
elif self._percent >= self._limit_medium and self._percent <= self._limit_high:
|
|
||||||
context.set_source_rgb(*self._color_status_medium)
|
|
||||||
elif self._percent < self._limit_medium:
|
|
||||||
context.set_source_rgb(*self._color_status_low)
|
|
||||||
|
|
||||||
context.fill_preserve()
|
|
||||||
|
|
||||||
def _change_size_cb(self, widget, allocation):
|
|
||||||
self._width = allocation.width
|
|
||||||
self._height = allocation.height
|
|
||||||
self.queue_draw()
|
|
@ -1,147 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva (edsiper@gmail.com).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
|
|
||||||
class HorizontalGraphic(gtk.DrawingArea):
|
|
||||||
_MARGIN = 5
|
|
||||||
_LINE_WIDTH = 2
|
|
||||||
_GRAPH_OFFSET = 7
|
|
||||||
_range_x = []
|
|
||||||
_range_y = []
|
|
||||||
_frequency_timer = 0
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
gtk.DrawingArea.__init__(self)
|
|
||||||
self._width = 0
|
|
||||||
self._height = 0
|
|
||||||
self._buffer = [0]
|
|
||||||
self.connect('expose-event', self.do_expose)
|
|
||||||
self.connect('size-allocate', self._change_size_cb)
|
|
||||||
|
|
||||||
def do_expose(self, widget, event):
|
|
||||||
context = widget.window.cairo_create()
|
|
||||||
context.rectangle(0, 0, self._width - 1, self._height - 1)
|
|
||||||
context.set_source_rgb (0,0,0)
|
|
||||||
context.fill_preserve()
|
|
||||||
context.set_line_width(self._LINE_WIDTH)
|
|
||||||
|
|
||||||
if event.area.x == 0:
|
|
||||||
draw_all = True
|
|
||||||
self._draw_border_lines(context)
|
|
||||||
context.stroke()
|
|
||||||
else:
|
|
||||||
draw_all = False
|
|
||||||
context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
|
|
||||||
context.clip()
|
|
||||||
|
|
||||||
context.set_source_rgb(1, 1, 1)
|
|
||||||
self._draw_buffer(event, widget, context, draw_all)
|
|
||||||
context.stroke()
|
|
||||||
|
|
||||||
self._updated = False
|
|
||||||
return False
|
|
||||||
|
|
||||||
def draw_value(self, percent):
|
|
||||||
redraw_all = False
|
|
||||||
|
|
||||||
if (len(self._buffer) + 1) *self._GRAPH_OFFSET >= self._width:
|
|
||||||
redraw_all = True
|
|
||||||
self._buffer = [self._buffer[-1]]
|
|
||||||
length = 1
|
|
||||||
else:
|
|
||||||
length = len(self._buffer) - 1
|
|
||||||
|
|
||||||
self._buffer.append(percent)
|
|
||||||
self._updated = True
|
|
||||||
|
|
||||||
if redraw_all:
|
|
||||||
area_x = 0
|
|
||||||
area_y = 0
|
|
||||||
height = self._height
|
|
||||||
width = self._width
|
|
||||||
else:
|
|
||||||
area_x = self._graph_x + (length*self._GRAPH_OFFSET)
|
|
||||||
area_y = self._graph_y
|
|
||||||
width = self._GRAPH_OFFSET*2
|
|
||||||
height = self._graph_height
|
|
||||||
|
|
||||||
self.queue_draw_area(area_x, area_y, width, height)
|
|
||||||
self._frequency_timer += 1
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _draw_border_lines(self, context):
|
|
||||||
context.set_source_rgb(1, 1, 1)
|
|
||||||
self._draw_line(context, self._MARGIN, self._MARGIN, self._MARGIN, self._height - self._MARGIN)
|
|
||||||
self._draw_line(context, self._MARGIN, self._height - self._MARGIN - 1, self._width - self._MARGIN, self._height - self._MARGIN - 1)
|
|
||||||
|
|
||||||
def _draw_line(self, context, from_x, from_y, to_x, to_y):
|
|
||||||
context.move_to(from_x, from_y)
|
|
||||||
context.line_to(to_x, to_y)
|
|
||||||
|
|
||||||
def _draw_buffer(self, event, drwarea, context, draw_all=True):
|
|
||||||
buffer_offset = 0
|
|
||||||
freq = 1 # Frequency timer
|
|
||||||
|
|
||||||
length = len(self._buffer)
|
|
||||||
|
|
||||||
if length == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Context properties
|
|
||||||
context.set_line_width(self._LINE_WIDTH)
|
|
||||||
context.set_source_rgb(0,1,0)
|
|
||||||
|
|
||||||
if draw_all == True:
|
|
||||||
buffer_offset = 0
|
|
||||||
freq = 0
|
|
||||||
else:
|
|
||||||
freq = buffer_offset = (event.area.x/self._GRAPH_OFFSET)
|
|
||||||
|
|
||||||
for percent in self._buffer[buffer_offset:length]:
|
|
||||||
if buffer_offset == 0:
|
|
||||||
from_y = self._get_y(self._buffer[0])
|
|
||||||
from_x = self._graph_x
|
|
||||||
else:
|
|
||||||
from_y = self._get_y(self._buffer[buffer_offset-1])
|
|
||||||
from_x = (freq * self._GRAPH_OFFSET)
|
|
||||||
|
|
||||||
to_x = (freq+1) * self._GRAPH_OFFSET
|
|
||||||
to_y = self._get_y(percent)
|
|
||||||
|
|
||||||
self._draw_line(context, from_x, from_y, to_x, to_y)
|
|
||||||
buffer_offset+=1
|
|
||||||
freq+=1
|
|
||||||
|
|
||||||
context.stroke()
|
|
||||||
|
|
||||||
def _get_y(self, percent):
|
|
||||||
if percent==0:
|
|
||||||
percent = 1
|
|
||||||
|
|
||||||
graph_y = ((self._height)/(100 - 1))*(percent - 1)
|
|
||||||
y = self._LINE_WIDTH + abs(abs(self._height - graph_y) - self._MARGIN*2)
|
|
||||||
return int(y)
|
|
||||||
|
|
||||||
def _change_size_cb(self, widget, allocation):
|
|
||||||
self._width = allocation.width
|
|
||||||
self._height = allocation.height
|
|
||||||
|
|
||||||
self._graph_x = self._MARGIN + self._LINE_WIDTH
|
|
||||||
self._graph_y = self._MARGIN
|
|
||||||
self._graph_width = self._width - (self._MARGIN*2 + self._LINE_WIDTH)
|
|
||||||
self._graph_height = self._height - ((self._MARGIN*2 + self._LINE_WIDTH))
|
|
@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/lib/net
|
|
||||||
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
device.py
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import fcntl
|
|
||||||
import struct
|
|
||||||
import string
|
|
||||||
|
|
||||||
class Device:
|
|
||||||
def __init__(self):
|
|
||||||
self._dev = self.get_interfaces()
|
|
||||||
|
|
||||||
def get_interfaces(self):
|
|
||||||
netdevfile = "/proc/net/dev"
|
|
||||||
dev = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
infile = file(netdevfile, "r")
|
|
||||||
except:
|
|
||||||
print "Error trying " + netdevfile
|
|
||||||
|
|
||||||
skip = 0
|
|
||||||
for line in infile:
|
|
||||||
# Skip first two lines
|
|
||||||
skip += 1
|
|
||||||
if skip <= 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
iface = string.split(line, ":",1)
|
|
||||||
arr = string.split(iface[1])
|
|
||||||
|
|
||||||
if len(arr) < 10:
|
|
||||||
continue
|
|
||||||
|
|
||||||
info = {'interface': iface[0].strip(), \
|
|
||||||
'bytes_recv': arr[0],\
|
|
||||||
'bytes_sent': arr[8],\
|
|
||||||
'packets_recv': arr[1],
|
|
||||||
'packets_sent': arr[9]}
|
|
||||||
|
|
||||||
dev.append(info)
|
|
||||||
return dev
|
|
||||||
|
|
||||||
def get_iface_info(self, ifname):
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
hwaddr = []
|
|
||||||
try:
|
|
||||||
ip = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, \
|
|
||||||
struct.pack('256s', ifname[:15]))[20:24])
|
|
||||||
except:
|
|
||||||
ip = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
netmask = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x891b, \
|
|
||||||
struct.pack('256s', ifname[:15]))[20:24])
|
|
||||||
except:
|
|
||||||
netmask = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
mac = []
|
|
||||||
info = fcntl.ioctl(s.fileno(), 0x8927, \
|
|
||||||
struct.pack('256s', ifname[:15]))
|
|
||||||
for char in info[18:24]:
|
|
||||||
hdigit = hex(ord(char))[2:]
|
|
||||||
if len(hdigit):
|
|
||||||
mac.append(hdigit)
|
|
||||||
except:
|
|
||||||
mac = None
|
|
||||||
|
|
||||||
mac_string = self.mac_to_string(mac)
|
|
||||||
return [ip, netmask, mac_string]
|
|
||||||
|
|
||||||
def mac_to_string(self, hexa):
|
|
||||||
string = ''
|
|
||||||
for value in hexa:
|
|
||||||
if len(string)==0:
|
|
||||||
string = value
|
|
||||||
else:
|
|
||||||
string += ':'+value
|
|
||||||
|
|
||||||
return string
|
|
@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
sugardir = $(pkgdatadir)/services/console/lib/procmem
|
|
||||||
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
proc.py \
|
|
||||||
proc_smaps.py \
|
|
||||||
analysis.py
|
|
@ -1,32 +0,0 @@
|
|||||||
import proc, proc_smaps
|
|
||||||
|
|
||||||
class Analysis:
|
|
||||||
|
|
||||||
pid = 0
|
|
||||||
|
|
||||||
def __init__(self, pid):
|
|
||||||
self.pid = pid
|
|
||||||
|
|
||||||
def SMaps(self):
|
|
||||||
smaps = proc_smaps.ProcSmaps(self.pid)
|
|
||||||
private_dirty = 0
|
|
||||||
shared_dirty = 0
|
|
||||||
referenced = 0
|
|
||||||
|
|
||||||
for map in smaps.mappings:
|
|
||||||
private_dirty += map.private_dirty
|
|
||||||
shared_dirty += map.shared_dirty
|
|
||||||
referenced += map.referenced
|
|
||||||
|
|
||||||
smaps = {"private_dirty": int(private_dirty), \
|
|
||||||
"shared_dirty": int(shared_dirty),\
|
|
||||||
"referenced": int(referenced)}
|
|
||||||
|
|
||||||
return smaps
|
|
||||||
|
|
||||||
def ApproxRealMemoryUsage(self):
|
|
||||||
maps = proc_smaps.ProcMaps(self.pid)
|
|
||||||
size = (maps.clean_size/1024)
|
|
||||||
|
|
||||||
return size
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
|||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
|
|
||||||
class ProcInfo:
|
|
||||||
|
|
||||||
dir_path = "/proc/" # Our cute Proc File System
|
|
||||||
status_file = "status"
|
|
||||||
stat_file = "stat"
|
|
||||||
|
|
||||||
proc_list = [] # Our PID list :D
|
|
||||||
proc_info = [] #
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.proc_list = self.Get_PID_List()
|
|
||||||
|
|
||||||
# Returns Process List
|
|
||||||
def Get_PID_List(self):
|
|
||||||
list = []
|
|
||||||
|
|
||||||
# Exists our procfs ?
|
|
||||||
if os.path.isdir(self.dir_path):
|
|
||||||
# around dir entries
|
|
||||||
for f in os.listdir(self.dir_path):
|
|
||||||
if os.path.isdir(self.dir_path+f) & str.isdigit(f):
|
|
||||||
list.append(int(f))
|
|
||||||
|
|
||||||
return list
|
|
||||||
|
|
||||||
def MemoryInfo(self, pid):
|
|
||||||
# Path
|
|
||||||
pidfile = self.dir_path + str(pid) + "/stat"
|
|
||||||
try:
|
|
||||||
infile = open(pidfile, "r")
|
|
||||||
except:
|
|
||||||
print "Error trying " + pidfile
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Parsing data , check 'man 5 proc' for details
|
|
||||||
stat_data = infile.read()
|
|
||||||
infile.close()
|
|
||||||
|
|
||||||
process_name = self._get_process_name(stat_data)
|
|
||||||
data = self._get_safe_split(stat_data)
|
|
||||||
|
|
||||||
state_dic = {
|
|
||||||
'R': 'Running',
|
|
||||||
'S': 'Sleeping',
|
|
||||||
'D': 'Disk sleep',
|
|
||||||
'Z': 'Zombie',
|
|
||||||
'T': 'Traced/Stopped',
|
|
||||||
'W': 'Paging'
|
|
||||||
}
|
|
||||||
|
|
||||||
# user and group owners
|
|
||||||
pidstat = os.stat(pidfile)
|
|
||||||
info = {
|
|
||||||
'pid': int(data[0]), # Process ID
|
|
||||||
'name': process_name,
|
|
||||||
'state': data[2], # Process State, ex: R|S|D|Z|T|W
|
|
||||||
'state_name': state_dic[data[2]], # Process State name, ex: Running, sleeping, Zombie, etc
|
|
||||||
'ppid': int(data[3]), # Parent process ID
|
|
||||||
'utime': int(data[13]), # Used jiffies in user mode
|
|
||||||
'stime': int(data[14]), # Used jiffies in kernel mode
|
|
||||||
'start_time': int(data[21]), # Process time from system boot (jiffies)
|
|
||||||
'vsize': int(data[22]), # Virtual memory size used (bytes)
|
|
||||||
'rss': int(data[23])*4, # Resident Set Size (bytes)
|
|
||||||
'user_id': pidstat.st_uid, # process owner
|
|
||||||
'group_id': pidstat.st_gid # owner group
|
|
||||||
}
|
|
||||||
|
|
||||||
return info
|
|
||||||
|
|
||||||
# Return the process name
|
|
||||||
def _get_process_name(self, data):
|
|
||||||
m = re.search("\(.*\)", data)
|
|
||||||
return m.string[m.start()+1:m.end()-1]
|
|
||||||
|
|
||||||
def _get_safe_split(self, data):
|
|
||||||
new_data = re.sub("\(.*\)", '()', data)
|
|
||||||
return new_data.split()
|
|
||||||
|
|
||||||
# Returns the CPU usage expressed in Jiffies
|
|
||||||
def get_CPU_usage(self, cpu_hz, used_jiffies, start_time):
|
|
||||||
|
|
||||||
# Uptime info
|
|
||||||
uptime_file = self.dir_path + "/uptime"
|
|
||||||
try:
|
|
||||||
infile = file(uptime_file, "r")
|
|
||||||
except:
|
|
||||||
print "Error trying uptime file"
|
|
||||||
return None
|
|
||||||
|
|
||||||
uptime_line = infile.readline()
|
|
||||||
uptime = string.split(uptime_line, " ",2)
|
|
||||||
|
|
||||||
infile.close()
|
|
||||||
|
|
||||||
# System uptime, from /proc/uptime
|
|
||||||
uptime = float(uptime[0])
|
|
||||||
|
|
||||||
# Jiffies
|
|
||||||
avail_jiffies = (uptime * cpu_hz) - start_time
|
|
||||||
|
|
||||||
cpu_usage = {'used_jiffies': used_jiffies, 'avail_jiffies': avail_jiffies}
|
|
||||||
|
|
||||||
return cpu_usage
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
|||||||
####################################################################
|
|
||||||
# This class open the /proc/PID/maps and /proc/PID/smaps files
|
|
||||||
# to get useful information about the real memory usage
|
|
||||||
####################################################################
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Parse the /proc/PID/smaps file
|
|
||||||
class ProcSmaps:
|
|
||||||
|
|
||||||
mappings = [] # Devices information
|
|
||||||
|
|
||||||
def __init__(self, pid):
|
|
||||||
|
|
||||||
smapfile = "/proc/%s/smaps" % pid
|
|
||||||
self.mappings = []
|
|
||||||
|
|
||||||
# Coded by Federico Mena (script)
|
|
||||||
try:
|
|
||||||
infile = open(smapfile, "r")
|
|
||||||
input = infile.read()
|
|
||||||
infile.close()
|
|
||||||
except:
|
|
||||||
print "Error trying " + smapfile
|
|
||||||
return
|
|
||||||
|
|
||||||
lines = input.splitlines()
|
|
||||||
|
|
||||||
num_lines = len (lines)
|
|
||||||
line_idx = 0
|
|
||||||
|
|
||||||
# 08065000-08067000 rw-p 0001c000 03:01 147613 /opt/gnome/bin/evolution-2.6
|
|
||||||
# Size: 8 kB
|
|
||||||
# Rss: 8 kB
|
|
||||||
# Shared_Clean: 0 kB
|
|
||||||
# Shared_Dirty: 0 kB
|
|
||||||
# Private_Clean: 8 kB
|
|
||||||
# Private_Dirty: 0 kB
|
|
||||||
# Referenced: 4 kb -> Introduced in kernel 2.6.22
|
|
||||||
|
|
||||||
while num_lines > 0:
|
|
||||||
fields = lines[line_idx].split (" ", 5)
|
|
||||||
if len (fields) == 6:
|
|
||||||
(offsets, permissions, bin_permissions, device, inode, name) = fields
|
|
||||||
else:
|
|
||||||
(offsets, permissions, bin_permissions, device, inode) = fields
|
|
||||||
name = ""
|
|
||||||
|
|
||||||
size = self.parse_smaps_size_line (lines[line_idx + 1])
|
|
||||||
rss = self.parse_smaps_size_line (lines[line_idx + 2])
|
|
||||||
shared_clean = self.parse_smaps_size_line (lines[line_idx + 3])
|
|
||||||
shared_dirty = self.parse_smaps_size_line (lines[line_idx + 4])
|
|
||||||
private_clean = self.parse_smaps_size_line (lines[line_idx + 5])
|
|
||||||
private_dirty = self.parse_smaps_size_line (lines[line_idx + 6])
|
|
||||||
referenced = self.parse_smaps_size_line (lines[line_idx + 7])
|
|
||||||
name = name.strip ()
|
|
||||||
|
|
||||||
mapping = Mapping (size, rss, shared_clean, shared_dirty, \
|
|
||||||
private_clean, private_dirty, referenced, permissions, name)
|
|
||||||
self.mappings.append (mapping)
|
|
||||||
|
|
||||||
num_lines -= 8
|
|
||||||
line_idx += 8
|
|
||||||
|
|
||||||
self._clear_reference(pid)
|
|
||||||
|
|
||||||
def _clear_reference(self, pid):
|
|
||||||
os.system("echo 1 > /proc/%s/clear_refs" % pid)
|
|
||||||
|
|
||||||
# Parses a line of the form "foo: 42 kB" and returns an integer for the "42" field
|
|
||||||
def parse_smaps_size_line (self, line):
|
|
||||||
# Rss: 8 kB
|
|
||||||
fields = line.split ()
|
|
||||||
return int(fields[1])
|
|
||||||
|
|
||||||
class Mapping:
|
|
||||||
def __init__ (self, size, rss, shared_clean, shared_dirty, \
|
|
||||||
private_clean, private_dirty, referenced, permissions, name):
|
|
||||||
self.size = size
|
|
||||||
self.rss = rss
|
|
||||||
self.shared_clean = shared_clean
|
|
||||||
self.shared_dirty = shared_dirty
|
|
||||||
self.private_clean = private_clean
|
|
||||||
self.private_dirty = private_dirty
|
|
||||||
self.referenced = referenced
|
|
||||||
self.permissions = permissions
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
# Parse /proc/PID/maps file to get the clean memory usage by process,
|
|
||||||
# we avoid lines with backed-files
|
|
||||||
class ProcMaps:
|
|
||||||
|
|
||||||
clean_size = 0
|
|
||||||
|
|
||||||
def __init__(self, pid):
|
|
||||||
mapfile = "/proc/%s/maps" % pid
|
|
||||||
|
|
||||||
try:
|
|
||||||
infile = open(mapfile, "r")
|
|
||||||
except:
|
|
||||||
print "Error trying " + mapfile
|
|
||||||
return None
|
|
||||||
|
|
||||||
sum = 0
|
|
||||||
to_data_do = {
|
|
||||||
"[anon]": self.parse_size_line,
|
|
||||||
"[heap]": self.parse_size_line
|
|
||||||
}
|
|
||||||
|
|
||||||
for line in infile:
|
|
||||||
arr = line.split()
|
|
||||||
|
|
||||||
# Just parse writable mapped areas
|
|
||||||
if arr[1][1] != "w":
|
|
||||||
continue
|
|
||||||
|
|
||||||
if len(arr) == 6:
|
|
||||||
# if we got a backed-file we skip this info
|
|
||||||
if os.path.isfile(arr[5]):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
line_size = to_data_do.get(arr[5], self.skip)(line)
|
|
||||||
sum += line_size
|
|
||||||
else:
|
|
||||||
line_size = self.parse_size_line(line)
|
|
||||||
sum += line_size
|
|
||||||
|
|
||||||
infile.close()
|
|
||||||
self.clean_size = sum
|
|
||||||
|
|
||||||
def skip(self, line):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# Parse a maps line and return the mapped size
|
|
||||||
def parse_size_line(self, line):
|
|
||||||
start, end = line.split()[0].split('-')
|
|
||||||
size = int(end, 16) - int(start, 16)
|
|
||||||
return size
|
|
@ -1,256 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
from ctypes import *
|
|
||||||
|
|
||||||
# XText Property
|
|
||||||
class _XTextProperty(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
_XTextProperty._fields_ = [("value", c_char_p),\
|
|
||||||
("encoding", c_ulong),\
|
|
||||||
("format", c_int),\
|
|
||||||
("nitems", c_ulong)]
|
|
||||||
|
|
||||||
# XResType Structure
|
|
||||||
class _XResTypeStruct(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
_XResTypeStruct._fields_ = [("resource_type", c_ulong),\
|
|
||||||
("count", c_uint)]
|
|
||||||
|
|
||||||
_ATOMNAMES = ["PIXMAP",\
|
|
||||||
"WINDOW",\
|
|
||||||
"GC",\
|
|
||||||
"FONT",\
|
|
||||||
"GLYPHSET",\
|
|
||||||
"PICTURE",\
|
|
||||||
"COLORMAP ENTRY",\
|
|
||||||
"PASSIVE GRAB",\
|
|
||||||
"CURSOR",\
|
|
||||||
"_NET_CLIENT_LIST",\
|
|
||||||
"_NET_WM_PID",\
|
|
||||||
"_NET_WM_NAME",\
|
|
||||||
"UTF8_STRING",\
|
|
||||||
"WM_NAME", # FIXME!
|
|
||||||
"CARDINAL" # FIXME!
|
|
||||||
]
|
|
||||||
|
|
||||||
_ATOM_PIXMAP = 0
|
|
||||||
_ATOM_WINDOW = 1
|
|
||||||
_ATOM_GC = 2
|
|
||||||
_ATOM_FONT = 3
|
|
||||||
_ATOM_GLYPHSET = 4
|
|
||||||
_ATOM_PICTURE = 5
|
|
||||||
_ATOM_COLORMAP_ENTRY = 6
|
|
||||||
_ATOM_PASSIVE_GRAB = 7
|
|
||||||
_ATOM_CURSOR = 8
|
|
||||||
_ATOM_NET_CLIENT_LIST = 9
|
|
||||||
_ATOM_NET_WM_PID = 10
|
|
||||||
_ATOM_NET_WM_NAME = 11
|
|
||||||
_ATOM_UTF8_STRING = 12
|
|
||||||
_ATOM_WM_NAME = 13
|
|
||||||
_ATOM_CARDINAL = 14
|
|
||||||
|
|
||||||
# XText Property
|
|
||||||
class _XTextProperty(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
_XTextProperty._fields_ = [("value", c_char_p),\
|
|
||||||
("encoding", c_ulong),\
|
|
||||||
("format", c_int),\
|
|
||||||
("nitems", c_ulong)]
|
|
||||||
|
|
||||||
class XRes(object):
|
|
||||||
_XRESLIB = "libXRes.so"
|
|
||||||
_XMULIB = "libXmu.so.6"
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._lib = CDLL(self._XRESLIB)
|
|
||||||
self._lib_xmu = CDLL(self._XMULIB)
|
|
||||||
|
|
||||||
def _set_atoms(self, display):
|
|
||||||
self.atoms = []
|
|
||||||
for atom in _ATOMNAMES:
|
|
||||||
atom_value = self._lib.XInternAtom(display, atom, True)
|
|
||||||
self.atoms.append(atom_value)
|
|
||||||
|
|
||||||
def open_display(self, display=None):
|
|
||||||
display = self._lib.XOpenDisplay(display)
|
|
||||||
self._set_atoms(display)
|
|
||||||
return display
|
|
||||||
|
|
||||||
# Return an array with XRestTypes:
|
|
||||||
#
|
|
||||||
# XResType.resource_type (Atom_type)
|
|
||||||
# XResTyoe.count
|
|
||||||
def get_resources(self, display, resource_base):
|
|
||||||
n_types = c_long()
|
|
||||||
types = pointer(_XResTypeStruct())
|
|
||||||
self._lib.XResQueryClientResources(display, resource_base, \
|
|
||||||
byref(n_types), byref(types))
|
|
||||||
|
|
||||||
pytypes = []
|
|
||||||
for t in types[:n_types.value]:
|
|
||||||
pytypes.append(t)
|
|
||||||
|
|
||||||
return pytypes
|
|
||||||
|
|
||||||
def get_windows(self, display):
|
|
||||||
self._windows = []
|
|
||||||
root = self._lib.XDefaultRootWindow(display)
|
|
||||||
self._lookat(display, root)
|
|
||||||
return self._windows
|
|
||||||
|
|
||||||
def _lookat(self, display, win_root):
|
|
||||||
wp = self._get_window_properties (display, win_root)
|
|
||||||
|
|
||||||
if wp:
|
|
||||||
self._windows.append(wp)
|
|
||||||
|
|
||||||
w = None
|
|
||||||
dummy = self._Window()
|
|
||||||
children = self._Window()
|
|
||||||
nchildren = c_uint()
|
|
||||||
|
|
||||||
r = self._lib.XQueryTree(display, win_root, byref(dummy), \
|
|
||||||
byref(dummy), byref(children), byref(nchildren))
|
|
||||||
|
|
||||||
for client in children[:nchildren.value]:
|
|
||||||
cli = self._lib_xmu.XmuClientWindow (display, client)
|
|
||||||
if client is not None:
|
|
||||||
wp = self._get_window_properties (display, cli)
|
|
||||||
if wp:
|
|
||||||
self._windows.append(wp)
|
|
||||||
|
|
||||||
def _get_window_properties(self, display, w):
|
|
||||||
cliargv = c_char_p()
|
|
||||||
cliargc = c_long()
|
|
||||||
machtp = pointer(_XTextProperty())
|
|
||||||
nametp = _XTextProperty()
|
|
||||||
w_name = None
|
|
||||||
|
|
||||||
if not self._lib.XGetWMClientMachine (display, w, byref(machtp)):
|
|
||||||
machtp.value = None
|
|
||||||
machtp.encoding = None
|
|
||||||
|
|
||||||
if not self._lib.XGetCommand(display, w, byref(cliargv), byref(cliargc)):
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._lib.XGetWMName(display, w, byref(nametp)):
|
|
||||||
w_name = nametp.value
|
|
||||||
|
|
||||||
bytes = c_ulong()
|
|
||||||
self._lib.XResQueryClientPixmapBytes(display, w, byref(bytes))
|
|
||||||
w_pixmaps = bytes.value
|
|
||||||
|
|
||||||
type = self._Atom()
|
|
||||||
format = c_int()
|
|
||||||
n_items = c_ulong()
|
|
||||||
bytes_after = c_int()
|
|
||||||
w_pid = pointer(c_long())
|
|
||||||
wname = c_char_p()
|
|
||||||
|
|
||||||
self._lib.XGetWindowProperty(display, w,\
|
|
||||||
self.atoms[_ATOM_NET_WM_PID],
|
|
||||||
0, 2L,\
|
|
||||||
False, self.atoms[_ATOM_CARDINAL],\
|
|
||||||
byref(type), byref(format), \
|
|
||||||
byref(n_items), byref(bytes_after), \
|
|
||||||
byref(w_pid))
|
|
||||||
|
|
||||||
|
|
||||||
# Calc aditional X resources by window
|
|
||||||
res = self.get_resources(display, w)
|
|
||||||
|
|
||||||
n_windows = 0
|
|
||||||
n_gcs = 0
|
|
||||||
n_pictures = 0
|
|
||||||
n_glyphsets = 0
|
|
||||||
n_fonts = 0
|
|
||||||
n_colormaps = 0
|
|
||||||
n_passive_grabs = 0
|
|
||||||
n_cursors = 0
|
|
||||||
n_other = 0
|
|
||||||
|
|
||||||
for r in res:
|
|
||||||
if r.resource_type == self.atoms[_ATOM_WINDOW]:
|
|
||||||
n_windows += r.count
|
|
||||||
elif r.resource_type == self.atoms[_ATOM_GC]:
|
|
||||||
n_gcs += r.count
|
|
||||||
elif r.resource_type == self.atoms[_ATOM_PICTURE]:
|
|
||||||
n_pictures += r.count
|
|
||||||
elif r.resource_type == self.atoms[_ATOM_GLYPHSET]:
|
|
||||||
n_glyphsets += r.count
|
|
||||||
elif r.resource_type == self.atoms[_ATOM_FONT]:
|
|
||||||
n_fonts += r.count
|
|
||||||
elif r.resource_type == self.atoms[_ATOM_COLORMAP_ENTRY]:
|
|
||||||
n_colormaps += r.count
|
|
||||||
elif r.resource_type == self.atoms[_ATOM_PASSIVE_GRAB]:
|
|
||||||
n_passive_grabs += r.count
|
|
||||||
elif r.resource_type == self.atoms[_ATOM_CURSOR]:
|
|
||||||
n_cursors += r.count
|
|
||||||
else:
|
|
||||||
n_other += r.count
|
|
||||||
|
|
||||||
other_bytes = n_windows * 24
|
|
||||||
other_bytes += n_gcs * 24
|
|
||||||
other_bytes += n_pictures * 24
|
|
||||||
other_bytes += n_glyphsets * 24
|
|
||||||
other_bytes += n_fonts * 1024
|
|
||||||
other_bytes += n_colormaps * 24
|
|
||||||
other_bytes += n_passive_grabs * 24
|
|
||||||
other_bytes += n_cursors * 24
|
|
||||||
other_bytes += n_other * 24
|
|
||||||
|
|
||||||
window = Window(w, w_pid.contents.value, w_pixmaps, \
|
|
||||||
n_windows, n_gcs, n_fonts, n_glyphsets, n_pictures,\
|
|
||||||
n_colormaps, n_passive_grabs, n_cursors, n_other,\
|
|
||||||
other_bytes, w_name)
|
|
||||||
|
|
||||||
return window
|
|
||||||
|
|
||||||
# Data types
|
|
||||||
def _Window(self):
|
|
||||||
return pointer(c_ulong())
|
|
||||||
|
|
||||||
def _Atom(self, data=0):
|
|
||||||
return pointer(c_ulong(data))
|
|
||||||
|
|
||||||
class Window(object):
|
|
||||||
def __init__(self, resource_base, pid, pixmap_bytes=0,\
|
|
||||||
n_windows=0, n_gcs=0, n_fonts=0, n_glyphsets=0, n_pictures=0,\
|
|
||||||
n_colormaps=0, n_passive_grabs=0, n_cursors=0, n_other=0,\
|
|
||||||
other_bytes=0, wm_name=None):
|
|
||||||
|
|
||||||
self.resource_base = resource_base
|
|
||||||
self.pid = pid
|
|
||||||
self.pixmap_bytes = pixmap_bytes
|
|
||||||
|
|
||||||
self.n_windows = n_windows
|
|
||||||
self.n_gcs = n_gcs
|
|
||||||
self.n_fonts = n_fonts
|
|
||||||
self.n_glyphsets = n_glyphsets
|
|
||||||
self.n_pictures = n_pictures
|
|
||||||
self.n_colormaps = n_colormaps
|
|
||||||
self.n_passive_grabs = n_passive_grabs
|
|
||||||
self.n_cursors = n_cursors
|
|
||||||
self.n_other = n_other
|
|
||||||
|
|
||||||
self.other_bytes = other_bytes
|
|
||||||
self.wm_name = wm_name
|
|
@ -1,5 +0,0 @@
|
|||||||
sugardir = $(pkgdatadir)/services/console/lib/ui
|
|
||||||
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
treeview.py
|
|
@ -1,73 +0,0 @@
|
|||||||
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
|
|
||||||
class TreeView(gtk.ScrolledWindow):
|
|
||||||
iters = [] # Iters index
|
|
||||||
|
|
||||||
# Create a window with a treeview object
|
|
||||||
#
|
|
||||||
# cols = List of dicts, ex:
|
|
||||||
#
|
|
||||||
# cols = []
|
|
||||||
# cols.append({'index': integer_index_position, 'name': string_col_name})
|
|
||||||
def __init__(self, cols_def, cols_name):
|
|
||||||
gtk.ScrolledWindow.__init__(self)
|
|
||||||
|
|
||||||
self._iters = []
|
|
||||||
self._treeview = gtk.TreeView()
|
|
||||||
|
|
||||||
# Creating column data types
|
|
||||||
self._store = gtk.TreeStore(*cols_def)
|
|
||||||
|
|
||||||
# Columns definition
|
|
||||||
cell = gtk.CellRendererText()
|
|
||||||
tv_cols = []
|
|
||||||
|
|
||||||
i=0
|
|
||||||
for col in cols_name:
|
|
||||||
col_tv = gtk.TreeViewColumn(col['name'], cell, text=i)
|
|
||||||
col_tv.set_reorderable(True)
|
|
||||||
col_tv.set_resizable(True)
|
|
||||||
tv_cols.append(col_tv)
|
|
||||||
i+=1
|
|
||||||
|
|
||||||
# Setting treeview properties
|
|
||||||
self._treeview.set_model(self._store)
|
|
||||||
self._treeview.set_enable_search(True)
|
|
||||||
self._treeview.set_rules_hint(True)
|
|
||||||
|
|
||||||
for col in tv_cols:
|
|
||||||
self._treeview.append_column(col)
|
|
||||||
self.add(self._treeview)
|
|
||||||
|
|
||||||
def add_row(self, cols_data):
|
|
||||||
iter = self._store.insert_after(None, None)
|
|
||||||
for col in cols_data:
|
|
||||||
print col['index'],col['info']
|
|
||||||
self._store.set_value(iter, int(col['index']) , col['info'])
|
|
||||||
|
|
||||||
self.iters.append(iter)
|
|
||||||
return iter
|
|
||||||
|
|
||||||
def update_row(self, iter, cols_data):
|
|
||||||
for col in cols_data:
|
|
||||||
self._store.set_value(iter, int(col['index']) , str(col['info']))
|
|
||||||
|
|
||||||
def remove_row(self, iter):
|
|
||||||
self._store.remove(iter)
|
|
@ -1,4 +0,0 @@
|
|||||||
[D-BUS Service]
|
|
||||||
Name = org.laptop.sugar.Console
|
|
||||||
Exec = @bindir@/sugar-console
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import pygtk
|
|
||||||
pygtk.require('2.0')
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from sugar import env
|
|
||||||
from sugar import util
|
|
||||||
|
|
||||||
sys.path.append(env.get_service_path('console'))
|
|
||||||
|
|
||||||
# change to the user's home directory if it is set
|
|
||||||
# root if not
|
|
||||||
os.chdir(os.environ.get('HOME', '/'))
|
|
||||||
|
|
||||||
#set the process title so it shows up as sugar-console not python
|
|
||||||
util.set_proc_title('sugar-console')
|
|
||||||
|
|
||||||
import console
|
|
428
shell/controlpanel/control.py
Normal file
428
shell/controlpanel/control.py
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
# Copyright (C) 2007, One Laptop Per Child
|
||||||
|
#
|
||||||
|
# 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 language config is based on the system-config-language
|
||||||
|
# (http://fedoraproject.org/wiki/SystemConfig/language) tool
|
||||||
|
# and the timezone config on the system-config-date
|
||||||
|
# (http://fedoraproject.org/wiki/SystemConfig/date) tool.
|
||||||
|
# Parts of the code were reused.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import string
|
||||||
|
import shutil
|
||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
from sugar import profile
|
||||||
|
from sugar.graphics.xocolor import XoColor
|
||||||
|
|
||||||
|
|
||||||
|
_COLORS = {'red': {'dark':'#b20008', 'medium':'#e6000a', 'light':'#ffadce'},
|
||||||
|
'orange': {'dark':'#9a5200', 'medium':'#c97e00', 'light':'#ffc169'},
|
||||||
|
'yellow': {'dark':'#807500', 'medium':'#be9e00', 'light':'#fffa00'},
|
||||||
|
'green': {'dark':'#008009', 'medium':'#00b20d', 'light':'#8bff7a'},
|
||||||
|
'blue': {'dark':'#00588c', 'medium':'#005fe4', 'light':'#bccdff'},
|
||||||
|
'purple': {'dark':'#5e008c', 'medium':'#7f00bf', 'light':'#d1a3ff'}
|
||||||
|
}
|
||||||
|
|
||||||
|
_MODIFIERS = ('dark', 'medium', 'light')
|
||||||
|
|
||||||
|
_TIMEZONE_CONFIG = '/etc/sysconfig/clock'
|
||||||
|
|
||||||
|
_LANGUAGES = {
|
||||||
|
'Afrikaans/South_Africa': ('af_ZA', 'lat0-sun16'),
|
||||||
|
'Albanian': ('sq_AL.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Algeria': ('ar_DZ.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Bahrain': ('ar_BH.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Egypt': ('ar_EG.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/India': ('ar_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Iraq': ('ar_IQ.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Jordan': ('ar_JO.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Kuwait': ('ar_KW.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Lebanon': ('ar_LB.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Libyan_Arab_Jamahiriya': ('ar_LY.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Morocco': ('ar_MA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Oman': ('ar_OM.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Qatar': ('ar_QA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Saudi_Arabia': ('ar_SA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Sudan': ('ar_SD.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Syrian_Arab_Republic': ('ar_SY.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Tunisia': ('ar_TN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/United_Arab_Emirates': ('ar_AE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Arabic/Yemen': ('ar_YE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Basque/Spain': ('eu_ES.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Belarusian': ('be_BY.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Bengali/BD': ('bn_BD.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Bengali/India': ('bn_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Bosnian/Bosnia_and_Herzegowina': ('bs_BA', 'lat2-sun16'),
|
||||||
|
'Breton/France': ('br_FR', 'lat0-sun16'),
|
||||||
|
'Bulgarian': ('bg_BG.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Catalan/Spain': ('ca_ES.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Chinese/Hong_Kong': ('zh_HK.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Chinese/P.R._of_China': ('zh_CN.UTF-8', 'lat0-sun16'),
|
||||||
|
'Chinese/Taiwan': ('zh_TW.UTF-8', 'lat0-sun16'),
|
||||||
|
'Cornish/Britain': ('kw_GB.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Croatian': ('hr_HR.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Czech': ('cs_CZ.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Danish': ('da_DK.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Dutch/Belgium': ('nl_BE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Dutch/Netherlands': ('nl_NL.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Australia': ('en_AU.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Botswana': ('en_BW.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Canada': ('en_CA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Denmark': ('en_DK.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Great_Britain': ('en_GB.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Hong_Kong': ('en_HK.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/India': ('en_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Ireland': ('en_IE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/New_Zealand': ('en_NZ.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Philippines': ('en_PH.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Singapore': ('en_SG.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/South_Africa': ('en_ZA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/USA': ('en_US.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'English/Zimbabwe': ('en_ZW.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Estonian': ('et_EE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Faroese/Faroe_Islands': ('fo_FO.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Finnish': ('fi_FI.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'French/Belgium': ('fr_BE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'French/Canada': ('fr_CA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'French/France': ('fr_FR.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'French/Luxemburg': ('fr_LU.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'French/Switzerland': ('fr_CH.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Galician/Spain': ('gl_ES.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'German/Austria': ('de_AT.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'German/Belgium': ('de_BE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'German/Germany': ('de_DE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'German/Luxemburg': ('de_LU.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'German/Switzerland': ('de_CH.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Greek': ('el_GR.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Greenlandic/Greenland': ('kl_GL.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Gujarati/India': ('gu_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Hebrew/Israel': ('he_IL.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Hindi/India': ('hi_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Hungarian': ('hu_HU.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Icelandic': ('is_IS.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Indonesian': ('id_ID.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Irish': ('ga_IE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Italian/Italy': ('it_IT.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Italian/Switzerland': ('it_CH.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Japanese': ('ja_JP.UTF-8', 'lat0-sun16'),
|
||||||
|
'Korean/Republic_of_Korea': ('ko_KR.UTF-8', 'lat0-sun16'),
|
||||||
|
'Lao/Laos': ('lo_LA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Latvian/Latvia': ('lv_LV.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Lithuanian': ('lt_LT.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Macedonian': ('mk_MK.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Malay/Malaysia': ('ms_MY.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Maltese/malta': ('mt_MT.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Manx/Britain': ('gv_GB.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Marathi/India': ('mr_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Northern/Norway': ('se_NO', 'latarcyrheb-sun16'),
|
||||||
|
'Norwegian': ('nb_NO.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Norwegian,/Norway': ('nn_NO.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Occitan/France': ('oc_FR', 'lat0-sun16'),
|
||||||
|
'Oriya/India': ('or_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Persian/Iran': ('fa_IR.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Polish': ('pl_PL.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Portuguese/Brasil': ('pt_BR.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Portuguese/Portugal': ('pt_PT.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Punjabi/India': ('pa_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Romanian': ('ro_RO.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Russian': ('ru_RU.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Russian/Ukraine': ('ru_UA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Serbian': ('sr_CS.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Serbian/Latin': ('sr_CS.UTF-8@Latn', 'latarcyrheb-sun16'),
|
||||||
|
'Slovak': ('sk_SK.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Slovenian/Slovenia': ('sl_SI.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Argentina': ('es_AR.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Bolivia': ('es_BO.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Chile': ('es_CL.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Colombia': ('es_CO.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Costa_Rica': ('es_CR.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Dominican_Republic': ('es_DO.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/El_Salvador': ('es_SV.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Equador': ('es_EC.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Guatemala': ('es_GT.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Honduras': ('es_HN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Mexico': ('es_MX.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Nicaragua': ('es_NI.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Panama': ('es_PA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Paraguay': ('es_PY.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Peru': ('es_PE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Puerto_Rico': ('es_PR.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Spain': ('es_ES.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/USA': ('es_US.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Uruguay': ('es_UY.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Spanish/Venezuela': ('es_VE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Swedish/Finland': ('sv_FI.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Swedish/Sweden': ('sv_SE.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Tagalog/Philippines': ('tl_PH', 'lat0-sun16'),
|
||||||
|
'Tamil/India': ('ta_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Telugu/India': ('te_IN.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Thai': ('th_TH.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Turkish': ('tr_TR.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Ukrainian': ('uk_UA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Urdu/Pakistan': ('ur_PK', 'latarcyrheb-sun16'),
|
||||||
|
'Uzbek/Uzbekistan': ('uz_UZ', 'lat0-sun16'),
|
||||||
|
'Walloon/Belgium': ('wa_BE@euro', 'lat0-sun16'),
|
||||||
|
'Welsh/Great_Britain': ('cy_GB.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Xhosa/South_Africa': ('xh_ZA.UTF-8', 'latarcyrheb-sun16'),
|
||||||
|
'Zulu/South_Africa': ('zu_ZA.UTF-8', 'latarcyrheb-sun16')
|
||||||
|
}
|
||||||
|
|
||||||
|
_timezones = []
|
||||||
|
|
||||||
|
def _initialize():
|
||||||
|
_timezones = _read_zonetab()
|
||||||
|
j=0
|
||||||
|
for timezone in _timezones:
|
||||||
|
set_timezone.__doc__ += timezone+', '
|
||||||
|
j+=1
|
||||||
|
if j%3 == 0:
|
||||||
|
set_timezone.__doc__ += '\n'
|
||||||
|
|
||||||
|
if not os.access(_TIMEZONE_CONFIG, os.R_OK):
|
||||||
|
#Theres no /etc/sysconfig/clock file, so make one
|
||||||
|
fd = open(_TIMEZONE_CONFIG, 'w')
|
||||||
|
f.write(' The ZONE parameter is only evaluated by sugarcontrol.\n')
|
||||||
|
f.write('The timezone of the system' +
|
||||||
|
' is defined by the contents of /etc/localtime.\n')
|
||||||
|
f.write('ZONE="America/NEW_York"\n')
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
keys = _LANGUAGES.keys()
|
||||||
|
keys.sort()
|
||||||
|
i = 0
|
||||||
|
for key in keys:
|
||||||
|
set_language.__doc__ += key+', '
|
||||||
|
i+=1
|
||||||
|
if i%3 == 0:
|
||||||
|
set_language.__doc__ += '\n'
|
||||||
|
|
||||||
|
def get_jabber():
|
||||||
|
pro = profile.get_profile()
|
||||||
|
return pro.jabber_server
|
||||||
|
|
||||||
|
def print_jabber():
|
||||||
|
print get_jabber()
|
||||||
|
|
||||||
|
def set_jabber(server):
|
||||||
|
"""Set the jabber server
|
||||||
|
server : 'olpc.collabora.co.uk'
|
||||||
|
"""
|
||||||
|
pro = profile.get_profile()
|
||||||
|
pro.jabber_server = server
|
||||||
|
pro.save()
|
||||||
|
|
||||||
|
def get_color():
|
||||||
|
return profile.get_color()
|
||||||
|
|
||||||
|
def print_color():
|
||||||
|
color = get_color().to_string()
|
||||||
|
str = color.split(',')
|
||||||
|
|
||||||
|
for color in _COLORS:
|
||||||
|
for hue in _COLORS[color]:
|
||||||
|
if _COLORS[color][hue] == str[0]:
|
||||||
|
print 'stroke: color=%s hue=%s'%(color, hue)
|
||||||
|
if _COLORS[color][hue] == str[1]:
|
||||||
|
print 'fill: color=%s hue=%s'%(color, hue)
|
||||||
|
|
||||||
|
def set_color(stroke, fill, modstroke='medium', modfill='medium'):
|
||||||
|
"""Set the system color.
|
||||||
|
fill : 'red, orange, yellow, blue, purple'
|
||||||
|
stroke : 'red, orange, yellow, blue, purple'
|
||||||
|
modstroke : 'dark, medium, light'
|
||||||
|
modfill : ''dark, medium, light'
|
||||||
|
"""
|
||||||
|
|
||||||
|
if modstroke not in _MODIFIERS or modfill not in _MODIFIERS:
|
||||||
|
print (_("Error in specified color modifiers."))
|
||||||
|
return
|
||||||
|
if stroke not in _COLORS or fill not in _COLORS:
|
||||||
|
print (_("Error in specified colors."))
|
||||||
|
return
|
||||||
|
|
||||||
|
if modstroke == modfill:
|
||||||
|
if modfill == medium:
|
||||||
|
modfill = light
|
||||||
|
else:
|
||||||
|
modfill = medium
|
||||||
|
|
||||||
|
color = _COLORS[stroke][modstroke] + ',' + _COLORS[fill][modfill]
|
||||||
|
pro = profile.get_profile()
|
||||||
|
pro.color = XoColor(color)
|
||||||
|
pro.save()
|
||||||
|
|
||||||
|
def get_nick():
|
||||||
|
return profile.get_nick_name()
|
||||||
|
|
||||||
|
def print_nick():
|
||||||
|
print get_nick()
|
||||||
|
|
||||||
|
def set_nick(nick):
|
||||||
|
"""Set the nickname.
|
||||||
|
nick : 'erikos'
|
||||||
|
"""
|
||||||
|
pro = profile.get_profile()
|
||||||
|
pro.nick_name = nick
|
||||||
|
pro.save()
|
||||||
|
|
||||||
|
def get_radio(state):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def print_radio(self):
|
||||||
|
print get_radio()
|
||||||
|
|
||||||
|
def set_radio(state):
|
||||||
|
"""Turn Radio off
|
||||||
|
state : 'on/off'
|
||||||
|
"""
|
||||||
|
if state == 'on':
|
||||||
|
cmd = '/sbin/iwconfig eth0 txpower on'
|
||||||
|
handle = os.popen(cmd, 'r')
|
||||||
|
print string.join(handle.readlines())
|
||||||
|
handle.close()
|
||||||
|
elif state == 'off':
|
||||||
|
cmd = '/sbin/iwconfig eth0 txpower off'
|
||||||
|
handle = os.popen(cmd, 'r')
|
||||||
|
print string.join(handle.readlines())
|
||||||
|
handle.close()
|
||||||
|
else:
|
||||||
|
print (_("Error in specified radio argument use on/off."))
|
||||||
|
|
||||||
|
def get_timezone():
|
||||||
|
fd = open(_TIMEZONE_CONFIG, "r")
|
||||||
|
lines = fd.readlines()
|
||||||
|
fd.close()
|
||||||
|
try:
|
||||||
|
for line in lines:
|
||||||
|
line = string.strip(line)
|
||||||
|
if len (line) and line[0] == '#':
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
tokens = string.split(line, "=")
|
||||||
|
if tokens[0] == "ZONE":
|
||||||
|
timezone = string.replace(tokens[1], '"', '')
|
||||||
|
return timezone
|
||||||
|
except Exception, e:
|
||||||
|
print (_("get_timezone: %s") % e)
|
||||||
|
except Exception, e:
|
||||||
|
print (_("get_timezone: %s") % e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def print_timezone():
|
||||||
|
timezone = get_timezone()
|
||||||
|
if timezone is None:
|
||||||
|
print (_("Error in reading timezone"))
|
||||||
|
else:
|
||||||
|
print timezone
|
||||||
|
|
||||||
|
def set_timezone(timezone):
|
||||||
|
"""Set the system timezone
|
||||||
|
timezone :
|
||||||
|
"""
|
||||||
|
if timezone in _timezones:
|
||||||
|
fromfile = os.path.join("/usr/share/zoneinfo/", timezone)
|
||||||
|
try:
|
||||||
|
shutil.copyfile(fromfile, "/etc/localtime")
|
||||||
|
except OSError, (errno, msg):
|
||||||
|
print (_("Error copying timezone (from %s): %s")%(fromfile, msg))
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
os.chmod("/etc/localtime", 0644)
|
||||||
|
except OSError, (errno, msg):
|
||||||
|
print (_("Changing permission of timezone: %s") % (msg))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Write info to the /etc/sysconfig/clock file
|
||||||
|
fd = open(_TIMEZONE_CONFIG, "w")
|
||||||
|
fd.write('# The ZONE parameter is only evaluated by sugarcontrol.\n')
|
||||||
|
fd.write('# The timezone of the system ' +
|
||||||
|
'is defined by the contents of /etc/localtime.\n')
|
||||||
|
fd.write('ZONE="%s"\n' % timezone)
|
||||||
|
fd.close()
|
||||||
|
else:
|
||||||
|
print (_("Error timezone does not exist."))
|
||||||
|
|
||||||
|
def _read_zonetab(fn='/usr/share/zoneinfo/zone.tab'):
|
||||||
|
fd = open (fn, 'r')
|
||||||
|
lines = fd.readlines()
|
||||||
|
fd.close()
|
||||||
|
timezones = []
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith('#'):
|
||||||
|
continue
|
||||||
|
line = line.split()
|
||||||
|
if len(line) > 1:
|
||||||
|
timezones.append(line[2])
|
||||||
|
timezones.sort()
|
||||||
|
return timezones
|
||||||
|
|
||||||
|
def _writeI18N(lang, sysfont):
|
||||||
|
path = '/etc/sysconfig/i18n'
|
||||||
|
if os.access(path, os.R_OK) == 0:
|
||||||
|
print(_("Could not access %s")%path)
|
||||||
|
else:
|
||||||
|
fd = open(path, 'w')
|
||||||
|
fd.write('LANG="' + lang + '"\n')
|
||||||
|
fd.write('SYSFONT="' + sysfont + '"\n')
|
||||||
|
fd.close()
|
||||||
|
|
||||||
|
def get_language():
|
||||||
|
originalFile = None
|
||||||
|
path = '/etc/sysconfig/i18n'
|
||||||
|
if os.access(path, os.R_OK) == 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
fd = open(path, "r")
|
||||||
|
originalFile = fd.readlines()
|
||||||
|
fd.close()
|
||||||
|
|
||||||
|
lang = None
|
||||||
|
|
||||||
|
for line in originalFile:
|
||||||
|
if line[:5] == "LANG=":
|
||||||
|
lang = line[5:].replace('"', '')
|
||||||
|
lang = lang.strip()
|
||||||
|
|
||||||
|
if not lang:
|
||||||
|
lang = "en_US"
|
||||||
|
|
||||||
|
return lang
|
||||||
|
|
||||||
|
def print_language():
|
||||||
|
code = get_language()
|
||||||
|
|
||||||
|
for lang in _LANGUAGES:
|
||||||
|
if _LANGUAGES[lang][0] == code:
|
||||||
|
print lang
|
||||||
|
return
|
||||||
|
print (_("Language for code=%s could not be determined.")%code)
|
||||||
|
|
||||||
|
def set_language(language):
|
||||||
|
"""Set the system language.
|
||||||
|
languages :
|
||||||
|
"""
|
||||||
|
if language in _LANGUAGES:
|
||||||
|
_writeI18N(_LANGUAGES[language][0], _LANGUAGES[language][1])
|
||||||
|
else:
|
||||||
|
print (_("Sorry I do not speak \'%s\'.")%language)
|
||||||
|
|
||||||
|
|
||||||
|
# inilialize the docstrings for the timezone and language
|
||||||
|
_initialize()
|
71
shell/controlpanel/sugar-control
Executable file
71
shell/controlpanel/sugar-control
Executable file
@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Copyright (C) 2007, One Laptop Per Child
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import getopt, sys
|
||||||
|
|
||||||
|
import control
|
||||||
|
|
||||||
|
def cmd_help():
|
||||||
|
print 'Usage: sugar-control [ option ] key [ args ... ] \n\
|
||||||
|
Control for the sugar environment. \n\
|
||||||
|
Options: \n\
|
||||||
|
-h, --help show this help message and exit \n\
|
||||||
|
-h key show information about this key \n\
|
||||||
|
-g key get the current value of the key \n\
|
||||||
|
-s key set the current value for the key \n\
|
||||||
|
'
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(sys.argv[1:], "h:s:g:", ["help"])
|
||||||
|
except getopt.GetoptError:
|
||||||
|
cmd_help()
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
output = None
|
||||||
|
verbose = False
|
||||||
|
|
||||||
|
for opt, key in opts:
|
||||||
|
if opt in ("-h"):
|
||||||
|
method = getattr(control, 'set_' + key, None)
|
||||||
|
if method is None:
|
||||||
|
cmd_help()
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
print method.__doc__
|
||||||
|
if opt in ("-g"):
|
||||||
|
method = getattr(control, 'print_' + key, None)
|
||||||
|
if method is None:
|
||||||
|
cmd_help()
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
method()
|
||||||
|
if opt in ("-s"):
|
||||||
|
method = getattr(control, 'set_' + key, None)
|
||||||
|
if method is None:
|
||||||
|
cmd_help()
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
method(*args)
|
||||||
|
except Exception, e:
|
||||||
|
print "sugar-control: %s"% e
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -57,6 +57,7 @@ class Shell(gobject.GObject):
|
|||||||
self._current_host = None
|
self._current_host = None
|
||||||
self._pending_host = None
|
self._pending_host = None
|
||||||
self._screen_rotation = 0
|
self._screen_rotation = 0
|
||||||
|
self._zoom_level = ShellModel.ZOOM_HOME
|
||||||
|
|
||||||
self._key_handler = KeyHandler(self)
|
self._key_handler = KeyHandler(self)
|
||||||
|
|
||||||
@ -73,6 +74,8 @@ class Shell(gobject.GObject):
|
|||||||
home_model.connect('pending-activity-changed',
|
home_model.connect('pending-activity-changed',
|
||||||
self._pending_activity_changed_cb)
|
self._pending_activity_changed_cb)
|
||||||
|
|
||||||
|
self._model.connect('notify::zoom-level', self._zoom_level_changed_cb)
|
||||||
|
|
||||||
gobject.idle_add(self._start_journal_idle)
|
gobject.idle_add(self._start_journal_idle)
|
||||||
|
|
||||||
def _start_journal_idle(self):
|
def _start_journal_idle(self):
|
||||||
@ -172,8 +175,7 @@ class Shell(gobject.GObject):
|
|||||||
activity.get_service().TakeScreenshot()
|
activity.get_service().TakeScreenshot()
|
||||||
|
|
||||||
def set_zoom_level(self, level):
|
def set_zoom_level(self, level):
|
||||||
old_level = self._model.get_zoom_level()
|
if level == self._zoom_level:
|
||||||
if level == old_level:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
self.take_activity_screenshot()
|
self.take_activity_screenshot()
|
||||||
@ -187,6 +189,17 @@ class Shell(gobject.GObject):
|
|||||||
self._screen.toggle_showing_desktop(True)
|
self._screen.toggle_showing_desktop(True)
|
||||||
self._home_window.set_zoom_level(level)
|
self._home_window.set_zoom_level(level)
|
||||||
|
|
||||||
|
def _zoom_level_changed_cb(self, model, pspec):
|
||||||
|
new_level = model.props.zoom_level
|
||||||
|
|
||||||
|
if new_level == ShellModel.ZOOM_HOME:
|
||||||
|
self._frame.show(Frame.MODE_HOME)
|
||||||
|
|
||||||
|
if self._zoom_level == ShellModel.ZOOM_HOME:
|
||||||
|
self._frame.hide()
|
||||||
|
|
||||||
|
self._zoom_level = new_level
|
||||||
|
|
||||||
def toggle_activity_fullscreen(self):
|
def toggle_activity_fullscreen(self):
|
||||||
if self._model.get_zoom_level() == ShellModel.ZOOM_ACTIVITY:
|
if self._model.get_zoom_level() == ShellModel.ZOOM_ACTIVITY:
|
||||||
self.get_current_activity().toggle_fullscreen()
|
self.get_current_activity().toggle_fullscreen()
|
||||||
|
@ -33,16 +33,11 @@ from view.frame.framewindow import FrameWindow
|
|||||||
from view.frame.clipboardpanelwindow import ClipboardPanelWindow
|
from view.frame.clipboardpanelwindow import ClipboardPanelWindow
|
||||||
from model.shellmodel import ShellModel
|
from model.shellmodel import ShellModel
|
||||||
|
|
||||||
MODE_NONE = 0
|
|
||||||
MODE_MOUSE = 1
|
|
||||||
MODE_KEYBOARD = 2
|
|
||||||
MODE_FORCE = 3
|
|
||||||
|
|
||||||
_FRAME_HIDING_DELAY = 500
|
_FRAME_HIDING_DELAY = 500
|
||||||
|
|
||||||
class _Animation(animator.Animation):
|
class _Animation(animator.Animation):
|
||||||
def __init__(self, frame, end):
|
def __init__(self, frame, end):
|
||||||
start = frame.get_current_position()
|
start = frame.current_position
|
||||||
animator.Animation.__init__(self, start, end)
|
animator.Animation.__init__(self, start, end)
|
||||||
self._frame = frame
|
self._frame = frame
|
||||||
|
|
||||||
@ -55,19 +50,16 @@ class _MouseListener(object):
|
|||||||
self._hide_sid = 0
|
self._hide_sid = 0
|
||||||
|
|
||||||
def mouse_enter(self):
|
def mouse_enter(self):
|
||||||
if self._frame.mode == MODE_NONE or \
|
|
||||||
self._frame.mode == MODE_MOUSE:
|
|
||||||
self._show_frame()
|
self._show_frame()
|
||||||
|
|
||||||
def mouse_leave(self):
|
def mouse_leave(self):
|
||||||
if self._frame.mode == MODE_MOUSE:
|
if self._frame.mode == Frame.MODE_MOUSE:
|
||||||
self._hide_frame()
|
self._hide_frame()
|
||||||
|
|
||||||
def _show_frame(self):
|
def _show_frame(self):
|
||||||
if self._hide_sid != 0:
|
if self._hide_sid != 0:
|
||||||
gobject.source_remove(self._hide_sid)
|
gobject.source_remove(self._hide_sid)
|
||||||
self._frame.show()
|
self._frame.show(Frame.MODE_MOUSE)
|
||||||
self._frame.mode = MODE_MOUSE
|
|
||||||
|
|
||||||
def _hide_frame_timeout_cb(self):
|
def _hide_frame_timeout_cb(self):
|
||||||
self._frame.hide()
|
self._frame.hide()
|
||||||
@ -80,52 +72,23 @@ class _MouseListener(object):
|
|||||||
_FRAME_HIDING_DELAY, self._hide_frame_timeout_cb)
|
_FRAME_HIDING_DELAY, self._hide_frame_timeout_cb)
|
||||||
|
|
||||||
class _KeyListener(object):
|
class _KeyListener(object):
|
||||||
_HIDDEN = 1
|
|
||||||
_SHOWN_PRESSED = 2
|
|
||||||
_SHOWN_REPEAT = 3
|
|
||||||
_SHOWN_RELEASED = 4
|
|
||||||
|
|
||||||
def __init__(self, frame):
|
def __init__(self, frame):
|
||||||
self._frame = frame
|
self._frame = frame
|
||||||
self._state = _KeyListener._HIDDEN
|
|
||||||
|
|
||||||
def key_press(self):
|
def key_press(self):
|
||||||
if self._frame.mode != MODE_NONE and \
|
|
||||||
self._frame.mode != MODE_KEYBOARD:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._frame.visible:
|
if self._frame.visible:
|
||||||
|
if self._frame.mode == Frame.MODE_KEYBOARD:
|
||||||
self._frame.hide()
|
self._frame.hide()
|
||||||
else:
|
else:
|
||||||
self._frame.show()
|
self._frame.show(Frame.MODE_KEYBOARD)
|
||||||
self._frame.mode = MODE_KEYBOARD
|
|
||||||
|
|
||||||
"""
|
|
||||||
if self._state == _KeyListener._HIDDEN:
|
|
||||||
self._frame.show()
|
|
||||||
self._frame.mode = MODE_KEYBOARD
|
|
||||||
self._state = _KeyListener._SHOWN_PRESSED
|
|
||||||
elif self._state == _KeyListener._SHOWN_PRESSED:
|
|
||||||
self._state = _KeyListener._SHOWN_REPEAT
|
|
||||||
elif self._state == _KeyListener._SHOWN_RELEASED:
|
|
||||||
self._frame.hide()
|
|
||||||
self._state = _KeyListener._HIDDEN
|
|
||||||
"""
|
|
||||||
|
|
||||||
def key_release(self):
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
if self._state == _KeyListener._SHOWN_PRESSED:
|
|
||||||
self._state = _KeyListener._SHOWN_RELEASED
|
|
||||||
elif self._state == _KeyListener._SHOWN_REPEAT:
|
|
||||||
self._frame.hide()
|
|
||||||
self._state = _KeyListener._HIDDEN
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Frame(object):
|
class Frame(object):
|
||||||
|
MODE_MOUSE = 0
|
||||||
|
MODE_KEYBOARD = 1
|
||||||
|
MODE_HOME = 2
|
||||||
|
|
||||||
def __init__(self, shell):
|
def __init__(self, shell):
|
||||||
self.mode = MODE_NONE
|
self.mode = None
|
||||||
self.visible = False
|
|
||||||
|
|
||||||
self._palette_group = palettegroup.get_group('frame')
|
self._palette_group = palettegroup.get_group('frame')
|
||||||
self._palette_group.connect('popdown', self._palette_group_popdown_cb)
|
self._palette_group.connect('popdown', self._palette_group_popdown_cb)
|
||||||
@ -136,7 +99,7 @@ class Frame(object):
|
|||||||
self._bottom_panel = None
|
self._bottom_panel = None
|
||||||
|
|
||||||
self._shell = shell
|
self._shell = shell
|
||||||
self._current_position = 0.0
|
self.current_position = 0.0
|
||||||
self._animator = None
|
self._animator = None
|
||||||
|
|
||||||
self._event_area = EventArea()
|
self._event_area = EventArea()
|
||||||
@ -157,9 +120,12 @@ class Frame(object):
|
|||||||
self._key_listener = _KeyListener(self)
|
self._key_listener = _KeyListener(self)
|
||||||
self._mouse_listener = _MouseListener(self)
|
self._mouse_listener = _MouseListener(self)
|
||||||
|
|
||||||
def hide(self, force=False):
|
self.move(1.0)
|
||||||
if not self.visible:
|
|
||||||
return
|
def is_visible(self):
|
||||||
|
return self.current_position != 0.0
|
||||||
|
|
||||||
|
def hide(self):
|
||||||
if self._animator:
|
if self._animator:
|
||||||
self._animator.stop()
|
self._animator.stop()
|
||||||
|
|
||||||
@ -169,16 +135,9 @@ class Frame(object):
|
|||||||
|
|
||||||
self._event_area.show()
|
self._event_area.show()
|
||||||
|
|
||||||
self.visible = False
|
self.mode = None
|
||||||
if force:
|
|
||||||
self.mode = MODE_NONE
|
|
||||||
else:
|
|
||||||
self.mode = MODE_FORCE
|
|
||||||
self._animator.connect('completed', self._hide_completed_cb)
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
self.mode = MODE_FORCE
|
|
||||||
|
|
||||||
|
def show(self, mode):
|
||||||
if self.visible:
|
if self.visible:
|
||||||
return
|
return
|
||||||
if self._animator:
|
if self._animator:
|
||||||
@ -186,19 +145,16 @@ class Frame(object):
|
|||||||
|
|
||||||
self._shell.take_activity_screenshot()
|
self._shell.take_activity_screenshot()
|
||||||
|
|
||||||
|
self.mode = mode
|
||||||
|
|
||||||
self._animator = animator.Animator(0.5)
|
self._animator = animator.Animator(0.5)
|
||||||
self._animator.add(_Animation(self, 1.0))
|
self._animator.add(_Animation(self, 1.0))
|
||||||
self._animator.start()
|
self._animator.start()
|
||||||
|
|
||||||
self._event_area.hide()
|
self._event_area.hide()
|
||||||
|
|
||||||
self.visible = True
|
|
||||||
|
|
||||||
def get_current_position(self):
|
|
||||||
return self._current_position
|
|
||||||
|
|
||||||
def move(self, pos):
|
def move(self, pos):
|
||||||
self._current_position = pos
|
self.current_position = pos
|
||||||
self._update_position()
|
self._update_position()
|
||||||
|
|
||||||
def _is_hover(self):
|
def _is_hover(self):
|
||||||
@ -266,21 +222,18 @@ class Frame(object):
|
|||||||
screen_h = gtk.gdk.screen_height()
|
screen_h = gtk.gdk.screen_height()
|
||||||
screen_w = gtk.gdk.screen_width()
|
screen_w = gtk.gdk.screen_width()
|
||||||
|
|
||||||
self._move_panel(self._top_panel, self._current_position,
|
self._move_panel(self._top_panel, self.current_position,
|
||||||
0, - self._top_panel.size, 0, 0)
|
0, - self._top_panel.size, 0, 0)
|
||||||
|
|
||||||
self._move_panel(self._bottom_panel, self._current_position,
|
self._move_panel(self._bottom_panel, self.current_position,
|
||||||
0, screen_h, 0, screen_h - self._bottom_panel.size)
|
0, screen_h, 0, screen_h - self._bottom_panel.size)
|
||||||
|
|
||||||
self._move_panel(self._left_panel, self._current_position,
|
self._move_panel(self._left_panel, self.current_position,
|
||||||
- self._left_panel.size, 0, 0, 0)
|
- self._left_panel.size, 0, 0, 0)
|
||||||
|
|
||||||
self._move_panel(self._right_panel, self._current_position,
|
self._move_panel(self._right_panel, self.current_position,
|
||||||
screen_w, 0, screen_w - self._right_panel.size, 0)
|
screen_w, 0, screen_w - self._right_panel.size, 0)
|
||||||
|
|
||||||
def _hide_completed_cb(self, animator):
|
|
||||||
self.mode = MODE_NONE
|
|
||||||
|
|
||||||
def _size_changed_cb(self, screen):
|
def _size_changed_cb(self, screen):
|
||||||
self._update_position()
|
self._update_position()
|
||||||
|
|
||||||
@ -316,6 +269,4 @@ class Frame(object):
|
|||||||
def notify_key_press(self):
|
def notify_key_press(self):
|
||||||
self._key_listener.key_press()
|
self._key_listener.key_press()
|
||||||
|
|
||||||
def notify_key_release(self):
|
visible = property(is_visible, None)
|
||||||
self._key_listener.key_release()
|
|
||||||
|
|
||||||
|
@ -48,55 +48,17 @@ class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
|
|
||||||
shell_model = shell.get_model()
|
shell_model = shell.get_model()
|
||||||
|
|
||||||
top_box = hippo.CanvasBox(yalign=hippo.ALIGNMENT_START,
|
|
||||||
box_height=style.GRID_CELL_SIZE,
|
|
||||||
orientation=hippo.ORIENTATION_HORIZONTAL)
|
|
||||||
self.append(top_box, hippo.PACK_EXPAND)
|
|
||||||
|
|
||||||
nw_arrow = CanvasIcon(icon_name='arrow_NW',
|
|
||||||
xalign=hippo.ALIGNMENT_START)
|
|
||||||
top_box.append(nw_arrow)
|
|
||||||
|
|
||||||
arrows_separator = hippo.CanvasBox()
|
|
||||||
top_box.append(arrows_separator, hippo.PACK_EXPAND)
|
|
||||||
|
|
||||||
ne_arrow = CanvasIcon(icon_name='arrow_NE',
|
|
||||||
xalign=hippo.ALIGNMENT_END)
|
|
||||||
top_box.append(ne_arrow)
|
|
||||||
|
|
||||||
self._donut = ActivitiesDonut(shell)
|
self._donut = ActivitiesDonut(shell)
|
||||||
self.append(self._donut)
|
self.append(self._donut, hippo.PACK_FIXED)
|
||||||
|
|
||||||
bottom_box = hippo.CanvasBox(yalign=hippo.ALIGNMENT_END,
|
|
||||||
box_height=style.GRID_CELL_SIZE,
|
|
||||||
orientation=hippo.ORIENTATION_HORIZONTAL)
|
|
||||||
self.append(bottom_box, hippo.PACK_EXPAND)
|
|
||||||
|
|
||||||
self._my_icon = _MyIcon(shell, style.XLARGE_ICON_SIZE)
|
self._my_icon = _MyIcon(shell, style.XLARGE_ICON_SIZE)
|
||||||
self.append(self._my_icon, hippo.PACK_FIXED)
|
self.append(self._my_icon, hippo.PACK_FIXED)
|
||||||
|
|
||||||
sw_arrow = CanvasIcon(icon_name='arrow_SW',
|
self._devices_box = _DevicesBox(shell_model.get_devices())
|
||||||
xalign=hippo.ALIGNMENT_START)
|
self.append(self._devices_box, hippo.PACK_FIXED)
|
||||||
bottom_box.append(sw_arrow)
|
|
||||||
|
|
||||||
devices_box = _DevicesBox(shell_model.get_devices())
|
|
||||||
bottom_box.append(devices_box, hippo.PACK_EXPAND)
|
|
||||||
|
|
||||||
se_arrow = CanvasIcon(icon_name='arrow_SE',
|
|
||||||
xalign=hippo.ALIGNMENT_END)
|
|
||||||
bottom_box.append(se_arrow)
|
|
||||||
|
|
||||||
self._arrows = [ nw_arrow, ne_arrow, sw_arrow, se_arrow ]
|
|
||||||
|
|
||||||
shell_model.connect('notify::state',
|
shell_model.connect('notify::state',
|
||||||
self._shell_state_changed_cb)
|
self._shell_state_changed_cb)
|
||||||
shell_model.connect('notify::zoom-level',
|
|
||||||
self._shell_zoom_level_changed_cb)
|
|
||||||
|
|
||||||
def _shell_zoom_level_changed_cb(self, model, pspec):
|
|
||||||
for arrow in self._arrows:
|
|
||||||
arrow.destroy()
|
|
||||||
self._arrows = []
|
|
||||||
|
|
||||||
def _shell_state_changed_cb(self, model, pspec):
|
def _shell_state_changed_cb(self, model, pspec):
|
||||||
# FIXME implement this
|
# FIXME implement this
|
||||||
@ -106,10 +68,18 @@ class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
def do_allocate(self, width, height, origin_changed):
|
def do_allocate(self, width, height, origin_changed):
|
||||||
hippo.CanvasBox.do_allocate(self, width, height, origin_changed)
|
hippo.CanvasBox.do_allocate(self, width, height, origin_changed)
|
||||||
|
|
||||||
|
[donut_width, donut_height] = self._donut.get_allocation()
|
||||||
|
self.set_position(self._donut, (width - donut_width) / 2,
|
||||||
|
(height - donut_height) / 2)
|
||||||
|
|
||||||
[icon_width, icon_height] = self._my_icon.get_allocation()
|
[icon_width, icon_height] = self._my_icon.get_allocation()
|
||||||
self.set_position(self._my_icon, (width - icon_width) / 2,
|
self.set_position(self._my_icon, (width - icon_width) / 2,
|
||||||
(height - icon_height) / 2)
|
(height - icon_height) / 2)
|
||||||
|
|
||||||
|
[box_width, box_height] = self._devices_box.get_allocation()
|
||||||
|
self.set_position(self._devices_box, (width - icon_width) / 2,
|
||||||
|
height - style.GRID_CELL_SIZE * 3)
|
||||||
|
|
||||||
_REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes
|
_REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes
|
||||||
|
|
||||||
def resume(self):
|
def resume(self):
|
||||||
|
@ -45,8 +45,6 @@ _actions_table = {
|
|||||||
'<ctrl>F11' : 'volume_min',
|
'<ctrl>F11' : 'volume_min',
|
||||||
'<ctrl>F12' : 'volume_max',
|
'<ctrl>F12' : 'volume_max',
|
||||||
'<alt>1' : 'screenshot',
|
'<alt>1' : 'screenshot',
|
||||||
'<alt>equal' : 'console',
|
|
||||||
'<alt>0' : 'console',
|
|
||||||
'<alt>f' : 'frame',
|
'<alt>f' : 'frame',
|
||||||
'0x93' : 'frame',
|
'0x93' : 'frame',
|
||||||
'<alt>o' : 'overlay',
|
'<alt>o' : 'overlay',
|
||||||
@ -156,9 +154,6 @@ class KeyHandler(object):
|
|||||||
def handle_screenshot(self):
|
def handle_screenshot(self):
|
||||||
self._shell.take_screenshot()
|
self._shell.take_screenshot()
|
||||||
|
|
||||||
def handle_console(self):
|
|
||||||
gobject.idle_add(self._toggle_console_visibility_cb)
|
|
||||||
|
|
||||||
def handle_frame(self):
|
def handle_frame(self):
|
||||||
self._shell.get_frame().notify_key_press()
|
self._shell.get_frame().notify_key_press()
|
||||||
|
|
||||||
@ -222,10 +217,3 @@ class KeyHandler(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _toggle_console_visibility_cb(self):
|
|
||||||
bus = dbus.SessionBus()
|
|
||||||
proxy = bus.get_object('org.laptop.sugar.Console',
|
|
||||||
'/org/laptop/sugar/Console')
|
|
||||||
console = dbus.Interface(proxy, 'org.laptop.sugar.Console')
|
|
||||||
console.ToggleVisibility()
|
|
||||||
|
Loading…
Reference in New Issue
Block a user