# Copyright 2008 Chris Ball. # # 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 """Screencast Activity: An activity for producing XO tutorials.""" from gettext import gettext as _ from dbus.service import method from dbus.service import signal as dbus_signal import fcntl import gobject import gtk import logging import os import popen2 import re import shutil import signal import sys from sugar.activity import activity from sugar.activity.activity import ActivityToolbox from sugar.activity.activity import get_bundle_path from sugar.activity.activity import get_bundle_name from sugar.graphics.alert import NotifyAlert from sugar.graphics.combobox import ComboBox SERVICE = "org.laptop.Screencast" IFACE = SERVICE PATH = "/org/laptop/Screencast" OUTFILE = "/tmp/recordmydesktop.ogv" class ScreencastActivity(activity.Activity): """Screencast Activity as specified in activity.info""" def __init__(self, handle): """Set up the Screencast activity.""" super(ScreencastActivity, self).__init__(handle) self._logger = logging.getLogger('screencast-activity') self.timed_id2 = None from sugar.graphics.menuitem import MenuItem from sugar.graphics.icon import Icon # Main layout. Record button, stop button, label. hbox = gtk.HBox() vbox = gtk.VBox() # Toolbar. toolbox = ActivityToolbox(self) activity_toolbar = toolbox.get_activity_toolbar() activity_toolbar.remove(activity_toolbar.share) activity_toolbar.share = None activity_toolbar.remove(activity_toolbar.keep) activity_toolbar.keep = None self.set_toolbox(toolbox) toolbox.show() # Recording buttons. self.record = gtk.Button("Record") self.record.connect("clicked", self.record_cb) self.record.set_size_request(150, 150) recimage = gtk.Image() recimage.set_from_icon_name("media-record", -1) self.record.set_image(recimage) self.stop = gtk.Button("Stop") self.stop.connect("clicked", self.stop_cb) self.stop.set_size_request(150, 150) self.stop.set_sensitive(False) stopimage = gtk.Image() stopimage.set_from_icon_name("media-playback-stop", -1) self.stop.set_image(stopimage) # Record sound checkbox and quality selector hbox2 = gtk.HBox(spacing=50) self.audiocheckbox = gtk.CheckButton(label="record sound") self.audiocheckbox.set_active(True) hbox2.add(self.audiocheckbox) self.qualitycombo = ComboBox() self.qualitycombo.append_item("0", "high quality video") self.qualitycombo.append_item("1", "medium quality video") self.qualitycombo.append_item("2", "low quality video") self.qualitycombo.set_active(2) hbox2.add(self.qualitycombo) options = gtk.Alignment(0.5, 0, 0, 0) options.add(hbox2) # Status label. self.status = gtk.Label(_("Status: Stopped")) hbox.pack_start(self.record, expand=False, padding=40) hbox.pack_start(self.stop, expand=False, padding=40) # Encoding progress bar self.progressbar = gtk.ProgressBar(adjustment=None) self.progressbar.set_fraction(0) self.progressbar.set_text("0% complete") valign = gtk.Alignment(0.5, 0.4, 0, 0) valign.add(vbox) vbox.pack_end(self.progressbar, expand=True, padding=20) vbox.pack_end(self.status, expand=True, padding=40) vbox.pack_end(hbox, expand=True, fill=False) vbox.pack_end(options, expand=True, padding=40) self.set_canvas(valign) self.show_all() self.progressbar.hide() def write_file(self, file_path): print "Saving file to %s" % file_path self.metadata['mime_type'] = 'video/ogg' #try: # shutil.copy(OUTFILE, file_path) #except IOError, e: # print "unable to save to outfile: %s" % e # FIXME: This fails in /tmp. # that comment by probably cjb # I have no idea why it was saving to filepath #added copy-to-journal in check_status_cb #error msgs are OK probably just no video processed #Tony Forster #try: # os.remove(OUTFILE) #except OSError, e: # print "unable to remove outfile: %s" % e def can_close(self): if self.status.get_text().startswith("Status: Stopped"): return True else: self.alert("You need to finish operation before quitting.", self.status.get_text()) def alert(self, title, text=None): alert = NotifyAlert(timeout=10) alert.props.title = title alert.props.msg = text self.add_alert(alert) alert.connect('response', self.alert_cancel_cb) alert.show() def alert_cancel_cb(self, alert, response_id): self.remove_alert(alert) def record_cb(self, record): self.stop.set_sensitive(True) self.record.set_sensitive(False) self.audiocheckbox.set_sensitive(False) self.qualitycombo.set_sensitive(False) execargs = ["./recordmydesktop", "--no-frame", "--overwrite"] if not self.audiocheckbox.get_active(): execargs.append("--no-sound") if self.qualitycombo.get_active() == 0: execargs.append("-v_quality") # in later versions seems to be --v_quality instead execargs.append("0") elif self.qualitycombo.get_active() == 1: execargs.append("-v_quality") # in later versions seems to be --v_quality instead execargs.append("31") execargs.append("-o") execargs.append(OUTFILE) self.childp = popen2.Popen3(execargs, "t", 0) flags = fcntl.fcntl(self.childp.childerr, fcntl.F_GETFL) fcntl.fcntl(self.childp.childerr, fcntl.F_SETFL, flags | os.O_NONBLOCK) flags = fcntl.fcntl(self.childp.fromchild, fcntl.F_GETFL) fcntl.fcntl(self.childp.fromchild, fcntl.F_SETFL, flags | os.O_NONBLOCK) self.timed_id = gobject.timeout_add(1000, self.check_status_cb) self.status.set_text("Status: Recording") def stop_cb(self, stop): exitret = os.waitpid(self.childp.pid, os.WNOHANG) if exitret[0] == 0: os.kill(self.childp.pid, signal.SIGTERM) self.stop.set_sensitive(False) def update_counter(self): self.progressbar.show() while True: try: strstdout = self.childp.fromchild.read() self.counter_fraction = float(re.search("[0-9][0-9]?[0-9]?", strstdout).group()) percentage = self.counter_fraction / 100.0 if percentage > 1.0: percentage = 1.0 #print "PORCENTAJE %s " % str(percentage) self.progressbar.set_fraction(percentage) self.progressbar.set_text("%d%%"%int(percentage * 100)+' complete') except IOError: gtk.main_iteration(block=False) except AttributeError: break except: print "Unexpected error:", sys.exc_info()[0] print "Unexpected error:", sys.exc_info()[1] break def check_status_cb(self): if self.childp.pid: exitret = os.waitpid(self.childp.pid, os.WNOHANG) if exitret[0] != 0: # The recording process exited self.status.set_text("Status: Stopped") if self.timed_id2: gobject.source_remove(self.timed_id2) self.timed_id2 = None self.progressbar.hide() self.alert("Success:", "Saved recording to journal") self.progressbar.set_fraction(0) self.progressbar.set_text('0% complete') self.record.set_sensitive(True) self.audiocheckbox.set_sensitive(True) self.qualitycombo.set_sensitive(True) if self._jobject.metadata['title_set_by_user'] == '1': title = self.metadata['title'] else: title = "My Screencast" os.system("copy-to-journal /tmp/recordmydesktop.ogv -m video/ogg -t \"%s\""% title) return False else: # Maybe we have new stderr. while True: try: err_line = self.childp.childerr.readline() if err_line.startswith("STATE:ENCODING"): if not self.timed_id2: self.timed_id2=gobject.timeout_add(300, self.update_counter) self.status.set_text("Status: Encoding, please wait") except: break return True