diff options
Diffstat (limited to 'src/intro')
-rw-r--r-- | src/intro/Makefile.am | 10 | ||||
-rw-r--r-- | src/intro/__init__.py | 0 | ||||
-rw-r--r-- | src/intro/colorpicker.py | 42 | ||||
-rw-r--r-- | src/intro/default-picture.png | bin | 0 -> 10442 bytes | |||
-rw-r--r-- | src/intro/glive.py | 196 | ||||
-rw-r--r-- | src/intro/intro.py | 267 |
6 files changed, 515 insertions, 0 deletions
diff --git a/src/intro/Makefile.am b/src/intro/Makefile.am new file mode 100644 index 0000000..3b92ea0 --- /dev/null +++ b/src/intro/Makefile.am @@ -0,0 +1,10 @@ +imagedir = $(pkgdatadir)/shell/intro +image_DATA = default-picture.png + +EXTRA_DIST = $(conf_DATA) $(image_DATA) +sugardir = $(pkgdatadir)/shell/intro +sugar_PYTHON = \ + __init__.py \ + colorpicker.py \ + intro.py \ + glive.py diff --git a/src/intro/__init__.py b/src/intro/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/intro/__init__.py diff --git a/src/intro/colorpicker.py b/src/intro/colorpicker.py new file mode 100644 index 0000000..90dbc26 --- /dev/null +++ b/src/intro/colorpicker.py @@ -0,0 +1,42 @@ +# Copyright (C) 2007, 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 + +import hippo + +from sugar.graphics.icon import CanvasIcon +from sugar.graphics import style +from sugar.graphics.xocolor import XoColor + +class ColorPicker(hippo.CanvasBox, hippo.CanvasItem): + def __init__(self, **kwargs): + hippo.CanvasBox.__init__(self, **kwargs) + self.props.orientation = hippo.ORIENTATION_HORIZONTAL + + self._xo = CanvasIcon(size=style.XLARGE_ICON_SIZE, + icon_name='computer-xo') + self._set_random_colors() + self._xo.connect('activated', self._xo_activated_cb) + self.append(self._xo) + + def _xo_activated_cb(self, item): + self._set_random_colors() + + def get_color(self): + return self._xo_color + + def _set_random_colors(self): + self._xo_color = XoColor() + self._xo.props.xo_color = self._xo_color diff --git a/src/intro/default-picture.png b/src/intro/default-picture.png Binary files differnew file mode 100644 index 0000000..e26b9b0 --- /dev/null +++ b/src/intro/default-picture.png diff --git a/src/intro/glive.py b/src/intro/glive.py new file mode 100644 index 0000000..a875e48 --- /dev/null +++ b/src/intro/glive.py @@ -0,0 +1,196 @@ +# -*- Mode: Python -*- +# vi:si:et:sw=4:sts=4:ts=4 + +import gtk +import pygtk +pygtk.require('2.0') +import sys + +import pygst +pygst.require('0.10') +import gst +import gst.interfaces + +import gobject +gobject.threads_init() + +class Glive(gobject.GObject): + __gsignals__ = { + 'new-picture': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), + 'sink': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self, parent, width, height): + gobject.GObject.__init__(self) + self._parent = parent + + #check out the halfpipe, d00d. + self.pipeline = gst.Pipeline() + + self.v4l2src = gst.element_factory_make("v4l2src", "v4l2src") + self.t = gst.element_factory_make("tee", "tee") + self.t_src_pad = self.t.get_request_pad( "src%d" ) + self.vscale = gst.element_factory_make("videoscale", "videoscale") + self.ximagesink = gst.element_factory_make("ximagesink", "ximagesink") + + self.pipeline.add(self.v4l2src) + self.pipeline.add(self.t) + self.pipeline.add(self.vscale) + self.pipeline.add(self.ximagesink) + + self.v4l2src.link(self.t) + + videoscale_structure = gst.Structure("video/x-raw-rgb") + videoscale_structure['width'] = width + videoscale_structure['height'] = height + videoscale_structure['bpp'] = 16 + videoscale_structure['depth'] = 16 + videoscale_caps = gst.Caps(videoscale_structure) + self.t_src_pad.link(self.vscale.get_pad("sink")) + self.vscale.link(self.ximagesink, videoscale_caps) + #self.vscale.link(self.ximagesink) + + self.queue = gst.element_factory_make("queue", "queue") + self.queue.set_property("leaky", True) + self.queue.set_property("max-size-buffers", 1) + self.qsrc = self.queue.get_pad( "src" ) + self.qsink = self.queue.get_pad("sink") + self.ffmpeg = gst.element_factory_make("ffmpegcolorspace", "ffmpegcolorspace") + self.jpgenc = gst.element_factory_make("jpegenc", "jpegenc") + self.filesink = gst.element_factory_make("fakesink", "fakesink") + self.filesink.connect( "handoff", self.copyframe ) + self.filesink.set_property("signal-handoffs", True) + self.pipeline.add(self.queue, self.ffmpeg, self.jpgenc, self.filesink) + + #only link at snapshot time + #self.t.link(self.queue) + self.queue.link(self.ffmpeg) + self.ffmpeg.link(self.jpgenc) + self.jpgenc.link(self.filesink) + self.exposureOpen = False + + self._bus = self.pipeline.get_bus() + self._CONNECT_SYNC = -1 + self._CONNECT_MSG = -1 + self.doPostBusStuff() + + def copyframe(self, fsink, buffer, pad, user_data=None): + #for some reason, we get two back to back buffers, even though we + #ask for only one. + if (self.exposureOpen): + self.exposureOpen = False + piccy = gtk.gdk.pixbuf_loader_new_with_mime_type("image/jpeg") + piccy.write( buffer ) + piccy.close() + pixbuf = piccy.get_pixbuf() + del piccy + + self.t.unlink(self.queue) + self.queue.set_property("leaky", True) + + gobject.idle_add(self.loadPic, pixbuf) + + def loadPic( self, pixbuf ): + self.emit('new-picture', pixbuf) + + def takeSnapshot( self ): + if (self.exposureOpen): + return + else: + self.exposureOpen = True + self.t.link(self.queue) + + def doPostBusStuff(self): + self._bus.enable_sync_message_emission() + self._bus.add_signal_watch() + self._CONNECT_SYNC = self._bus.connect('sync-message::element', self.on_sync_message) + self._CONNECT_MSG = self._bus.connect('message', self.on_message) + + def on_sync_message(self, bus, message): + if message.structure is None: + return + if message.structure.get_name() == 'prepare-xwindow-id': + self.emit('sink', message.src) + message.src.set_property('force-aspect-ratio', True) + + def on_message(self, bus, message): + t = message.type + if (t == gst.MESSAGE_ERROR): + err, debug = message.parse_error() + if (self.on_eos): + self.on_eos() + self._playing = False + elif (t == gst.MESSAGE_EOS): + if (self.on_eos): + self.on_eos() + self._playing = False + + def on_eos( self ): + pass + + def stop(self): + self.pipeline.set_state(gst.STATE_NULL) + + def play(self): + self.pipeline.set_state(gst.STATE_PLAYING) + + def pause(self): + self.pipeline.set_state(gst.STATE_PAUSED) + + +class LiveVideoSlot(gtk.EventBox): + __gsignals__ = { + 'pixbuf': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), + } + + def __init__(self, width, height): + gtk.EventBox.__init__(self) + + self.imagesink = None + self.playa = None + self._width = width + self._height = height + + self.unset_flags(gtk.DOUBLE_BUFFERED) + self.connect('focus-in-event', self.focus_in) + self.connect('focus-out-event', self.focus_out) + self.connect("button-press-event", self._button_press_event_cb) + self.connect("expose-event", self._expose_event_cb) + + def _expose_event_cb(self, widget, event): + if not self.playa: + self.playa = Glive(self, self._width, self._height) + self.playa.connect('new-picture', self._new_picture_cb) + self.playa.connect('sink', self._new_sink_cb) + + def _new_picture_cb(self, playa, pixbuf): + self.emit('pixbuf', pixbuf) + + def _new_sink_cb(self, playa, sink): + if (self.imagesink != None): + assert self.window.xid + self.imagesink = None + del self.imagesink + self.imagesink = sink + self.imagesink.set_xwindow_id(self.window.xid) + + def _button_press_event_cb(self, widget, event): + self.takeSnapshot() + + def focus_in(self, widget, event, args=None): + self.play() + + def focus_out(self, widget, event, args=None): + self.stop() + + def play( self ): + self.playa.play() + + def pause( self ): + self.playa.pause() + + def stop( self ): + self.playa.stop() + + def takeSnapshot( self ): + self.playa.takeSnapshot() diff --git a/src/intro/intro.py b/src/intro/intro.py new file mode 100644 index 0000000..1bd46c7 --- /dev/null +++ b/src/intro/intro.py @@ -0,0 +1,267 @@ +# Copyright (C) 2007, 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 + +import os +from ConfigParser import ConfigParser +from gettext import gettext as _ + +import gtk +import gobject +import dbus +import hippo +import logging + +from sugar import env +from sugar.graphics import style +from sugar.graphics.icon import Icon +from sugar.graphics.entry import CanvasEntry +from sugar.profile import get_profile + +import colorpicker + +_BACKGROUND_COLOR = style.COLOR_PANEL_GREY + +class _Page(hippo.CanvasBox): + __gproperties__ = { + 'valid' : (bool, None, None, False, + gobject.PARAM_READABLE) + } + + def __init__(self, **kwargs): + hippo.CanvasBox.__init__(self, **kwargs) + self.valid = False + + def set_valid(self, valid): + self.valid = valid + self.notify('valid') + + def do_get_property(self, pspec): + if pspec.name == 'valid': + return self.valid + + def activate(self): + pass + +class _NamePage(_Page): + def __init__(self, intro): + _Page.__init__(self, xalign=hippo.ALIGNMENT_CENTER, + background_color=_BACKGROUND_COLOR.get_int(), + spacing=style.DEFAULT_SPACING, + orientation=hippo.ORIENTATION_HORIZONTAL,) + + self._intro = intro + + label = hippo.CanvasText(text=_("Name:")) + self.append(label) + + self._entry = CanvasEntry(box_width=style.zoom(300)) + self._entry.set_background(_BACKGROUND_COLOR.get_html()) + self._entry.connect('notify::text', self._text_changed_cb) + + widget = self._entry.props.widget + widget.set_max_length(45) + + self.append(self._entry) + + def _text_changed_cb(self, entry, pspec): + valid = len(entry.props.text.strip()) > 0 + self.set_valid(valid) + + def get_name(self): + return self._entry.props.text + + def activate(self): + self._entry.props.widget.grab_focus() + +class _ColorPage(_Page): + def __init__(self, **kwargs): + _Page.__init__(self, xalign=hippo.ALIGNMENT_CENTER, + background_color=_BACKGROUND_COLOR.get_int(), + spacing=style.DEFAULT_SPACING, + yalign=hippo.ALIGNMENT_CENTER, **kwargs) + + self._label = hippo.CanvasText(text=_("Click to change color:"), + xalign=hippo.ALIGNMENT_CENTER) + self.append(self._label) + + self._cp = colorpicker.ColorPicker(xalign=hippo.ALIGNMENT_CENTER) + self.append(self._cp) + + self._color = self._cp.get_color() + self.set_valid(True) + + def get_color(self): + return self._cp.get_color() + +class _IntroBox(hippo.CanvasBox): + __gsignals__ = { + 'done': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, + gobject.TYPE_PYOBJECT])) + } + + PAGE_NAME = 0 + PAGE_COLOR = 1 + + PAGE_FIRST = PAGE_NAME + PAGE_LAST = PAGE_COLOR + + def __init__(self): + hippo.CanvasBox.__init__(self, padding=style.zoom(30), + background_color=_BACKGROUND_COLOR.get_int()) + + self._page = self.PAGE_NAME + self._name_page = _NamePage(self) + self._color_page = _ColorPage() + self._current_page = None + + self._setup_page() + + def _setup_page(self): + self.remove_all() + + if self._page == self.PAGE_NAME: + self._current_page = self._name_page + elif self._page == self.PAGE_COLOR: + self._current_page = self._color_page + + self.append(self._current_page, hippo.PACK_EXPAND) + + button_box = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL) + + if self._page != self.PAGE_FIRST: + back_button = hippo.CanvasButton(text=_('Back')) + image = Icon(icon_name='go-left') + back_button.props.widget.set_image(image) + back_button.connect('activated', self._back_activated_cb) + button_box.append(back_button) + + spacer = hippo.CanvasBox() + button_box.append(spacer, hippo.PACK_EXPAND) + + self._next_button = hippo.CanvasButton() + image = Icon(icon_name='go-right') + self._next_button.props.widget.set_image(image) + + if self._page == self.PAGE_LAST: + self._next_button.props.text = _('Done') + self._next_button.connect('activated', self._done_activated_cb) + else: + self._next_button.props.text = _('Next') + self._next_button.connect('activated', self._next_activated_cb) + + self._current_page.activate() + + self._update_next_button() + button_box.append(self._next_button) + + self._current_page.connect('notify::valid', + self._page_valid_changed_cb) + self.append(button_box) + + def _update_next_button(self): + widget = self._next_button.props.widget + widget.props.sensitive = self._current_page.props.valid + + def _page_valid_changed_cb(self, page, pspec): + self._update_next_button() + + def _back_activated_cb(self, item): + self.back() + + def back(self): + if self._page != self.PAGE_FIRST: + self._page -= 1 + self._setup_page() + + def _next_activated_cb(self, item): + self.next() + + def next(self): + if self._page == self.PAGE_LAST: + self.done() + if self._current_page.props.valid: + self._page += 1 + self._setup_page() + + def _done_activated_cb(self, item): + self.done() + + def done(self): + path = os.path.join(os.path.dirname(__file__), 'default-picture.png') + pixbuf = gtk.gdk.pixbuf_new_from_file(path) + name = self._name_page.get_name() + color = self._color_page.get_color() + + self.emit('done', pixbuf, name, color) + + def _key_press_cb(self, widget, event): + if gtk.gdk.keyval_name(event.keyval) == "Return": + self.next() + return True + elif gtk.gdk.keyval_name(event.keyval) == "Escape": + self.back() + return True + return False + +class IntroWindow(gtk.Window): + def __init__(self): + gtk.Window.__init__(self) + + self._canvas = hippo.Canvas() + self._intro_box = _IntroBox() + self._intro_box.connect('done', self._done_cb) + self._canvas.set_root(self._intro_box) + + self.add(self._canvas) + self._canvas.show() + self.connect('key-press-event', self._intro_box._key_press_cb) + + def _done_cb(self, box, pixbuf, name, color): + self.hide() + gobject.idle_add(self._create_profile, pixbuf, name, color) + + def _create_profile(self, pixbuf, name, color): + # Save the buddy icon + icon_path = os.path.join(env.get_profile_path(), "buddy-icon.jpg") + scaled = pixbuf.scale_simple(200, 200, gtk.gdk.INTERP_BILINEAR) + pixbuf.save(icon_path, "jpeg", {"quality":"85"}) + + profile = get_profile() + profile.nick_name = name + profile.color = color + profile.save() + + # Generate keypair + import commands + keypath = os.path.join(env.get_profile_path(), "owner.key") + if not os.path.isfile(keypath): + cmd = "ssh-keygen -q -t dsa -f %s -C '' -N ''" % keypath + (s, o) = commands.getstatusoutput(cmd) + if s != 0: + logging.error("Could not generate key pair: %d" % s) + else: + logging.error("Keypair exists, skip generation.") + + gtk.main_quit() + return False + + +if __name__ == "__main__": + w = IntroWindow() + w.show() + w.connect('destroy', gtk.main_quit) + gtk.main() |