Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/audio_sensors/__init__.py0
-rw-r--r--plugins/audio_sensors/audio_sensors.py282
-rw-r--r--plugins/audio_sensors/audiograb.py623
-rw-r--r--plugins/audio_sensors/ringbuffer.py108
-rw-r--r--plugins/camera_sensor/__init__.py0
-rw-r--r--plugins/camera_sensor/camera_sensor.py171
-rw-r--r--plugins/camera_sensor/tacamera.py42
-rw-r--r--plugins/camera_sensor/v4l2.py1914
-rw-r--r--plugins/rfid/__init__.py0
-rw-r--r--plugins/rfid/device.py61
-rw-r--r--plugins/rfid/rfid.py140
-rw-r--r--plugins/rfid/rfidrweusb.py200
-rw-r--r--plugins/rfid/rfidutils.py123
-rw-r--r--plugins/rfid/serial/__init__.py25
-rw-r--r--plugins/rfid/serial/serialposix.py492
-rw-r--r--plugins/rfid/serial/serialutil.py400
-rw-r--r--plugins/rfid/tis2000.py252
-rw-r--r--plugins/rfid/utils.py98
-rw-r--r--plugins/turtle_blocks_extras/__init__.py0
-rw-r--r--plugins/turtle_blocks_extras/__init__.pycbin0 -> 170 bytes
-rw-r--r--plugins/turtle_blocks_extras/icons/extrasoff.svg75
-rw-r--r--plugins/turtle_blocks_extras/icons/extrason.svg158
-rw-r--r--plugins/turtle_blocks_extras/icons/mediaoff.svg46
-rw-r--r--plugins/turtle_blocks_extras/icons/mediaon.svg46
-rw-r--r--plugins/turtle_blocks_extras/icons/portfoliooff.svg72
-rw-r--r--plugins/turtle_blocks_extras/icons/portfolioon.svg284
-rw-r--r--plugins/turtle_blocks_extras/icons/sensoroff.svg41
-rw-r--r--plugins/turtle_blocks_extras/icons/sensoron.svg41
-rw-r--r--plugins/turtle_blocks_extras/turtle_blocks_extras.py1213
-rw-r--r--plugins/turtle_blocks_extras/turtle_blocks_extras.pycbin0 -> 42524 bytes
-rw-r--r--plugins/turtle_blocks_extras/turtle_blocks_extras.py~1213
31 files changed, 8120 insertions, 0 deletions
diff --git a/plugins/audio_sensors/__init__.py b/plugins/audio_sensors/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/audio_sensors/__init__.py
diff --git a/plugins/audio_sensors/audio_sensors.py b/plugins/audio_sensors/audio_sensors.py
new file mode 100644
index 0000000..39bad17
--- /dev/null
+++ b/plugins/audio_sensors/audio_sensors.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python
+#Copyright (c) 2011 Walter Bender
+#
+# 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
+
+try:
+ from numpy import append
+ from numpy.fft import rfft
+ PITCH_AVAILABLE = True
+except:
+ PITCH_AVAILABLE = False
+
+from plugins.plugin import Plugin
+
+from plugins.audio_sensors.audiograb import AudioGrab_Unknown, AudioGrab_XO1, \
+ AudioGrab_XO15, SENSOR_DC_NO_BIAS, SENSOR_DC_BIAS
+
+from plugins.audio_sensors.ringbuffer import RingBuffer1d
+
+from TurtleArt.tapalette import make_palette
+from TurtleArt.taconstants import XO1, XO15
+from TurtleArt.talogo import primitive_dictionary
+from TurtleArt.tautils import debug_output
+
+import logging
+_logger = logging.getLogger('turtleart-activity audio sensors plugin')
+
+
+def _avg(array, abs_value=False):
+ """ Calc. the average value of an array """
+ if len(array) == 0:
+ return 0
+ array_sum = 0
+ if abs_value:
+ for a in array:
+ array_sum += abs(a)
+ else:
+ for a in array:
+ array_sum += a
+ return float(array_sum) / len(array)
+
+
+class Audio_sensors(Plugin):
+
+ def __init__(self, parent):
+ self._parent = parent
+ self.hw = self._parent.hw
+ self.running_sugar = self._parent.running_sugar
+ self._status = True # TODO: test for audio device
+
+ def setup(self):
+ # set up audio-sensor-specific blocks
+ if not self._status:
+ return
+
+ self.max_samples = 1500
+ self.input_step = 1
+
+ self.ringbuffer = RingBuffer1d(self.max_samples, dtype='int16')
+ if self.hw == XO1:
+ self.voltage_gain = 0.00002225
+ self.voltage_bias = 1.140
+ elif self.hw == XO15:
+ self.voltage_gain = -0.0001471
+ self.voltage_bias = 1.695
+
+ palette = make_palette('sensor',
+ colors=["#FF6060", "#A06060"],
+ help_string=_('Palette of sensor blocks'))
+
+ primitive_dictionary['sound'] = self.prim_sound
+ palette.add_block('sound',
+ style='box-style',
+ label=_('sound'),
+ help_string=_('raw microphone input signal'),
+ value_block=True,
+ prim_name='sound')
+ self._parent.lc.def_prim('sound', 0,
+ lambda self: primitive_dictionary['sound']())
+
+ primitive_dictionary['volume'] = self.prim_volume
+ palette.add_block('volume',
+ style='box-style',
+ label=_('loudness'),
+ help_string=_('microphone input volume'),
+ value_block=True,
+ prim_name='volume')
+ self._parent.lc.def_prim('volume', 0,
+ lambda self: primitive_dictionary['volume']())
+
+ primitive_dictionary['pitch'] = self.prim_pitch
+ if PITCH_AVAILABLE:
+ palette.add_block('pitch',
+ style='box-style',
+ label=_('pitch'),
+ help_string=_('microphone input pitch'),
+ value_block=True,
+ prim_name='pitch')
+ else:
+ palette.add_block('pitch',
+ hidden=True,
+ style='box-style',
+ label=_('pitch'),
+ help_string=_('microphone input pitch'),
+ value_block=True,
+ prim_name='pitch')
+ self._parent.lc.def_prim('pitch', 0,
+ lambda self: primitive_dictionary['pitch']())
+
+ if self.hw in [XO1, XO15]:
+ primitive_dictionary['resistance'] = self.prim_resistance
+ palette.add_block('resistance',
+ style='box-style',
+ label=_('resistance'),
+ help_string=_('microphone input resistance'),
+ value_block=True,
+ prim_name='resistance')
+ self._parent.lc.def_prim('resistance', 0,
+ lambda self: primitive_dictionary['resistance']())
+
+ primitive_dictionary['voltage'] = self.prim_voltage
+ palette.add_block('voltage',
+ style='box-style',
+ label=_('voltage'),
+ help_string=_('microphone input voltage'),
+ value_block=True,
+ prim_name='voltage')
+ self._parent.lc.def_prim('voltage', 0,
+ lambda self: primitive_dictionary['voltage']())
+
+ self.audio_started = False
+
+ def start(self):
+ # This gets called by the start button
+ if not self._status:
+ return
+ """ Start grabbing audio if there is an audio block in use """
+ if len(self._parent.block_list.get_similar_blocks('block',
+ ['volume', 'sound', 'pitch', 'resistance', 'voltage'])) > 0:
+ if self.audio_started:
+ self.audiograb.resume_grabbing()
+ else:
+ if self.hw == XO15:
+ self.audiograb = AudioGrab_XO15(self.new_buffer, self)
+ elif self.hw == XO1:
+ self.audiograb = AudioGrab_XO1(self.new_buffer, self)
+ else:
+ self.audiograb = AudioGrab_Unknown(self.new_buffer, self)
+ self.audiograb.start_grabbing()
+ self.audio_started = True
+ self._update_audio_mode()
+
+ def new_buffer(self, buf):
+ """ Append a new buffer to the ringbuffer """
+ self.ringbuffer.append(buf)
+ return True
+
+ def _update_audio_mode(self):
+ """ If there are sensor blocks, set the appropriate audio mode """
+ if not hasattr(self._parent.lc, 'value_blocks'):
+ return
+ for name in ['sound', 'volume', 'pitch']:
+ if name in self._parent.lc.value_blocks:
+ if len(self._parent.lc.value_blocks[name]) > 0:
+ self.audiograb.set_sensor_type()
+ return
+ if 'resistance' in self._parent.lc.value_blocks:
+ if len(self._parent.lc.value_blocks['resistance']) > 0:
+ self.audiograb.set_sensor_type(SENSOR_DC_BIAS)
+ return
+ if 'voltage' in self._parent.lc.value_blocks:
+ if len(self._parent.lc.value_blocks['voltage']) > 0:
+ self.audiograb.set_sensor_type(SENSOR_DC_NO_BIAS)
+ return
+
+ def stop(self):
+ # This gets called by the stop button
+ if self._status:
+ if self.audio_started:
+ self.audiograb.pause_grabbing()
+
+ def goto_background(self):
+ # This gets called when your process is sent to the background
+ # TODO: handle this case
+ pass
+
+ def return_to_foreground(self):
+ # This gets called when your process returns from the background
+ # TODO: handle this case
+ pass
+
+ def quit(self):
+ # This gets called by the quit button
+ self.stop()
+
+ def _status_report(self):
+ debug_output('Reporting audio sensor status: %s' % (str(self._status)))
+ return self._status
+
+ # Block primitives used in talogo
+
+ def prim_volume(self):
+ """ return mic in value """
+ #TODO: Adjust gain for different HW
+ buf = self.ringbuffer.read(None, self.input_step)
+ if len(buf) > 0:
+ volume = float(_avg(buf, abs_value=True))
+ self._parent.lc.update_label_value('volume', volume)
+ return volume
+ else:
+ return 0
+
+ def prim_sound(self):
+ """ return raw mic in value """
+ buf = self.ringbuffer.read(None, self.input_step)
+ if len(buf) > 0:
+ sound = float(buf[0])
+ self._parent.lc.update_label_value('sound', sound)
+ return sound
+ else:
+ return 0
+
+ def prim_pitch(self):
+ """ return index of max value in fft of mic in values """
+ if not PITCH_AVAILABLE:
+ return 0
+ buf = []
+ for i in range(4):
+ buf = append(buf, self.ringbuffer.read(None, self.input_step))
+ if len(buf) > 0:
+ r = []
+ for j in rfft(buf):
+ r.append(abs(j))
+ # Convert output to Hertz
+ pitch = r.index(max(r)) * 48000 / len(buf)
+ self._parent.lc.update_label_value('pitch', pitch)
+ return pitch
+ else:
+ return 0
+
+ def prim_resistance(self):
+ """ return resistance sensor value """
+ buf = self.ringbuffer.read(None, self.input_step)
+ if len(buf) > 0:
+ # See <http://bugs.sugarlabs.org/ticket/552#comment:7>
+ # TODO: test this calibration on XO 1.5
+ if self.hw == XO1:
+ resistance = 2.718 ** ((float(_avg(buf)) * 0.000045788) + \
+ 8.0531)
+ else:
+ avg_buf = float(_avg(buf))
+ if avg_buf > 0:
+ resistance = (420000000 / avg_buf) - 13500
+ else:
+ resistance = 420000000
+ self._parent.lc.update_label_value('resistance', resistance)
+ return resistance
+ else:
+ return 0
+
+ def prim_voltage(self):
+ """ return voltage sensor value """
+ buf = self.ringbuffer.read(None, self.input_step)
+ if len(buf) > 0:
+ # See <http://bugs.sugarlabs.org/ticket/552#comment:7>
+ voltage = float(_avg(buf)) * self.voltage_gain + self.voltage_bias
+ self._parent.lc.update_label_value('voltage', voltage)
+ return voltage
+ else:
+ return 0
diff --git a/plugins/audio_sensors/audiograb.py b/plugins/audio_sensors/audiograb.py
new file mode 100644
index 0000000..84e0c8d
--- /dev/null
+++ b/plugins/audio_sensors/audiograb.py
@@ -0,0 +1,623 @@
+#! /usr/bin/python
+#
+# Author: Arjun Sarwal arjun@laptop.org
+# Copyright (C) 2007, Arjun Sarwal
+# Copyright (C) 2009,10 Walter Bender
+# Copyright (C) 2009, Benjamin Berg, Sebastian Berg
+# Copyright (C) 2009, Sayamindu Dasgupta
+# Copyright (C) 2010, Sascha Silbe
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+SENSOR_AC_NO_BIAS = 'external'
+SENSOR_AC_BIAS = 'sound'
+SENSOR_DC_NO_BIAS = 'voltage'
+SENSOR_DC_BIAS = 'resistance'
+
+import pygst
+import gst
+import gst.interfaces
+from numpy import fromstring
+import os
+import subprocess
+from string import find
+from threading import Timer
+
+# Initial device settings
+RATE = 48000
+MIC_BOOST = True
+DC_MODE_ENABLE = False
+CAPTURE_GAIN = 50
+BIAS = True
+
+# Setting on quit
+QUIT_MIC_BOOST = False
+QUIT_DC_MODE_ENABLE = False
+QUIT_CAPTURE_GAIN = 100
+QUIT_BIAS = True
+
+from TurtleArt.taconstants import XO1
+from TurtleArt.tautils import debug_output
+
+
+class AudioGrab:
+ """ The interface between measure and the audio device """
+
+ def __init__(self, callable1, parent):
+ """ Initialize the class: callable1 is a data buffer;
+ parent is the parent class"""
+
+ self.callable1 = callable1
+ self.parent = parent
+ self.sensor = None
+
+ self.temp_buffer = [0]
+ self.picture_buffer = [] # place to hold screen grabs
+
+ self.draw_graph_status = False
+ self.screenshot = True
+
+ self.rate = 48000
+ self.final_count = 0
+ self.count_temp = 0
+ self.entry_count = 0
+
+ self.counter_buffer = 0
+
+ self._dc_control = None
+ self._mic_bias_control = None
+ self._capture_control = None
+ self._mic_boost_control = None
+ self._hardwired = False # Query controls or use hardwired names
+
+ # Set up gst pipeline
+ self.pipeline = gst.Pipeline("pipeline")
+ self.alsasrc = gst.element_factory_make("alsasrc", "alsa-source")
+ self.pipeline.add(self.alsasrc)
+ self.caps1 = gst.element_factory_make("capsfilter", "caps1")
+ self.pipeline.add(self.caps1)
+ caps_str = "audio/x-raw-int,rate=%d,channels=1,depth=16" % (RATE)
+ self.caps1.set_property("caps", gst.caps_from_string(caps_str))
+ self.fakesink = gst.element_factory_make("fakesink", "fsink")
+ self.pipeline.add(self.fakesink)
+ self.fakesink.connect("handoff", self.on_buffer)
+ self.fakesink.set_property("signal-handoffs", True)
+ gst.element_link_many(self.alsasrc, self.caps1, self.fakesink)
+
+ self.dont_queue_the_buffer = False
+
+ self._mixer = gst.element_factory_make('alsamixer')
+ rc = self._mixer.set_state(gst.STATE_PAUSED)
+ assert rc == gst.STATE_CHANGE_SUCCESS
+
+ # Query the available controls
+ try: # F11+
+ debug_output('controls: %r' % \
+ ([t.props.untranslated_label for t in self._mixer.list_tracks()]),
+ self.parent.running_sugar)
+ self._dc_control = self._find_control(['dc mode'])
+ self._mic_bias_control = self._find_control(['mic bias',
+ 'dc input bias',
+ 'v_refout'])
+ self._mic_boost_control = self._find_control(['mic boost',
+ 'mic boost (+20db)',
+ 'internal mic boost',
+ 'analog mic boost'])
+ self._mic_gain_control = self._find_control(['mic'])
+ self._capture_control = self._find_control(['capture'])
+ self._master_control = self._find_control(['master'])
+ except AttributeError: # F9- (no untranslated_label attribute)
+ self._hardwired = True
+
+ # Variables for saving and resuming state of sound device
+ self.master = self.get_master()
+ self.bias = BIAS
+ self.dcmode = DC_MODE_ENABLE
+ self.capture_gain = CAPTURE_GAIN
+ self.mic_boost = MIC_BOOST
+ self.mic = self.get_mic_gain()
+
+ # Timer for interval sampling and switch to indicate when to capture
+ self.capture_timer = None
+ self.capture_interval_sample = False
+
+ def set_handoff_signal(self, handoff_state):
+ """Sets whether the handoff signal would generate an interrupt or not"""
+ self.fakesink.set_property("signal-handoffs", handoff_state)
+
+ def _new_buffer(self, buf):
+ """ Use a new buffer """
+ if not self.dont_queue_the_buffer:
+ self.temp_buffer = buf
+ self.callable1(buf)
+ else:
+ pass
+
+ def on_buffer(self, element, buffer, pad):
+ """The function that is called whenever new data is available
+ This is the signal handler for the handoff signal"""
+ if buffer is None:
+ debug_output('audiograb buffer is None', self.parent.running_sugar)
+ return False
+
+ temp_buffer = fromstring(buffer, 'int16')
+ if not self.dont_queue_the_buffer:
+ self._new_buffer(temp_buffer)
+ return False
+
+ def set_sensor(self, sensor):
+ """Keep a reference to the sensot toolbar for logging"""
+ self.sensor = sensor
+
+ def start_sound_device(self):
+ """Start or Restart grabbing data from the audio capture"""
+ gst.event_new_flush_start()
+ self.pipeline.set_state(gst.STATE_PLAYING)
+
+ def stop_sound_device(self):
+ """Stop grabbing data from capture device"""
+ gst.event_new_flush_stop()
+ self.pipeline.set_state(gst.STATE_NULL)
+
+ def set_sampling_rate(self, sr):
+ """Sets the sampling rate of the capture device
+ Sampling rate must be given as an integer for example 16000 for
+ setting 16Khz sampling rate
+ The sampling rate would be set in the device to the nearest available"""
+ self.pause_grabbing()
+ caps_str = "audio/x-raw-int,rate=%d,channels=1,depth=16" % (sr, )
+ self.caps1.set_property("caps", gst.caps_from_string(caps_str))
+ self.resume_grabbing()
+
+ def get_sampling_rate(self):
+ """Gets the sampling rate of the capture device"""
+ return int(self.caps1.get_property("caps")[0]['rate'])
+
+ def set_callable1(self, callable1):
+ """Sets the callable to the drawing function for giving the
+ data at the end of idle-add"""
+ self.callable1 = callable1
+
+ def start_grabbing(self):
+ """Called right at the start of the Activity"""
+ self.start_sound_device()
+
+ def pause_grabbing(self):
+ """When Activity goes into background"""
+ self.save_state()
+ self.stop_sound_device()
+
+ def resume_grabbing(self):
+ """When Activity becomes active after going to background"""
+ self.start_sound_device()
+ self.resume_state()
+
+ def stop_grabbing(self):
+ """Not used ???"""
+ self.stop_sound_device()
+ self.set_handoff_signal(False)
+
+ def _find_control(self, prefixes):
+ """Try to find a mixer control matching one of the prefixes.
+
+ The control with the best match (smallest difference in length
+ between label and prefix) will be returned. If no match is found,
+ None is returned.
+ """
+ def best_prefix(label, prefixes):
+ matches =\
+ [len(label) - len(p) for p in prefixes if label.startswith(p)]
+ if not matches:
+ return None
+
+ matches.sort()
+ return matches[0]
+
+ controls = []
+ for track in self._mixer.list_tracks():
+ label = track.props.untranslated_label.lower()
+ diff = best_prefix(label, prefixes)
+ if diff is not None:
+ controls.append((track, diff))
+
+ controls.sort(key=lambda e: e[1])
+ if controls:
+ debug_output('Found control: %s' % \
+ (str(controls[0][0].props.untranslated_label)),
+ self.parent.running_sugar)
+ return controls[0][0]
+
+ return None
+
+ def save_state(self):
+ """Saves the state of all audio controls"""
+ self.master = self.get_master()
+ self.bias = self.get_bias()
+ self.dcmode = self.get_dc_mode()
+ self.capture_gain = self.get_capture_gain()
+ self.mic_boost = self.get_mic_boost()
+
+ def resume_state(self):
+ """Put back all audio control settings from the saved state"""
+ self.set_master(self.master)
+ self.set_bias(self.bias)
+ self.set_dc_mode(self.dcmode)
+ self.set_capture_gain(self.capture_gain)
+ self.set_mic_boost(self.mic_boost)
+
+ def _get_mute(self, control, name, default):
+ """Get mute status of a control"""
+ if not control:
+ return default
+
+ value = bool(control.flags & gst.interfaces.MIXER_TRACK_MUTE)
+ debug_output('Getting %s (%s) mute status: %r' % (name,
+ control.props.untranslated_label, value),
+ self.parent.running_sugar)
+ return value
+
+ def _set_mute(self, control, name, value):
+ """Mute a control"""
+ if not control:
+ return
+
+ self._mixer.set_mute(control, value)
+ debug_output('Set mute for %s (%s) to %r' % (name,
+ control.props.untranslated_label, value),
+ self.parent.running_sugar)
+
+ def _get_volume(self, control, name):
+ """Get volume of a control and convert to a scale of 0-100"""
+ if not control:
+ return 100
+
+ try: # sometimes get_volume does not return a tuple
+ hw_volume = self._mixer.get_volume(control)[0]
+ except IndexError:
+ return 100
+
+ min_vol = control.min_volume
+ max_vol = control.max_volume
+ percent = (hw_volume - min_vol)*100//(max_vol - min_vol)
+ return percent
+
+ def _set_volume(self, control, name, value):
+ """Sets the level of a control on a scale of 0-100"""
+ if not control:
+ return
+
+ # convert value to scale of control
+ min_vol = control.min_volume
+ max_vol = control.max_volume
+ if min_vol != max_vol:
+ hw_volume = value*(max_vol - min_vol)//100 + min_vol
+ self._mixer.set_volume(control, (hw_volume,)*control.num_channels)
+
+ def amixer_set(self, control, state):
+ """ Direct call to amixer for old systems. """
+ if state:
+ os.system("amixer set '%s' unmute" % (control))
+ else:
+ os.system("amixer set '%s' mute" % (control))
+
+ def mute_master(self):
+ """Mutes the Master Control"""
+ if not self._hardwired and self.parent.hw != XO1:
+ self._set_mute(self._master_control, 'Master', True)
+ else:
+ self.amixer_set('Master', False)
+
+ def unmute_master(self):
+ """Unmutes the Master Control"""
+ if not self._hardwired and self.parent.hw != XO1:
+ self._set_mute(self._master_control, 'Master', True)
+ else:
+ self.amixer_set('Master', True)
+
+ def set_master(self, master_val):
+ """Sets the Master gain slider settings
+ master_val must be given as an integer between 0 and 100 indicating the
+ percentage of the slider to be set"""
+ if not self._hardwired:
+ self._set_volume(self._master_control, 'Master', master_val)
+ else:
+ os.system("amixer set Master " + str(master_val) + "%")
+
+ def get_master(self):
+ """Gets the Master gain slider settings. The value returned is an
+ integer between 0-100 and is an indicative of the percentage 0 - 100%"""
+ if not self._hardwired:
+ return self._get_volume(self._master_control, 'master')
+ else:
+ p = str(subprocess.Popen(["amixer", "get", "Master"],
+ stdout=subprocess.PIPE).communicate()[0])
+ p = p[find(p, "Front Left:"):]
+ p = p[find(p, "[")+1:]
+ p = p[:find(p, "%]")]
+ return int(p)
+
+ def set_bias(self, bias_state=False):
+ """Enables / disables bias voltage."""
+ if not self._hardwired and self.parent.hw != XO1:
+ if self._mic_bias_control is None:
+ return
+ # if not isinstance(self._mic_bias_control,
+ # gst.interfaces.MixerOptions):
+ if self._mic_bias_control not in self._mixer.list_tracks():
+ return self._set_mute(self._mic_bias_control, 'Mic Bias',
+ not bias_state)
+
+ # We assume that values are sorted from lowest (=off) to highest.
+ # Since they are mixed strings ("Off", "50%", etc.), we cannot
+ # easily ensure this by sorting with the default sort order.
+ try:
+ if bias_state:
+ # self._mixer.set_option(self._mic_bias_control, values[-1])
+ self._mixer.set_volume(self._mic_bias_control,
+ self._mic_bias_control.max_volume)
+ else:
+ self._mixer.set_volume(self._mic_bias_control,
+ self._mic_bias_control.min_volume)
+ # self._mixer.set_option(self._mic_bias_control, values[0])
+ except TypeError:
+ self._set_mute(self._mic_bias_control, 'Mic Bias',
+ not bias_state)
+ elif self._hardwired:
+ self.amixer_set('V_REFOUT Enable', bias_state)
+ else:
+ self.amixer_set('MIC Bias Enable', bias_state)
+
+ def get_bias(self):
+ """Check whether bias voltage is enabled."""
+ if not self._hardwired:
+ if self._mic_bias_control is None:
+ return False
+ if self._mic_bias_control not in self._mixer.list_tracks():
+ return not self._get_mute(self._mic_bias_control, 'Mic Bias',
+ False)
+ current = self._mixer.get_volume(self._mic_bias_control)
+ # same ordering assertion as in set_bias() applies
+ # if current == values[0]:
+ if current == self._mic_bias_control.min_volume:
+ return False
+ return True
+ else:
+ p = str(subprocess.Popen(["amixer", "get", "'V_REFOUT Enable'"],
+ stdout=subprocess.PIPE).communicate()[0])
+ p = p[find(p, "Mono:"):]
+ p = p[find(p, "[")+1:]
+ p = p[:find(p, "]")]
+ if p == "on":
+ return True
+ return False
+
+ def set_dc_mode(self, dc_mode=False):
+ """Sets the DC Mode Enable control
+ pass False to mute and True to unmute"""
+ if not self._hardwired and self.parent.hw != XO1:
+ if self._dc_control is not None:
+ self._set_mute(self._dc_control, 'DC mode', not dc_mode)
+ else:
+ self.amixer_set('DC Mode Enable', dc_mode)
+
+ def get_dc_mode(self):
+ """Returns the setting of DC Mode Enable control
+ i .e. True: Unmuted and False: Muted"""
+ if not self._hardwired:
+ if self._dc_control is not None:
+ return not self._get_mute(self._dc_control, 'DC mode', False)
+ else:
+ return False
+ else:
+ p = str(subprocess.Popen(["amixer", "get", "'DC Mode Enable'"],
+ stdout=subprocess.PIPE).communicate()[0])
+ p = p[find(p, "Mono:"):]
+ p = p[find(p, "[")+1:]
+ p = p[:find(p, "]")]
+ if p == "on":
+ return True
+ else:
+ return False
+
+ def set_mic_boost(self, mic_boost=False):
+ """Set Mic Boost.
+ True = +20dB, False = 0dB"""
+ if not self._hardwired:
+ if self._mic_boost_control is None:
+ return
+ if self._mic_boost_control not in self._mixer.list_tracks():
+ return self._set_mute(self._mic_boost_control, 'Mic Boost',
+ mic_boost)
+ value = self._mixer.get_volume(self._mic_boost_control)
+ try:
+ if mic_boost:
+ self._mixer.set_volume(self._mic_boost_control,
+ self._mic_boost_control.max_volume)
+ else:
+ self._mixer.set_volume(self._mic_boost_control,
+ self._mic_boost_control.min_volume)
+ except TypeError:
+ return self._set_mute(self._mic_boost_control, 'Mic Boost',
+ not mic_boost)
+ else:
+ self.amixer_set('Mic Boost (+20dB)', mic_boost)
+
+ def get_mic_boost(self):
+ """Return Mic Boost setting.
+ True = +20dB, False = 0dB"""
+ if not self._hardwired:
+ if self._mic_boost_control is None:
+ return False
+ if self._mic_boost_control not in self._mixer.list_tracks():
+ return self._get_mute(self._mic_boost_control, 'Mic Boost',
+ False)
+ current = self._mixer.get_volume(self._mic_boost_control)
+ debug_output('current: %s' % (str(current)),
+ self.parent.running_sugar)
+ if current != self._mic_boost_control.min_volume:
+ return True
+ return False
+ else:
+ p = str(subprocess.Popen(["amixer", "get", "'Mic Boost (+20dB)'"],
+ stdout=subprocess.PIPE).communicate()[0])
+ p = p[find(p, "Mono:"):]
+ p = p[find(p, "[")+1:]
+ p = p[:find(p, "]")]
+ if p == "on":
+ return True
+ else:
+ return False
+
+ def set_capture_gain(self, capture_val):
+ """Sets the Capture gain slider settings
+ capture_val must be given as an integer between 0 and 100 indicating the
+ percentage of the slider to be set"""
+ if not self._hardwired and self.parent.hw != XO1:
+ if self._capture_control is not None:
+ self._set_volume(self._capture_control, 'Capture', capture_val)
+ else:
+ os.system("amixer set Capture " + str(capture_val) + "%")
+
+ def get_capture_gain(self):
+ """Gets the Capture gain slider settings. The value returned is an
+ integer between 0-100 and is an indicative of the percentage 0 - 100%"""
+ if not self._hardwired:
+ if self._capture_control is not None:
+ return self._get_volume(self._capture_control, 'Capture')
+ else:
+ return 0
+ else:
+ p = str(subprocess.Popen(["amixer", "get", "Capture"],
+ stdout=subprocess.PIPE).communicate()[0])
+ p = p[find(p, "Front Left:"):]
+ p = p[find(p, "[")+1:]
+ p = p[:find(p, "%]")]
+ return int(p)
+
+ def set_mic_gain(self, mic_val):
+ """Sets the MIC gain slider settings
+ mic_val must be given as an integer between 0 and 100 indicating the
+ percentage of the slider to be set"""
+ if not self._hardwired and self.parent.hw != XO1:
+ self._set_volume(self._mic_gain_control, 'Mic', mic_val)
+ else:
+ os.system("amixer set Mic " + str(mic_val) + "%")
+
+ def get_mic_gain(self):
+ """Gets the MIC gain slider settings. The value returned is an
+ integer between 0-100 and is an indicative of the percentage 0 - 100%"""
+ if not self._hardwired:
+ return self._get_volume(self._mic_gain_control, 'Mic')
+ else:
+ p = str(subprocess.Popen(["amixer", "get", "Mic"],
+ stdout=subprocess.PIPE).communicate()[0])
+ try:
+ p = p[find(p, "Mono:"):]
+ p = p[find(p, "[")+1:]
+ p = p[:find(p, "%]")]
+ return int(p)
+ except:
+ return(0)
+
+ def set_sensor_type(self, sensor_type=SENSOR_AC_BIAS):
+ """Set the type of sensor you want to use. Set sensor_type according
+ to the following
+ SENSOR_AC_NO_BIAS - AC coupling with Bias Off --> Very rarely used.
+ Use when connecting a dynamic microphone externally
+ SENSOR_AC_BIAS - AC coupling with Bias On --> The default settings.
+ The internal MIC uses these
+ SENSOR_DC_NO_BIAS - DC coupling with Bias Off --> measuring voltage
+ output sensor. For example LM35 which gives output proportional
+ to temperature
+ SENSOR_DC_BIAS - DC coupling with Bias On --> measuing resistance.
+ """
+ PARAMETERS = {
+ SENSOR_AC_NO_BIAS: (False, False, 50, True),
+ SENSOR_AC_BIAS: (False, True, 40, True),
+ SENSOR_DC_NO_BIAS: (True, False, 0, False),
+ SENSOR_DC_BIAS: (True, True, 0, False)
+ }
+ mode, bias, gain, boost = PARAMETERS[sensor_type]
+ debug_output('Set Sensor Type to %s' % (str(sensor_type)),
+ self.parent.running_sugar)
+ self._set_sensor_type(mode, bias, gain, boost)
+
+ def _set_sensor_type(self, mode=None, bias=None, gain=None, boost=None):
+ """Helper to modify (some) of the sensor settings."""
+ if mode is not None:
+ self.set_dc_mode(mode)
+ if self._dc_control is not None:
+ os.system("amixer get '%s'" %\
+ (self._dc_control.props.untranslated_label))
+ if bias is not None:
+ self.set_bias(bias)
+ if self._mic_bias_control is not None:
+ os.system("amixer get '%s'" %\
+ (self._mic_bias_control.props.untranslated_label))
+ if gain is not None:
+ self.set_capture_gain(gain)
+ if self._capture_control is not None:
+ os.system("amixer get '%s'" %\
+ (self._capture_control.props.untranslated_label))
+ if boost is not None:
+ self.set_mic_boost(boost)
+ if self._mic_boost_control is not None:
+ os.system("amixer get '%s'" %\
+ (self._mic_boost_control.props.untranslated_label))
+
+ def on_activity_quit(self):
+ """When Activity quits"""
+ self.set_mic_boost(QUIT_MIC_BOOST)
+ self.set_dc_mode(QUIT_DC_MODE_ENABLE)
+ self.set_capture_gain(QUIT_CAPTURE_GAIN)
+ self.set_bias(QUIT_BIAS)
+ self.stop_sound_device()
+
+
+class AudioGrab_XO1(AudioGrab):
+ """ Use default parameters for OLPC XO 1.0 laptop """
+ pass
+
+
+class AudioGrab_XO15(AudioGrab):
+ """ Override parameters for OLPC XO 1.5 laptop """
+ def set_sensor_type(self, sensor_type=SENSOR_AC_BIAS):
+ """Helper to modify (some) of the sensor settings."""
+ PARAMETERS = {
+ SENSOR_AC_NO_BIAS: (False, False, 80, True),
+ SENSOR_AC_BIAS: (False, True, 80, True),
+ SENSOR_DC_NO_BIAS: (True, False, 80, False),
+ SENSOR_DC_BIAS: (True, True, 90, False)
+ }
+ debug_output('Set Sensor Type to %s' % (str(sensor_type)),
+ self.parent.running_sugar)
+ mode, bias, gain, boost = PARAMETERS[sensor_type]
+ self._set_sensor_type(mode, bias, gain, boost)
+
+
+class AudioGrab_Unknown(AudioGrab):
+ """ Override parameters for generic hardware """
+ def set_sensor_type(self, sensor_type=SENSOR_AC_BIAS):
+ """Helper to modify (some) of the sensor settings."""
+ PARAMETERS = {
+ SENSOR_AC_NO_BIAS: (None, False, 50, True),
+ SENSOR_AC_BIAS: (None, True, 40, True),
+ SENSOR_DC_NO_BIAS: (True, False, 80, False),
+ SENSOR_DC_BIAS: (True, True, 90, False)
+ }
+ debug_output('Set Sensor Type to %s' % (str(sensor_type)),
+ self.parent.running_sugar)
+ mode, bias, gain, boost = PARAMETERS[sensor_type]
+ self._set_sensor_type(mode, bias, gain, boost)
diff --git a/plugins/audio_sensors/ringbuffer.py b/plugins/audio_sensors/ringbuffer.py
new file mode 100644
index 0000000..2afb5c9
--- /dev/null
+++ b/plugins/audio_sensors/ringbuffer.py
@@ -0,0 +1,108 @@
+# Copyright (C) 2009, Benjamin Berg, Sebastian Berg
+# Copyright (C) 2010, Walter Bender
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import numpy as np
+
+
+class RingBuffer1d(object):
+ """This class implements an array being written in as a ring and that can
+ be read from continuously ending with the newest data or starting with the
+ oldest. It returns a numpy array copy of the data;
+ """
+
+ def __init__(self, length, dtype=None):
+ """Initialize the 1 dimensional ring buffer with the given lengths.
+ The initial values are all 0s
+ """
+ self.offset = 0
+
+ self._data = np.zeros(length, dtype=dtype)
+
+ self.stored = 0
+
+ def fill(self, number):
+ self._data.fill(number)
+ self.offset = 0
+
+ def append(self, data):
+ """Append to the ring buffer (and overwrite old data). If len(data)
+ is greater then the ring buffers length, the newest data takes
+ precedence.
+ """
+ data = np.asarray(data)
+
+ if len(self._data) == 0:
+ return
+
+ if len(data) >= len(self._data):
+ self._data[:] = data[-len(self._data):]
+ self.offset = 0
+ self.stored = len(self._data)
+
+ elif len(self._data) - self.offset >= len(data):
+ self._data[self.offset: self.offset + len(data)] = data
+ self.offset = self.offset + len(data)
+ self.stored += len(data)
+ else:
+ self._data[self.offset:] = data[:len(self._data) - self.offset]
+ self._data[:len(data) - (len(self._data) - self.offset)] = \
+ data[-len(data) + (len(self._data) - self.offset):]
+ self.offset = len(data) - (len(self._data) - self.offset)
+ self.stored += len(data)
+
+ if len(self._data) <= self.stored:
+ self.read = self._read
+
+ def read(self, number=None, step=1):
+ """Read the ring Buffer. Number can be positive or negative.
+ Positive values will give the latest information, negative values will
+ give the newest added information from the buffer. (in normal order)
+
+ Before the buffer is filled once: This returns just None
+ """
+ return np.array([])
+
+ def _read(self, number=None, step=1):
+ """Read the ring Buffer. Number can be positive or negative.
+ Positive values will give the latest information, negative values will
+ give the newest added information from the buffer. (in normal order)
+ """
+ if number == None:
+ number = len(self._data) // step
+
+ number *= step
+ assert abs(number) <= len(self._data), \
+ 'Number to read*step must be smaller then length'
+
+ if number < 0:
+ if abs(number) <= self.offset:
+ return self._data[self.offset + number:self.offset:step]
+
+ spam = (self.offset - 1) % step
+
+ return np.concatenate(
+ (self._data[step - spam - 1 + self.offset + number::step],
+ self._data[spam:self.offset:step]))
+
+ if number - (len(self._data) - self.offset) > 0:
+ spam = ((self.offset + number) - self.offset - 1) % step
+ return np.concatenate(
+ (self._data[self.offset:self.offset + number:step],
+ self._data[spam:number -
+ (len(self._data) - self.offset):step]))
+
+ return self._data[self.offset:self.offset + number:step].copy()
diff --git a/plugins/camera_sensor/__init__.py b/plugins/camera_sensor/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/camera_sensor/__init__.py
diff --git a/plugins/camera_sensor/camera_sensor.py b/plugins/camera_sensor/camera_sensor.py
new file mode 100644
index 0000000..dd2e6ef
--- /dev/null
+++ b/plugins/camera_sensor/camera_sensor.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python
+#Copyright (c) 2011 Walter Bender
+#
+# 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 gst
+import gtk
+from fcntl import ioctl
+
+from plugins.camera_sensor.tacamera import Camera
+from plugins.camera_sensor.v4l2 import v4l2_control, V4L2_CID_AUTOGAIN, \
+ VIDIOC_G_CTRL, VIDIOC_S_CTRL
+
+from plugins.plugin import Plugin
+
+from TurtleArt.tapalette import make_palette
+from TurtleArt.talogo import media_blocks_dictionary, primitive_dictionary
+from TurtleArt.tautils import get_path, debug_output
+
+import logging
+_logger = logging.getLogger('turtleart-activity camera plugin')
+
+
+class Camera_sensor(Plugin):
+
+ def __init__(self, parent):
+ self._parent = parent
+ self._status = False
+
+ v4l2src = gst.element_factory_make('v4l2src')
+ if v4l2src.props.device_name is not None:
+
+ if self._parent.running_sugar:
+ self._imagepath = get_path(self._parent.activity,
+ 'data/turtlepic.png')
+ else:
+ self._imagepath = '/tmp/turtlepic.png'
+ self._camera = Camera(self._imagepath)
+
+ self._status = True
+
+ def setup(self):
+ palette = make_palette('sensor',
+ colors=["#FF6060", "#A06060"],
+ help_string=_('Palette of sensor blocks'))
+
+ # set up camera-specific blocks
+ if self._status:
+ primitive_dictionary['luminance'] = self.prim_read_camera
+ palette.add_block('luminance',
+ style='box-style',
+ label=_('brightness'),
+ help_string=_('light level detected by camera'),
+ value_block=True,
+ prim_name='luminance')
+ self._parent.lc.def_prim('luminance', 0,
+ lambda self: primitive_dictionary['luminance'](True))
+
+ # Depreciated block
+ primitive_dictionary['read_camera'] = self.prim_read_camera
+ palette.add_block('read_camera',
+ hidden=True,
+ style='box-style',
+ label=_('brightness'),
+ help_string=_('Average RGB color from camera \
+is pushed to the stack'),
+ value_block=True,
+ prim_name='luminance')
+ self._parent.lc.def_prim('read_camera', 0,
+ lambda self: primitive_dictionary['read_camera'](True))
+
+ media_blocks_dictionary['camera'] = self.prim_take_picture
+ palette.add_block('camera',
+ style='box-style-media',
+ label=' ',
+ default='CAMERA',
+ help_string=_('camera output'),
+ content_block=True)
+
+ def stop(self):
+ # This gets called by the stop button
+ if self._status:
+ self._camera.stop_camera_input()
+
+ def _status_report(self):
+ debug_output('Reporting camera status: %s' % (str(self._status)))
+ return self._status
+
+ # Block primitives used in talogo
+
+ def prim_take_picture(self):
+ if self._status:
+ ''' method called by media block '''
+ self._camera.save_camera_input_to_file()
+ self._camera.stop_camera_input()
+ self._parent.lc.filepath = self._imagepath
+
+ def prim_read_camera(self, luminance_only=False):
+ """ Read average pixel from camera and push b, g, r to the stack """
+ pixbuf = None
+ array = None
+ w = int((self._parent.canvas.width * self._parent.lc.scale) / 100.)
+ h = int((self._parent.canvas.height * self._parent.lc.scale) / 100.)
+ if w > 0 and h > 0 and self._status:
+ try:
+ self._video_capture_device = open('/dev/video0', 'rw')
+ except:
+ self._video_capture_device = None
+ _logger.debug('video capture device not available')
+
+ if self._video_capture_device is not None:
+ self._ag_control = v4l2_control(V4L2_CID_AUTOGAIN)
+ try:
+ ioctl(self._video_capture_device, VIDIOC_G_CTRL,
+ self._ag_control)
+ self._ag_control.value = 0 # disable AUTOGAIN
+ ioctl(self._video_capture_device, VIDIOC_S_CTRL,
+ self._ag_control)
+ except:
+ _logger.debug('AUTOGAIN control not available')
+ pass
+
+ if self._video_capture_device is not None:
+ self._video_capture_device.close()
+
+ self._camera.save_camera_input_to_file()
+ self._camera.stop_camera_input()
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self._imagepath,
+ w, h)
+ try:
+ array = pixbuf.get_pixels()
+ except:
+ array = None
+
+ if array is not None:
+ length = len(array) / 3
+ r, g, b, i = 0, 0, 0, 0
+ for j in range(length):
+ r += ord(array[i])
+ i += 1
+ g += ord(array[i])
+ i += 1
+ b += ord(array[i])
+ i += 1
+ if luminance_only:
+ lum = int((r * 0.3 + g * 0.6 + b * 0.1) / length)
+ self._parent.lc.update_label_value('luminance', lum)
+ return lum
+ else:
+ self._parent.lc.heap.append(int((b / length)))
+ self._parent.lc.heap.append(int((g / length)))
+ self._parent.lc.heap.append(int((r / length)))
+ else:
+ if luminance_only:
+ return -1
+ else:
+ self._parent.lc.heap.append(-1)
+ self._parent.lc.heap.append(-1)
+ self._parent.lc.heap.append(-1)
diff --git a/plugins/camera_sensor/tacamera.py b/plugins/camera_sensor/tacamera.py
new file mode 100644
index 0000000..2177288
--- /dev/null
+++ b/plugins/camera_sensor/tacamera.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+#Copyright (c) 2010, Walter Bender
+#Copyright (c) 2010, Tony Forster
+
+#Permission is hereby granted, free of charge, to any person obtaining a copy
+#of this software and associated documentation files (the "Software"), to deal
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
+
+import gst, time
+
+GST_PIPE = ['v4l2src', 'ffmpegcolorspace', 'pngenc']
+
+class Camera():
+ """ A class for representing the video camera """
+
+ def __init__(self, imagepath):
+ GST_PIPE.append('filesink location=%s' % imagepath)
+ self.pipe = gst.parse_launch('!'.join(GST_PIPE))
+ self.bus = self.pipe.get_bus()
+
+ def save_camera_input_to_file(self):
+ """ Grab a frame from the camera """
+ self.pipe.set_state(gst.STATE_PLAYING)
+ self.bus.poll(gst.MESSAGE_EOS, -1)
+
+ def stop_camera_input(self):
+ self.pipe.set_state(gst.STATE_NULL)
+
diff --git a/plugins/camera_sensor/v4l2.py b/plugins/camera_sensor/v4l2.py
new file mode 100644
index 0000000..9c052fd
--- /dev/null
+++ b/plugins/camera_sensor/v4l2.py
@@ -0,0 +1,1914 @@
+# Python bindings for the v4l2 userspace api
+
+# Copyright (C) 1999-2009 the contributors
+
+# 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.
+
+# Alternatively you can redistribute this file under the terms of the
+# BSD license as stated below:
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. The names of its contributors may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+Python bindings for the v4l2 userspace api in Linux 2.6.34
+"""
+
+# see linux/videodev2.h
+
+import ctypes
+
+
+_IOC_NRBITS = 8
+_IOC_TYPEBITS = 8
+_IOC_SIZEBITS = 14
+_IOC_DIRBITS = 2
+
+_IOC_NRSHIFT = 0
+_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS
+_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS
+_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS
+
+_IOC_NONE = 0
+_IOC_WRITE = 1
+_IOC_READ = 2
+
+
+def _IOC(dir_, type_, nr, size):
+ return (
+ ctypes.c_int32(dir_ << _IOC_DIRSHIFT).value |
+ ctypes.c_int32(ord(type_) << _IOC_TYPESHIFT).value |
+ ctypes.c_int32(nr << _IOC_NRSHIFT).value |
+ ctypes.c_int32(size << _IOC_SIZESHIFT).value)
+
+
+def _IOC_TYPECHECK(t):
+ return ctypes.sizeof(t)
+
+
+def _IO(type_, nr):
+ return _IOC(_IOC_NONE, type_, nr, 0)
+
+
+def _IOW(type_, nr, size):
+ return _IOC(_IOC_WRITE, type_, nr, _IOC_TYPECHECK(size))
+
+
+def _IOR(type_, nr, size):
+ return _IOC(_IOC_READ, type_, nr, _IOC_TYPECHECK(size))
+
+
+def _IOWR(type_, nr, size):
+ return _IOC(_IOC_READ | _IOC_WRITE, type_, nr, _IOC_TYPECHECK(size))
+
+
+#
+# type alias
+#
+
+enum = ctypes.c_uint
+c_int = ctypes.c_int
+
+
+#
+# time
+#
+
+class timeval(ctypes.Structure):
+ _fields_ = [
+ ('secs', ctypes.c_long),
+ ('usecs', ctypes.c_long),
+ ]
+
+
+#
+# v4l2
+#
+
+
+VIDEO_MAX_FRAME = 32
+
+
+VID_TYPE_CAPTURE = 1
+VID_TYPE_TUNER = 2
+VID_TYPE_TELETEXT = 4
+VID_TYPE_OVERLAY = 8
+VID_TYPE_CHROMAKEY = 16
+VID_TYPE_CLIPPING = 32
+VID_TYPE_FRAMERAM = 64
+VID_TYPE_SCALES = 128
+VID_TYPE_MONOCHROME = 256
+VID_TYPE_SUBCAPTURE = 512
+VID_TYPE_MPEG_DECODER = 1024
+VID_TYPE_MPEG_ENCODER = 2048
+VID_TYPE_MJPEG_DECODER = 4096
+VID_TYPE_MJPEG_ENCODER = 8192
+
+
+def v4l2_fourcc(a, b, c, d):
+ return ord(a) | (ord(b) << 8) | (ord(c) << 16) | (ord(d) << 24)
+
+
+v4l2_field = enum
+(
+ V4L2_FIELD_ANY,
+ V4L2_FIELD_NONE,
+ V4L2_FIELD_TOP,
+ V4L2_FIELD_BOTTOM,
+ V4L2_FIELD_INTERLACED,
+ V4L2_FIELD_SEQ_TB,
+ V4L2_FIELD_SEQ_BT,
+ V4L2_FIELD_ALTERNATE,
+ V4L2_FIELD_INTERLACED_TB,
+ V4L2_FIELD_INTERLACED_BT,
+) = range(10)
+
+
+def V4L2_FIELD_HAS_TOP(field):
+ return (
+ field == V4L2_FIELD_TOP or
+ field == V4L2_FIELD_INTERLACED or
+ field == V4L2_FIELD_INTERLACED_TB or
+ field == V4L2_FIELD_INTERLACED_BT or
+ field == V4L2_FIELD_SEQ_TB or
+ field == V4L2_FIELD_SEQ_BT)
+
+
+def V4L2_FIELD_HAS_BOTTOM(field):
+ return (
+ field == V4L2_FIELD_BOTTOM or
+ field == V4L2_FIELD_INTERLACED or
+ field == V4L2_FIELD_INTERLACED_TB or
+ field == V4L2_FIELD_INTERLACED_BT or
+ field == V4L2_FIELD_SEQ_TB or
+ field == V4L2_FIELD_SEQ_BT)
+
+
+def V4L2_FIELD_HAS_BOTH(field):
+ return (
+ field == V4L2_FIELD_INTERLACED or
+ field == V4L2_FIELD_INTERLACED_TB or
+ field == V4L2_FIELD_INTERLACED_BT or
+ field == V4L2_FIELD_SEQ_TB or
+ field == V4L2_FIELD_SEQ_BT)
+
+
+v4l2_buf_type = enum
+(
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_BUF_TYPE_VIDEO_OVERLAY,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_BUF_TYPE_VBI_OUTPUT,
+ V4L2_BUF_TYPE_SLICED_VBI_CAPTURE,
+ V4L2_BUF_TYPE_SLICED_VBI_OUTPUT,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY,
+ V4L2_BUF_TYPE_PRIVATE,
+) = range(1, 9) + [0x80]
+
+
+v4l2_ctrl_type = enum
+(
+ V4L2_CTRL_TYPE_INTEGER,
+ V4L2_CTRL_TYPE_BOOLEAN,
+ V4L2_CTRL_TYPE_MENU,
+ V4L2_CTRL_TYPE_BUTTON,
+ V4L2_CTRL_TYPE_INTEGER64,
+ V4L2_CTRL_TYPE_CTRL_CLASS,
+ V4L2_CTRL_TYPE_STRING,
+) = range(1, 8)
+
+
+v4l2_tuner_type = enum
+(
+ V4L2_TUNER_RADIO,
+ V4L2_TUNER_ANALOG_TV,
+ V4L2_TUNER_DIGITAL_TV,
+) = range(1, 4)
+
+
+v4l2_memory = enum
+(
+ V4L2_MEMORY_MMAP,
+ V4L2_MEMORY_USERPTR,
+ V4L2_MEMORY_OVERLAY,
+) = range(1, 4)
+
+
+v4l2_colorspace = enum
+(
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_COLORSPACE_SMPTE240M,
+ V4L2_COLORSPACE_REC709,
+ V4L2_COLORSPACE_BT878,
+ V4L2_COLORSPACE_470_SYSTEM_M,
+ V4L2_COLORSPACE_470_SYSTEM_BG,
+ V4L2_COLORSPACE_JPEG,
+ V4L2_COLORSPACE_SRGB,
+) = range(1, 9)
+
+
+v4l2_priority = enum
+(
+ V4L2_PRIORITY_UNSET,
+ V4L2_PRIORITY_BACKGROUND,
+ V4L2_PRIORITY_INTERACTIVE,
+ V4L2_PRIORITY_RECORD,
+ V4L2_PRIORITY_DEFAULT,
+) = range(0, 4) + [2]
+
+
+class v4l2_rect(ctypes.Structure):
+ _fields_ = [
+ ('left', ctypes.c_int32),
+ ('top', ctypes.c_int32),
+ ('width', ctypes.c_int32),
+ ('height', ctypes.c_int32),
+ ]
+
+
+class v4l2_fract(ctypes.Structure):
+ _fields_ = [
+ ('numerator', ctypes.c_uint32),
+ ('denominator', ctypes.c_uint32),
+ ]
+
+
+#
+# Driver capabilities
+#
+
+class v4l2_capability(ctypes.Structure):
+ _fields_ = [
+ ('driver', ctypes.c_char * 16),
+ ('card', ctypes.c_char * 32),
+ ('bus_info', ctypes.c_char * 32),
+ ('version', ctypes.c_uint32),
+ ('capabilities', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+
+#
+# Values for 'capabilities' field
+#
+
+V4L2_CAP_VIDEO_CAPTURE = 0x00000001
+V4L2_CAP_VIDEO_OUTPUT = 0x00000002
+V4L2_CAP_VIDEO_OVERLAY = 0x00000004
+V4L2_CAP_VBI_CAPTURE = 0x00000010
+V4L2_CAP_VBI_OUTPUT = 0x00000020
+V4L2_CAP_SLICED_VBI_CAPTURE = 0x00000040
+V4L2_CAP_SLICED_VBI_OUTPUT = 0x00000080
+V4L2_CAP_RDS_CAPTURE = 0x00000100
+V4L2_CAP_VIDEO_OUTPUT_OVERLAY = 0x00000200
+V4L2_CAP_HW_FREQ_SEEK = 0x00000400
+V4L2_CAP_RDS_OUTPUT = 0x00000800
+
+V4L2_CAP_TUNER = 0x00010000
+V4L2_CAP_AUDIO = 0x00020000
+V4L2_CAP_RADIO = 0x00040000
+V4L2_CAP_MODULATOR = 0x00080000
+
+V4L2_CAP_READWRITE = 0x01000000
+V4L2_CAP_ASYNCIO = 0x02000000
+V4L2_CAP_STREAMING = 0x04000000
+
+
+#
+# Video image format
+#
+
+class v4l2_pix_format(ctypes.Structure):
+ _fields_ = [
+ ('width', ctypes.c_uint32),
+ ('height', ctypes.c_uint32),
+ ('pixelformat', ctypes.c_uint32),
+ ('field', v4l2_field),
+ ('bytesperline', ctypes.c_uint32),
+ ('sizeimage', ctypes.c_uint32),
+ ('colorspace', v4l2_colorspace),
+ ('priv', ctypes.c_uint32),
+ ]
+
+# RGB formats
+V4L2_PIX_FMT_RGB332 = v4l2_fourcc('R', 'G', 'B', '1')
+V4L2_PIX_FMT_RGB444 = v4l2_fourcc('R', '4', '4', '4')
+V4L2_PIX_FMT_RGB555 = v4l2_fourcc('R', 'G', 'B', 'O')
+V4L2_PIX_FMT_RGB565 = v4l2_fourcc('R', 'G', 'B', 'P')
+V4L2_PIX_FMT_RGB555X = v4l2_fourcc('R', 'G', 'B', 'Q')
+V4L2_PIX_FMT_RGB565X = v4l2_fourcc('R', 'G', 'B', 'R')
+V4L2_PIX_FMT_BGR24 = v4l2_fourcc('B', 'G', 'R', '3')
+V4L2_PIX_FMT_RGB24 = v4l2_fourcc('R', 'G', 'B', '3')
+V4L2_PIX_FMT_BGR32 = v4l2_fourcc('B', 'G', 'R', '4')
+V4L2_PIX_FMT_RGB32 = v4l2_fourcc('R', 'G', 'B', '4')
+
+# Grey formats
+V4L2_PIX_FMT_GREY = v4l2_fourcc('G', 'R', 'E', 'Y')
+V4L2_PIX_FMT_Y10 = v4l2_fourcc('Y', '1', '0', ' ')
+V4L2_PIX_FMT_Y16 = v4l2_fourcc('Y', '1', '6', ' ')
+
+# Palette formats
+V4L2_PIX_FMT_PAL8 = v4l2_fourcc('P', 'A', 'L', '8')
+
+# Luminance+Chrominance formats
+V4L2_PIX_FMT_YVU410 = v4l2_fourcc('Y', 'V', 'U', '9')
+V4L2_PIX_FMT_YVU420 = v4l2_fourcc('Y', 'V', '1', '2')
+V4L2_PIX_FMT_YUYV = v4l2_fourcc('Y', 'U', 'Y', 'V')
+V4L2_PIX_FMT_YYUV = v4l2_fourcc('Y', 'Y', 'U', 'V')
+V4L2_PIX_FMT_YVYU = v4l2_fourcc('Y', 'V', 'Y', 'U')
+V4L2_PIX_FMT_UYVY = v4l2_fourcc('U', 'Y', 'V', 'Y')
+V4L2_PIX_FMT_VYUY = v4l2_fourcc('V', 'Y', 'U', 'Y')
+V4L2_PIX_FMT_YUV422P = v4l2_fourcc('4', '2', '2', 'P')
+V4L2_PIX_FMT_YUV411P = v4l2_fourcc('4', '1', '1', 'P')
+V4L2_PIX_FMT_Y41P = v4l2_fourcc('Y', '4', '1', 'P')
+V4L2_PIX_FMT_YUV444 = v4l2_fourcc('Y', '4', '4', '4')
+V4L2_PIX_FMT_YUV555 = v4l2_fourcc('Y', 'U', 'V', 'O')
+V4L2_PIX_FMT_YUV565 = v4l2_fourcc('Y', 'U', 'V', 'P')
+V4L2_PIX_FMT_YUV32 = v4l2_fourcc('Y', 'U', 'V', '4')
+V4L2_PIX_FMT_YUV410 = v4l2_fourcc('Y', 'U', 'V', '9')
+V4L2_PIX_FMT_YUV420 = v4l2_fourcc('Y', 'U', '1', '2')
+V4L2_PIX_FMT_HI240 = v4l2_fourcc('H', 'I', '2', '4')
+V4L2_PIX_FMT_HM12 = v4l2_fourcc('H', 'M', '1', '2')
+
+# two planes -- one Y, one Cr + Cb interleaved
+V4L2_PIX_FMT_NV12 = v4l2_fourcc('N', 'V', '1', '2')
+V4L2_PIX_FMT_NV21 = v4l2_fourcc('N', 'V', '2', '1')
+V4L2_PIX_FMT_NV16 = v4l2_fourcc('N', 'V', '1', '6')
+V4L2_PIX_FMT_NV61 = v4l2_fourcc('N', 'V', '6', '1')
+
+# Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm
+V4L2_PIX_FMT_SBGGR8 = v4l2_fourcc('B', 'A', '8', '1')
+V4L2_PIX_FMT_SGBRG8 = v4l2_fourcc('G', 'B', 'R', 'G')
+V4L2_PIX_FMT_SGRBG8 = v4l2_fourcc('G', 'R', 'B', 'G')
+V4L2_PIX_FMT_SRGGB8 = v4l2_fourcc('R', 'G', 'G', 'B')
+V4L2_PIX_FMT_SBGGR10 = v4l2_fourcc('B', 'G', '1', '0')
+V4L2_PIX_FMT_SGBRG10 = v4l2_fourcc('G', 'B', '1', '0')
+V4L2_PIX_FMT_SGRBG10 = v4l2_fourcc('B', 'A', '1', '0')
+V4L2_PIX_FMT_SRGGB10 = v4l2_fourcc('R', 'G', '1', '0')
+V4L2_PIX_FMT_SGRBG10DPCM8 = v4l2_fourcc('B', 'D', '1', '0')
+V4L2_PIX_FMT_SBGGR16 = v4l2_fourcc('B', 'Y', 'R', '2')
+
+# compressed formats
+V4L2_PIX_FMT_MJPEG = v4l2_fourcc('M', 'J', 'P', 'G')
+V4L2_PIX_FMT_JPEG = v4l2_fourcc('J', 'P', 'E', 'G')
+V4L2_PIX_FMT_DV = v4l2_fourcc('d', 'v', 's', 'd')
+V4L2_PIX_FMT_MPEG = v4l2_fourcc('M', 'P', 'E', 'G')
+
+# Vendor-specific formats
+V4L2_PIX_FMT_CPIA1 = v4l2_fourcc('C', 'P', 'I', 'A')
+V4L2_PIX_FMT_WNVA = v4l2_fourcc('W', 'N', 'V', 'A')
+V4L2_PIX_FMT_SN9C10X = v4l2_fourcc('S', '9', '1', '0')
+V4L2_PIX_FMT_SN9C20X_I420 = v4l2_fourcc('S', '9', '2', '0')
+V4L2_PIX_FMT_PWC1 = v4l2_fourcc('P', 'W', 'C', '1')
+V4L2_PIX_FMT_PWC2 = v4l2_fourcc('P', 'W', 'C', '2')
+V4L2_PIX_FMT_ET61X251 = v4l2_fourcc('E', '6', '2', '5')
+V4L2_PIX_FMT_SPCA501 = v4l2_fourcc('S', '5', '0', '1')
+V4L2_PIX_FMT_SPCA505 = v4l2_fourcc('S', '5', '0', '5')
+V4L2_PIX_FMT_SPCA508 = v4l2_fourcc('S', '5', '0', '8')
+V4L2_PIX_FMT_SPCA561 = v4l2_fourcc('S', '5', '6', '1')
+V4L2_PIX_FMT_PAC207 = v4l2_fourcc('P', '2', '0', '7')
+V4L2_PIX_FMT_MR97310A = v4l2_fourcc('M', '3', '1', '0')
+V4L2_PIX_FMT_SN9C2028 = v4l2_fourcc('S', 'O', 'N', 'X')
+V4L2_PIX_FMT_SQ905C = v4l2_fourcc('9', '0', '5', 'C')
+V4L2_PIX_FMT_PJPG = v4l2_fourcc('P', 'J', 'P', 'G')
+V4L2_PIX_FMT_OV511 = v4l2_fourcc('O', '5', '1', '1')
+V4L2_PIX_FMT_OV518 = v4l2_fourcc('O', '5', '1', '8')
+V4L2_PIX_FMT_STV0680 = v4l2_fourcc('S', '6', '8', '0')
+
+
+#
+# Format enumeration
+#
+
+class v4l2_fmtdesc(ctypes.Structure):
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('type', ctypes.c_int),
+ ('flags', ctypes.c_uint32),
+ ('description', ctypes.c_char * 32),
+ ('pixelformat', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+V4L2_FMT_FLAG_COMPRESSED = 0x0001
+V4L2_FMT_FLAG_EMULATED = 0x0002
+
+
+#
+# Experimental frame size and frame rate enumeration
+#
+
+v4l2_frmsizetypes = enum
+(
+ V4L2_FRMSIZE_TYPE_DISCRETE,
+ V4L2_FRMSIZE_TYPE_CONTINUOUS,
+ V4L2_FRMSIZE_TYPE_STEPWISE,
+) = range(1, 4)
+
+
+class v4l2_frmsize_discrete(ctypes.Structure):
+ _fields_ = [
+ ('width', ctypes.c_uint32),
+ ('height', ctypes.c_uint32),
+ ]
+
+
+class v4l2_frmsize_stepwise(ctypes.Structure):
+ _fields_ = [
+ ('min_width', ctypes.c_uint32),
+ ('min_height', ctypes.c_uint32),
+ ('step_width', ctypes.c_uint32),
+ ('min_height', ctypes.c_uint32),
+ ('max_height', ctypes.c_uint32),
+ ('step_height', ctypes.c_uint32),
+ ]
+
+
+class v4l2_frmsizeenum(ctypes.Structure):
+ class _u(ctypes.Union):
+ _fields_ = [
+ ('discrete', v4l2_frmsize_discrete),
+ ('stepwise', v4l2_frmsize_stepwise),
+ ]
+
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('pixel_format', ctypes.c_uint32),
+ ('type', ctypes.c_uint32),
+ ('_u', _u),
+ ('reserved', ctypes.c_uint32 * 2)
+ ]
+
+ _anonymous_ = ('_u',)
+
+
+#
+# Frame rate enumeration
+#
+
+v4l2_frmivaltypes = enum
+(
+ V4L2_FRMIVAL_TYPE_DISCRETE,
+ V4L2_FRMIVAL_TYPE_CONTINUOUS,
+ V4L2_FRMIVAL_TYPE_STEPWISE,
+) = range(1, 4)
+
+
+class v4l2_frmival_stepwise(ctypes.Structure):
+ _fields_ = [
+ ('min', v4l2_fract),
+ ('max', v4l2_fract),
+ ('step', v4l2_fract),
+ ]
+
+
+class v4l2_frmivalenum(ctypes.Structure):
+ class _u(ctypes.Union):
+ _fields_ = [
+ ('discrete', v4l2_fract),
+ ('stepwise', v4l2_frmival_stepwise),
+ ]
+
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('pixel_format', ctypes.c_uint32),
+ ('width', ctypes.c_uint32),
+ ('height', ctypes.c_uint32),
+ ('type', ctypes.c_uint32),
+ ('_u', _u),
+ ('reserved', ctypes.c_uint32 * 2),
+ ]
+
+ _anonymous_ = ('_u',)
+
+
+#
+# Timecode
+#
+
+class v4l2_timecode(ctypes.Structure):
+ _fields_ = [
+ ('type', ctypes.c_uint32),
+ ('flags', ctypes.c_uint32),
+ ('frames', ctypes.c_uint8),
+ ('seconds', ctypes.c_uint8),
+ ('minutes', ctypes.c_uint8),
+ ('hours', ctypes.c_uint8),
+ ('userbits', ctypes.c_uint8 * 4),
+ ]
+
+
+V4L2_TC_TYPE_24FPS = 1
+V4L2_TC_TYPE_25FPS = 2
+V4L2_TC_TYPE_30FPS = 3
+V4L2_TC_TYPE_50FPS = 4
+V4L2_TC_TYPE_60FPS = 5
+
+V4L2_TC_FLAG_DROPFRAME = 0x0001
+V4L2_TC_FLAG_COLORFRAME = 0x0002
+V4L2_TC_USERBITS_field = 0x000C
+V4L2_TC_USERBITS_USERDEFINED = 0x0000
+V4L2_TC_USERBITS_8BITCHARS = 0x0008
+
+
+class v4l2_jpegcompression(ctypes.Structure):
+ _fields_ = [
+ ('quality', ctypes.c_int),
+ ('APPn', ctypes.c_int),
+ ('APP_len', ctypes.c_int),
+ ('APP_data', ctypes.c_char * 60),
+ ('COM_len', ctypes.c_int),
+ ('COM_data', ctypes.c_char * 60),
+ ('jpeg_markers', ctypes.c_uint32),
+ ]
+
+
+V4L2_JPEG_MARKER_DHT = 1 << 3
+V4L2_JPEG_MARKER_DQT = 1 << 4
+V4L2_JPEG_MARKER_DRI = 1 << 5
+V4L2_JPEG_MARKER_COM = 1 << 6
+V4L2_JPEG_MARKER_APP = 1 << 7
+
+
+#
+# Memory-mapping buffers
+#
+
+class v4l2_requestbuffers(ctypes.Structure):
+ _fields_ = [
+ ('count', ctypes.c_uint32),
+ ('type', v4l2_buf_type),
+ ('memory', v4l2_memory),
+ ('reserved', ctypes.c_uint32 * 2),
+ ]
+
+
+class v4l2_buffer(ctypes.Structure):
+ class _u(ctypes.Union):
+ _fields_ = [
+ ('offset', ctypes.c_uint32),
+ ('userptr', ctypes.c_ulong),
+ ]
+
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('type', v4l2_buf_type),
+ ('bytesused', ctypes.c_uint32),
+ ('flags', ctypes.c_uint32),
+ ('field', v4l2_field),
+ ('timestamp', timeval),
+ ('timecode', v4l2_timecode),
+ ('sequence', ctypes.c_uint32),
+ ('memory', v4l2_memory),
+ ('m', _u),
+ ('length', ctypes.c_uint32),
+ ('input', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32),
+ ]
+
+
+V4L2_BUF_FLAG_MAPPED = 0x0001
+V4L2_BUF_FLAG_QUEUED = 0x0002
+V4L2_BUF_FLAG_DONE = 0x0004
+V4L2_BUF_FLAG_KEYFRAME = 0x0008
+V4L2_BUF_FLAG_PFRAME = 0x0010
+V4L2_BUF_FLAG_BFRAME = 0x0020
+V4L2_BUF_FLAG_TIMECODE = 0x0100
+V4L2_BUF_FLAG_INPUT = 0x0200
+
+
+#
+# Overlay preview
+#
+
+class v4l2_framebuffer(ctypes.Structure):
+ _fields_ = [
+ ('capability', ctypes.c_uint32),
+ ('flags', ctypes.c_uint32),
+ ('base', ctypes.c_void_p),
+ ('fmt', v4l2_pix_format),
+ ]
+
+V4L2_FBUF_CAP_EXTERNOVERLAY = 0x0001
+V4L2_FBUF_CAP_CHROMAKEY = 0x0002
+V4L2_FBUF_CAP_LIST_CLIPPING = 0x0004
+V4L2_FBUF_CAP_BITMAP_CLIPPING = 0x0008
+V4L2_FBUF_CAP_LOCAL_ALPHA = 0x0010
+V4L2_FBUF_CAP_GLOBAL_ALPHA = 0x0020
+V4L2_FBUF_CAP_LOCAL_INV_ALPHA = 0x0040
+V4L2_FBUF_CAP_SRC_CHROMAKEY = 0x0080
+
+V4L2_FBUF_FLAG_PRIMARY = 0x0001
+V4L2_FBUF_FLAG_OVERLAY = 0x0002
+V4L2_FBUF_FLAG_CHROMAKEY = 0x0004
+V4L2_FBUF_FLAG_LOCAL_ALPHA = 0x0008
+V4L2_FBUF_FLAG_GLOBAL_ALPHA = 0x0010
+V4L2_FBUF_FLAG_LOCAL_INV_ALPHA = 0x0020
+V4L2_FBUF_FLAG_SRC_CHROMAKEY = 0x0040
+
+
+class v4l2_clip(ctypes.Structure):
+ pass
+v4l2_clip._fields_ = [
+ ('c', v4l2_rect),
+ ('next', ctypes.POINTER(v4l2_clip)),
+]
+
+
+class v4l2_window(ctypes.Structure):
+ _fields_ = [
+ ('w', v4l2_rect),
+ ('field', v4l2_field),
+ ('chromakey', ctypes.c_uint32),
+ ('clips', ctypes.POINTER(v4l2_clip)),
+ ('clipcount', ctypes.c_uint32),
+ ('bitmap', ctypes.c_void_p),
+ ('global_alpha', ctypes.c_uint8),
+ ]
+
+
+#
+# Capture parameters
+#
+
+class v4l2_captureparm(ctypes.Structure):
+ _fields_ = [
+ ('capability', ctypes.c_uint32),
+ ('capturemode', ctypes.c_uint32),
+ ('timeperframe', v4l2_fract),
+ ('extendedmode', ctypes.c_uint32),
+ ('readbuffers', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+
+V4L2_MODE_HIGHQUALITY = 0x0001
+V4L2_CAP_TIMEPERFRAME = 0x1000
+
+
+class v4l2_outputparm(ctypes.Structure):
+ _fields_ = [
+ ('capability', ctypes.c_uint32),
+ ('outputmode', ctypes.c_uint32),
+ ('timeperframe', v4l2_fract),
+ ('extendedmode', ctypes.c_uint32),
+ ('writebuffers', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+
+#
+# Input image cropping
+#
+
+class v4l2_cropcap(ctypes.Structure):
+ _fields_ = [
+ ('type', v4l2_buf_type),
+ ('bounds', v4l2_rect),
+ ('defrect', v4l2_rect),
+ ('pixelaspect', v4l2_fract),
+ ]
+
+
+class v4l2_crop(ctypes.Structure):
+ _fields_ = [
+ ('type', ctypes.c_int),
+ ('c', v4l2_rect),
+ ]
+
+
+#
+# Analog video standard
+#
+
+v4l2_std_id = ctypes.c_uint64
+
+
+V4L2_STD_PAL_B = 0x00000001
+V4L2_STD_PAL_B1 = 0x00000002
+V4L2_STD_PAL_G = 0x00000004
+V4L2_STD_PAL_H = 0x00000008
+V4L2_STD_PAL_I = 0x00000010
+V4L2_STD_PAL_D = 0x00000020
+V4L2_STD_PAL_D1 = 0x00000040
+V4L2_STD_PAL_K = 0x00000080
+
+V4L2_STD_PAL_M = 0x00000100
+V4L2_STD_PAL_N = 0x00000200
+V4L2_STD_PAL_Nc = 0x00000400
+V4L2_STD_PAL_60 = 0x00000800
+
+V4L2_STD_NTSC_M = 0x00001000
+V4L2_STD_NTSC_M_JP = 0x00002000
+V4L2_STD_NTSC_443 = 0x00004000
+V4L2_STD_NTSC_M_KR = 0x00008000
+
+V4L2_STD_SECAM_B = 0x00010000
+V4L2_STD_SECAM_D = 0x00020000
+V4L2_STD_SECAM_G = 0x00040000
+V4L2_STD_SECAM_H = 0x00080000
+V4L2_STD_SECAM_K = 0x00100000
+V4L2_STD_SECAM_K1 = 0x00200000
+V4L2_STD_SECAM_L = 0x00400000
+V4L2_STD_SECAM_LC = 0x00800000
+
+V4L2_STD_ATSC_8_VSB = 0x01000000
+V4L2_STD_ATSC_16_VSB = 0x02000000
+
+
+# some common needed stuff
+V4L2_STD_PAL_BG = (V4L2_STD_PAL_B | V4L2_STD_PAL_B1 | V4L2_STD_PAL_G)
+V4L2_STD_PAL_DK = (V4L2_STD_PAL_D | V4L2_STD_PAL_D1 | V4L2_STD_PAL_K)
+V4L2_STD_PAL = (V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_H | V4L2_STD_PAL_I)
+V4L2_STD_NTSC = (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR)
+V4L2_STD_SECAM_DK = (V4L2_STD_SECAM_D | V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1)
+V4L2_STD_SECAM = (V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H | V4L2_STD_SECAM_DK | V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)
+
+V4L2_STD_525_60 = (V4L2_STD_PAL_M | V4L2_STD_PAL_60 | V4L2_STD_NTSC | V4L2_STD_NTSC_443)
+V4L2_STD_625_50 = (V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | V4L2_STD_SECAM)
+V4L2_STD_ATSC = (V4L2_STD_ATSC_8_VSB | V4L2_STD_ATSC_16_VSB)
+
+V4L2_STD_UNKNOWN = 0
+V4L2_STD_ALL = (V4L2_STD_525_60 | V4L2_STD_625_50)
+
+# some merged standards
+V4L2_STD_MN = (V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | V4L2_STD_NTSC)
+V4L2_STD_B = (V4L2_STD_PAL_B | V4L2_STD_PAL_B1 | V4L2_STD_SECAM_B)
+V4L2_STD_GH = (V4L2_STD_PAL_G | V4L2_STD_PAL_H|V4L2_STD_SECAM_G | V4L2_STD_SECAM_H)
+V4L2_STD_DK = (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK)
+
+
+class v4l2_standard(ctypes.Structure):
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('id', v4l2_std_id),
+ ('name', ctypes.c_char * 24),
+ ('frameperiod', v4l2_fract),
+ ('framelines', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+
+#
+# Video timings dv preset
+#
+
+class v4l2_dv_preset(ctypes.Structure):
+ _fields_ = [
+ ('preset', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4)
+ ]
+
+
+#
+# DV preset enumeration
+#
+
+class v4l2_dv_enum_preset(ctypes.Structure):
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('preset', ctypes.c_uint32),
+ ('name', ctypes.c_char * 32),
+ ('width', ctypes.c_uint32),
+ ('height', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+#
+# DV preset values
+#
+
+V4L2_DV_INVALID = 0
+V4L2_DV_480P59_94 = 1
+V4L2_DV_576P50 = 2
+V4L2_DV_720P24 = 3
+V4L2_DV_720P25 = 4
+V4L2_DV_720P30 = 5
+V4L2_DV_720P50 = 6
+V4L2_DV_720P59_94 = 7
+V4L2_DV_720P60 = 8
+V4L2_DV_1080I29_97 = 9
+V4L2_DV_1080I30 = 10
+V4L2_DV_1080I25 = 11
+V4L2_DV_1080I50 = 12
+V4L2_DV_1080I60 = 13
+V4L2_DV_1080P24 = 14
+V4L2_DV_1080P25 = 15
+V4L2_DV_1080P30 = 16
+V4L2_DV_1080P50 = 17
+V4L2_DV_1080P60 = 18
+
+
+#
+# DV BT timings
+#
+
+class v4l2_bt_timings(ctypes.Structure):
+ _fields_ = [
+ ('width', ctypes.c_uint32),
+ ('height', ctypes.c_uint32),
+ ('interlaced', ctypes.c_uint32),
+ ('polarities', ctypes.c_uint32),
+ ('pixelclock', ctypes.c_uint64),
+ ('hfrontporch', ctypes.c_uint32),
+ ('hsync', ctypes.c_uint32),
+ ('hbackporch', ctypes.c_uint32),
+ ('vfrontporch', ctypes.c_uint32),
+ ('vsync', ctypes.c_uint32),
+ ('vbackporch', ctypes.c_uint32),
+ ('il_vfrontporch', ctypes.c_uint32),
+ ('il_vsync', ctypes.c_uint32),
+ ('il_vbackporch', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 16),
+ ]
+
+ _pack_ = True
+
+# Interlaced or progressive format
+V4L2_DV_PROGRESSIVE = 0
+V4L2_DV_INTERLACED = 1
+
+# Polarities. If bit is not set, it is assumed to be negative polarity
+V4L2_DV_VSYNC_POS_POL = 0x00000001
+V4L2_DV_HSYNC_POS_POL = 0x00000002
+
+
+class v4l2_dv_timings(ctypes.Structure):
+ class _u(ctypes.Union):
+ _fields_ = [
+ ('bt', v4l2_bt_timings),
+ ('reserved', ctypes.c_uint32 * 32),
+ ]
+
+ _fields_ = [
+ ('type', ctypes.c_uint32),
+ ('_u', _u),
+ ]
+
+ _anonymous_ = ('_u',)
+ _pack_ = True
+
+
+# Values for the type field
+V4L2_DV_BT_656_1120 = 0
+
+
+#
+# Video inputs
+#
+
+class v4l2_input(ctypes.Structure):
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('name', ctypes.c_char * 32),
+ ('type', ctypes.c_uint32),
+ ('audioset', ctypes.c_uint32),
+ ('tuner', ctypes.c_uint32),
+ ('std', v4l2_std_id),
+ ('status', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+
+V4L2_INPUT_TYPE_TUNER = 1
+V4L2_INPUT_TYPE_CAMERA = 2
+
+V4L2_IN_ST_NO_POWER = 0x00000001
+V4L2_IN_ST_NO_SIGNAL = 0x00000002
+V4L2_IN_ST_NO_COLOR = 0x00000004
+
+V4L2_IN_ST_HFLIP = 0x00000010
+V4L2_IN_ST_VFLIP = 0x00000020
+
+V4L2_IN_ST_NO_H_LOCK = 0x00000100
+V4L2_IN_ST_COLOR_KILL = 0x00000200
+
+V4L2_IN_ST_NO_SYNC = 0x00010000
+V4L2_IN_ST_NO_EQU = 0x00020000
+V4L2_IN_ST_NO_CARRIER = 0x00040000
+
+V4L2_IN_ST_MACROVISION = 0x01000000
+V4L2_IN_ST_NO_ACCESS = 0x02000000
+V4L2_IN_ST_VTR = 0x04000000
+
+V4L2_IN_CAP_PRESETS = 0x00000001
+V4L2_IN_CAP_CUSTOM_TIMINGS = 0x00000002
+V4L2_IN_CAP_STD = 0x00000004
+
+#
+# Video outputs
+#
+
+class v4l2_output(ctypes.Structure):
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('name', ctypes.c_char * 32),
+ ('type', ctypes.c_uint32),
+ ('audioset', ctypes.c_uint32),
+ ('modulator', ctypes.c_uint32),
+ ('std', v4l2_std_id),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+
+V4L2_OUTPUT_TYPE_MODULATOR = 1
+V4L2_OUTPUT_TYPE_ANALOG = 2
+V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY = 3
+
+V4L2_OUT_CAP_PRESETS = 0x00000001
+V4L2_OUT_CAP_CUSTOM_TIMINGS = 0x00000002
+V4L2_OUT_CAP_STD = 0x00000004
+
+#
+# Controls
+#
+
+class v4l2_control(ctypes.Structure):
+ _fields_ = [
+ ('id', ctypes.c_uint32),
+ ('value', ctypes.c_int32),
+ ]
+
+
+class v4l2_ext_control(ctypes.Structure):
+ class _u(ctypes.Union):
+ _fields_ = [
+ ('value', ctypes.c_int32),
+ ('value64', ctypes.c_int64),
+ ('reserved', ctypes.c_void_p),
+ ]
+
+ _fields_ = [
+ ('id', ctypes.c_uint32),
+ ('reserved2', ctypes.c_uint32 * 2),
+ ('_u', _u)
+ ]
+
+ _anonymous_ = ('_u',)
+ _pack_ = True
+
+
+class v4l2_ext_controls(ctypes.Structure):
+ _fields_ = [
+ ('ctrl_class', ctypes.c_uint32),
+ ('count', ctypes.c_uint32),
+ ('error_idx', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 2),
+ ('controls', ctypes.POINTER(v4l2_ext_control)),
+ ]
+
+
+V4L2_CTRL_CLASS_USER = 0x00980000
+V4L2_CTRL_CLASS_MPEG = 0x00990000
+V4L2_CTRL_CLASS_CAMERA = 0x009a0000
+V4L2_CTRL_CLASS_FM_TX = 0x009b0000
+
+
+def V4L2_CTRL_ID_MASK():
+ return 0x0fffffff
+
+
+def V4L2_CTRL_ID2CLASS(id_):
+ return id_ & 0x0fff0000 # unsigned long
+
+
+def V4L2_CTRL_DRIVER_PRIV(id_):
+ return (id_ & 0xffff) >= 0x1000
+
+
+class v4l2_queryctrl(ctypes.Structure):
+ _fields_ = [
+ ('id', ctypes.c_uint32),
+ ('type', v4l2_ctrl_type),
+ ('name', ctypes.c_char * 32),
+ ('minimum', ctypes.c_int32),
+ ('maximum', ctypes.c_int32),
+ ('step', ctypes.c_int32),
+ ('default', ctypes.c_int32),
+ ('flags', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 2),
+ ]
+
+
+class v4l2_querymenu(ctypes.Structure):
+ _fields_ = [
+ ('id', ctypes.c_uint32),
+ ('index', ctypes.c_uint32),
+ ('name', ctypes.c_char * 32),
+ ('reserved', ctypes.c_uint32),
+ ]
+
+
+V4L2_CTRL_FLAG_DISABLED = 0x0001
+V4L2_CTRL_FLAG_GRABBED = 0x0002
+V4L2_CTRL_FLAG_READ_ONLY = 0x0004
+V4L2_CTRL_FLAG_UPDATE = 0x0008
+V4L2_CTRL_FLAG_INACTIVE = 0x0010
+V4L2_CTRL_FLAG_SLIDER = 0x0020
+V4L2_CTRL_FLAG_WRITE_ONLY = 0x0040
+
+V4L2_CTRL_FLAG_NEXT_CTRL = 0x80000000
+
+V4L2_CID_BASE = V4L2_CTRL_CLASS_USER | 0x900
+V4L2_CID_USER_BASE = V4L2_CID_BASE
+V4L2_CID_PRIVATE_BASE = 0x08000000
+
+V4L2_CID_USER_CLASS = V4L2_CTRL_CLASS_USER | 1
+V4L2_CID_BRIGHTNESS = V4L2_CID_BASE + 0
+V4L2_CID_CONTRAST = V4L2_CID_BASE + 1
+V4L2_CID_SATURATION = V4L2_CID_BASE + 2
+V4L2_CID_HUE = V4L2_CID_BASE + 3
+V4L2_CID_AUDIO_VOLUME = V4L2_CID_BASE + 5
+V4L2_CID_AUDIO_BALANCE = V4L2_CID_BASE + 6
+V4L2_CID_AUDIO_BASS = V4L2_CID_BASE + 7
+V4L2_CID_AUDIO_TREBLE = V4L2_CID_BASE + 8
+V4L2_CID_AUDIO_MUTE = V4L2_CID_BASE + 9
+V4L2_CID_AUDIO_LOUDNESS = V4L2_CID_BASE + 10
+V4L2_CID_BLACK_LEVEL = V4L2_CID_BASE + 11 # Deprecated
+V4L2_CID_AUTO_WHITE_BALANCE = V4L2_CID_BASE + 12
+V4L2_CID_DO_WHITE_BALANCE = V4L2_CID_BASE + 13
+V4L2_CID_RED_BALANCE = V4L2_CID_BASE + 14
+V4L2_CID_BLUE_BALANCE = V4L2_CID_BASE + 15
+V4L2_CID_GAMMA = V4L2_CID_BASE + 16
+V4L2_CID_WHITENESS = V4L2_CID_GAMMA # Deprecated
+V4L2_CID_EXPOSURE = V4L2_CID_BASE + 17
+V4L2_CID_AUTOGAIN = V4L2_CID_BASE + 18
+V4L2_CID_GAIN = V4L2_CID_BASE + 19
+V4L2_CID_HFLIP = V4L2_CID_BASE + 20
+V4L2_CID_VFLIP = V4L2_CID_BASE + 21
+
+# Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET
+V4L2_CID_HCENTER = V4L2_CID_BASE + 22
+V4L2_CID_VCENTER = V4L2_CID_BASE + 23
+
+V4L2_CID_POWER_LINE_FREQUENCY = V4L2_CID_BASE + 24
+
+v4l2_power_line_frequency = enum
+(
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
+) = range(3)
+
+V4L2_CID_HUE_AUTO = V4L2_CID_BASE + 25
+V4L2_CID_WHITE_BALANCE_TEMPERATURE = V4L2_CID_BASE + 26
+V4L2_CID_SHARPNESS = V4L2_CID_BASE + 27
+V4L2_CID_BACKLIGHT_COMPENSATION = V4L2_CID_BASE + 28
+V4L2_CID_CHROMA_AGC = V4L2_CID_BASE + 29
+V4L2_CID_COLOR_KILLER = V4L2_CID_BASE + 30
+V4L2_CID_COLORFX = V4L2_CID_BASE + 31
+
+v4l2_colorfx = enum
+(
+ V4L2_COLORFX_NONE,
+ V4L2_COLORFX_BW,
+ V4L2_COLORFX_SEPIA,
+) = range(3)
+
+V4L2_CID_AUTOBRIGHTNESS = V4L2_CID_BASE + 32
+V4L2_CID_BAND_STOP_FILTER = V4L2_CID_BASE + 33
+
+V4L2_CID_ROTATE = V4L2_CID_BASE + 34
+V4L2_CID_BG_COLOR = V4L2_CID_BASE + 35
+V4L2_CID_LASTP1 = V4L2_CID_BASE + 36
+
+V4L2_CID_MPEG_BASE = V4L2_CTRL_CLASS_MPEG | 0x900
+V4L2_CID_MPEG_CLASS = V4L2_CTRL_CLASS_MPEG | 1
+
+# MPEG streams
+V4L2_CID_MPEG_STREAM_TYPE = V4L2_CID_MPEG_BASE + 0
+
+v4l2_mpeg_stream_type = enum
+(
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+ V4L2_MPEG_STREAM_TYPE_MPEG1_SS,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_DVD,
+ V4L2_MPEG_STREAM_TYPE_MPEG1_VCD,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD,
+) = range(6)
+
+V4L2_CID_MPEG_STREAM_PID_PMT = V4L2_CID_MPEG_BASE + 1
+V4L2_CID_MPEG_STREAM_PID_AUDIO = V4L2_CID_MPEG_BASE + 2
+V4L2_CID_MPEG_STREAM_PID_VIDEO = V4L2_CID_MPEG_BASE + 3
+V4L2_CID_MPEG_STREAM_PID_PCR = V4L2_CID_MPEG_BASE + 4
+V4L2_CID_MPEG_STREAM_PES_ID_AUDIO = V4L2_CID_MPEG_BASE + 5
+V4L2_CID_MPEG_STREAM_PES_ID_VIDEO = V4L2_CID_MPEG_BASE + 6
+V4L2_CID_MPEG_STREAM_VBI_FMT = V4L2_CID_MPEG_BASE + 7
+
+v4l2_mpeg_stream_vbi_fmt = enum
+(
+ V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ V4L2_MPEG_STREAM_VBI_FMT_IVTV,
+) = range(2)
+
+V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ = V4L2_CID_MPEG_BASE + 100
+
+v4l2_mpeg_audio_sampling_freq = enum
+(
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000,
+) = range(3)
+
+V4L2_CID_MPEG_AUDIO_ENCODING = V4L2_CID_MPEG_BASE + 101
+
+v4l2_mpeg_audio_encoding = enum
+(
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_1,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_3,
+ V4L2_MPEG_AUDIO_ENCODING_AAC,
+ V4L2_MPEG_AUDIO_ENCODING_AC3,
+) = range(5)
+
+V4L2_CID_MPEG_AUDIO_L1_BITRATE = V4L2_CID_MPEG_BASE + 102
+
+v4l2_mpeg_audio_l1_bitrate = enum
+(
+ V4L2_MPEG_AUDIO_L1_BITRATE_32K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_64K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_96K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_128K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_160K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_192K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_224K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_256K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_288K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_320K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_352K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_384K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_416K,
+ V4L2_MPEG_AUDIO_L1_BITRATE_448K,
+) = range(14)
+
+V4L2_CID_MPEG_AUDIO_L2_BITRATE = V4L2_CID_MPEG_BASE + 103
+
+v4l2_mpeg_audio_l2_bitrate = enum
+(
+ V4L2_MPEG_AUDIO_L2_BITRATE_32K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_48K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_56K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_64K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_80K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_96K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_112K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_128K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_160K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_192K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_224K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_320K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K,
+) = range(14)
+
+V4L2_CID_MPEG_AUDIO_L3_BITRATE = V4L2_CID_MPEG_BASE + 104
+
+v4l2_mpeg_audio_l3_bitrate = enum
+(
+ V4L2_MPEG_AUDIO_L3_BITRATE_32K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_40K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_48K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_56K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_64K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_80K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_96K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_112K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_128K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_160K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_192K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_224K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_256K,
+ V4L2_MPEG_AUDIO_L3_BITRATE_320K,
+) = range(14)
+
+V4L2_CID_MPEG_AUDIO_MODE = V4L2_CID_MPEG_BASE + 105
+
+v4l2_mpeg_audio_mode = enum
+(
+ V4L2_MPEG_AUDIO_MODE_STEREO,
+ V4L2_MPEG_AUDIO_MODE_JOINT_STEREO,
+ V4L2_MPEG_AUDIO_MODE_DUAL,
+ V4L2_MPEG_AUDIO_MODE_MONO,
+) = range(4)
+
+V4L2_CID_MPEG_AUDIO_MODE_EXTENSION = V4L2_CID_MPEG_BASE + 106
+
+v4l2_mpeg_audio_mode_extension = enum
+(
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16,
+) = range(4)
+
+V4L2_CID_MPEG_AUDIO_EMPHASIS = V4L2_CID_MPEG_BASE + 107
+
+v4l2_mpeg_audio_emphasis = enum
+(
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE,
+ V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS,
+ V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17,
+) = range(3)
+
+V4L2_CID_MPEG_AUDIO_CRC = V4L2_CID_MPEG_BASE + 108
+
+v4l2_mpeg_audio_crc = enum
+(
+ V4L2_MPEG_AUDIO_CRC_NONE,
+ V4L2_MPEG_AUDIO_CRC_CRC16,
+) = range(2)
+
+V4L2_CID_MPEG_AUDIO_MUTE = V4L2_CID_MPEG_BASE + 109
+V4L2_CID_MPEG_AUDIO_AAC_BITRATE = V4L2_CID_MPEG_BASE + 110
+V4L2_CID_MPEG_AUDIO_AC3_BITRATE = V4L2_CID_MPEG_BASE + 111
+
+v4l2_mpeg_audio_ac3_bitrate = enum
+(
+ V4L2_MPEG_AUDIO_AC3_BITRATE_32K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_40K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_48K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_56K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_64K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_80K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_96K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_112K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_128K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_160K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_192K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_224K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_320K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_448K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_512K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_576K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_640K,
+) = range(19)
+
+V4L2_CID_MPEG_VIDEO_ENCODING = V4L2_CID_MPEG_BASE + 200
+
+v4l2_mpeg_video_encoding = enum
+(
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC,
+) = range(3)
+
+V4L2_CID_MPEG_VIDEO_ASPECT = V4L2_CID_MPEG_BASE + 201
+
+v4l2_mpeg_video_aspect = enum
+(
+ V4L2_MPEG_VIDEO_ASPECT_1x1,
+ V4L2_MPEG_VIDEO_ASPECT_4x3,
+ V4L2_MPEG_VIDEO_ASPECT_16x9,
+ V4L2_MPEG_VIDEO_ASPECT_221x100,
+) = range(4)
+
+V4L2_CID_MPEG_VIDEO_B_FRAMES = V4L2_CID_MPEG_BASE + 202
+V4L2_CID_MPEG_VIDEO_GOP_SIZE = V4L2_CID_MPEG_BASE + 203
+V4L2_CID_MPEG_VIDEO_GOP_CLOSURE = V4L2_CID_MPEG_BASE + 204
+V4L2_CID_MPEG_VIDEO_PULLDOWN = V4L2_CID_MPEG_BASE + 205
+V4L2_CID_MPEG_VIDEO_BITRATE_MODE = V4L2_CID_MPEG_BASE + 206
+
+v4l2_mpeg_video_bitrate_mode = enum
+(
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+) = range(2)
+
+V4L2_CID_MPEG_VIDEO_BITRATE = V4L2_CID_MPEG_BASE + 207
+V4L2_CID_MPEG_VIDEO_BITRATE_PEAK = V4L2_CID_MPEG_BASE + 208
+V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION = V4L2_CID_MPEG_BASE + 209
+V4L2_CID_MPEG_VIDEO_MUTE = V4L2_CID_MPEG_BASE + 210
+V4L2_CID_MPEG_VIDEO_MUTE_YUV = V4L2_CID_MPEG_BASE + 211
+
+V4L2_CID_MPEG_CX2341X_BASE = V4L2_CTRL_CLASS_MPEG | 0x1000
+V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE = V4L2_CID_MPEG_CX2341X_BASE + 0
+
+v4l2_mpeg_cx2341x_video_spatial_filter_mode = enum
+(
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO,
+) = range(2)
+
+V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER = V4L2_CID_MPEG_CX2341X_BASE + 1
+V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 2
+
+v4l2_mpeg_cx2341x_video_luma_spatial_filter_type = enum
+(
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+) = range(5)
+
+V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 3
+
+v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type = enum
+(
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+) = range(2)
+
+V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE = V4L2_CID_MPEG_CX2341X_BASE + 4
+
+v4l2_mpeg_cx2341x_video_temporal_filter_mode = enum
+(
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO,
+) = range(2)
+
+V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER = V4L2_CID_MPEG_CX2341X_BASE + 5
+V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 6
+
+v4l2_mpeg_cx2341x_video_median_filter_type = enum
+(
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG,
+) = range(5)
+
+V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM = V4L2_CID_MPEG_CX2341X_BASE + 7
+V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP = V4L2_CID_MPEG_CX2341X_BASE + 8
+V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM = V4L2_CID_MPEG_CX2341X_BASE + 9
+V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP = V4L2_CID_MPEG_CX2341X_BASE + 10
+V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS = V4L2_CID_MPEG_CX2341X_BASE + 11
+
+V4L2_CID_CAMERA_CLASS_BASE = V4L2_CTRL_CLASS_CAMERA | 0x900
+V4L2_CID_CAMERA_CLASS = V4L2_CTRL_CLASS_CAMERA | 1
+
+V4L2_CID_EXPOSURE_AUTO = V4L2_CID_CAMERA_CLASS_BASE + 1
+
+v4l2_exposure_auto_type = enum
+(
+ V4L2_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL,
+ V4L2_EXPOSURE_SHUTTER_PRIORITY,
+ V4L2_EXPOSURE_APERTURE_PRIORITY,
+) = range(4)
+
+V4L2_CID_EXPOSURE_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 2
+V4L2_CID_EXPOSURE_AUTO_PRIORITY = V4L2_CID_CAMERA_CLASS_BASE + 3
+
+V4L2_CID_PAN_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 4
+V4L2_CID_TILT_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 5
+V4L2_CID_PAN_RESET = V4L2_CID_CAMERA_CLASS_BASE + 6
+V4L2_CID_TILT_RESET = V4L2_CID_CAMERA_CLASS_BASE + 7
+
+V4L2_CID_PAN_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 8
+V4L2_CID_TILT_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 9
+
+V4L2_CID_FOCUS_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 10
+V4L2_CID_FOCUS_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 11
+V4L2_CID_FOCUS_AUTO = V4L2_CID_CAMERA_CLASS_BASE + 12
+
+V4L2_CID_ZOOM_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 13
+V4L2_CID_ZOOM_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 14
+V4L2_CID_ZOOM_CONTINUOUS = V4L2_CID_CAMERA_CLASS_BASE + 15
+
+V4L2_CID_PRIVACY = V4L2_CID_CAMERA_CLASS_BASE + 16
+
+V4L2_CID_FM_TX_CLASS_BASE = V4L2_CTRL_CLASS_FM_TX | 0x900
+V4L2_CID_FM_TX_CLASS = V4L2_CTRL_CLASS_FM_TX | 1
+
+V4L2_CID_RDS_TX_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 1
+V4L2_CID_RDS_TX_PI = V4L2_CID_FM_TX_CLASS_BASE + 2
+V4L2_CID_RDS_TX_PTY = V4L2_CID_FM_TX_CLASS_BASE + 3
+V4L2_CID_RDS_TX_PS_NAME = V4L2_CID_FM_TX_CLASS_BASE + 5
+V4L2_CID_RDS_TX_RADIO_TEXT = V4L2_CID_FM_TX_CLASS_BASE + 6
+
+V4L2_CID_AUDIO_LIMITER_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 64
+V4L2_CID_AUDIO_LIMITER_RELEASE_TIME = V4L2_CID_FM_TX_CLASS_BASE + 65
+V4L2_CID_AUDIO_LIMITER_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 66
+
+V4L2_CID_AUDIO_COMPRESSION_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 80
+V4L2_CID_AUDIO_COMPRESSION_GAIN = V4L2_CID_FM_TX_CLASS_BASE + 81
+V4L2_CID_AUDIO_COMPRESSION_THRESHOLD = V4L2_CID_FM_TX_CLASS_BASE + 82
+V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME = V4L2_CID_FM_TX_CLASS_BASE + 83
+V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME = V4L2_CID_FM_TX_CLASS_BASE + 84
+
+V4L2_CID_PILOT_TONE_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 96
+V4L2_CID_PILOT_TONE_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 97
+V4L2_CID_PILOT_TONE_FREQUENCY = V4L2_CID_FM_TX_CLASS_BASE + 98
+
+V4L2_CID_TUNE_PREEMPHASIS = V4L2_CID_FM_TX_CLASS_BASE + 112
+
+v4l2_preemphasis = enum
+(
+ V4L2_PREEMPHASIS_DISABLED,
+ V4L2_PREEMPHASIS_50_uS,
+ V4L2_PREEMPHASIS_75_uS,
+) = range(3)
+
+V4L2_CID_TUNE_POWER_LEVEL = V4L2_CID_FM_TX_CLASS_BASE + 113
+V4L2_CID_TUNE_ANTENNA_CAPACITOR = V4L2_CID_FM_TX_CLASS_BASE + 114
+
+
+#
+# Tuning
+#
+
+class v4l2_tuner(ctypes.Structure):
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('name', ctypes.c_char * 32),
+ ('type', v4l2_tuner_type),
+ ('capability', ctypes.c_uint32),
+ ('rangelow', ctypes.c_uint32),
+ ('rangehigh', ctypes.c_uint32),
+ ('rxsubchans', ctypes.c_uint32),
+ ('audmode', ctypes.c_uint32),
+ ('signal', ctypes.c_int32),
+ ('afc', ctypes.c_int32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+
+class v4l2_modulator(ctypes.Structure):
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('name', ctypes.c_char * 32),
+ ('capability', ctypes.c_uint32),
+ ('rangelow', ctypes.c_uint32),
+ ('rangehigh', ctypes.c_uint32),
+ ('txsubchans', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ]
+
+
+V4L2_TUNER_CAP_LOW = 0x0001
+V4L2_TUNER_CAP_NORM = 0x0002
+V4L2_TUNER_CAP_STEREO = 0x0010
+V4L2_TUNER_CAP_LANG2 = 0x0020
+V4L2_TUNER_CAP_SAP = 0x0020
+V4L2_TUNER_CAP_LANG1 = 0x0040
+V4L2_TUNER_CAP_RDS = 0x0080
+
+V4L2_TUNER_SUB_MONO = 0x0001
+V4L2_TUNER_SUB_STEREO = 0x0002
+V4L2_TUNER_SUB_LANG2 = 0x0004
+V4L2_TUNER_SUB_SAP = 0x0004
+V4L2_TUNER_SUB_LANG1 = 0x0008
+V4L2_TUNER_SUB_RDS = 0x0010
+
+V4L2_TUNER_MODE_MONO = 0x0000
+V4L2_TUNER_MODE_STEREO = 0x0001
+V4L2_TUNER_MODE_LANG2 = 0x0002
+V4L2_TUNER_MODE_SAP = 0x0002
+V4L2_TUNER_MODE_LANG1 = 0x0003
+V4L2_TUNER_MODE_LANG1_LANG2 = 0x0004
+
+
+class v4l2_frequency(ctypes.Structure):
+ _fields_ = [
+ ('tuner', ctypes.c_uint32),
+ ('type', v4l2_tuner_type),
+ ('frequency', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 8),
+ ]
+
+
+class v4l2_hw_freq_seek(ctypes.Structure):
+ _fields_ = [
+ ('tuner', ctypes.c_uint32),
+ ('type', v4l2_tuner_type),
+ ('seek_upward', ctypes.c_uint32),
+ ('wrap_around', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 8),
+ ]
+
+
+#
+# RDS
+#
+
+class v4l2_rds_data(ctypes.Structure):
+ _fields_ = [
+ ('lsb', ctypes.c_char),
+ ('msb', ctypes.c_char),
+ ('block', ctypes.c_char),
+ ]
+
+ _pack_ = True
+
+
+V4L2_RDS_BLOCK_MSK = 0x7
+V4L2_RDS_BLOCK_A = 0
+V4L2_RDS_BLOCK_B = 1
+V4L2_RDS_BLOCK_C = 2
+V4L2_RDS_BLOCK_D = 3
+V4L2_RDS_BLOCK_C_ALT = 4
+V4L2_RDS_BLOCK_INVALID = 7
+
+V4L2_RDS_BLOCK_CORRECTED = 0x40
+V4L2_RDS_BLOCK_ERROR = 0x80
+
+
+#
+# Audio
+#
+
+class v4l2_audio(ctypes.Structure):
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('name', ctypes.c_char * 32),
+ ('capability', ctypes.c_uint32),
+ ('mode', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 2),
+ ]
+
+
+V4L2_AUDCAP_STEREO = 0x00001
+V4L2_AUDCAP_AVL = 0x00002
+
+V4L2_AUDMODE_AVL = 0x00001
+
+
+class v4l2_audioout(ctypes.Structure):
+ _fields_ = [
+ ('index', ctypes.c_uint32),
+ ('name', ctypes.c_char * 32),
+ ('capability', ctypes.c_uint32),
+ ('mode', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 2),
+ ]
+
+
+#
+# Mpeg services (experimental)
+#
+
+V4L2_ENC_IDX_FRAME_I = 0
+V4L2_ENC_IDX_FRAME_P = 1
+V4L2_ENC_IDX_FRAME_B = 2
+V4L2_ENC_IDX_FRAME_MASK = 0xf
+
+
+class v4l2_enc_idx_entry(ctypes.Structure):
+ _fields_ = [
+ ('offset', ctypes.c_uint64),
+ ('pts', ctypes.c_uint64),
+ ('length', ctypes.c_uint32),
+ ('flags', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 2),
+ ]
+
+
+V4L2_ENC_IDX_ENTRIES = 64
+
+
+class v4l2_enc_idx(ctypes.Structure):
+ _fields_ = [
+ ('entries', ctypes.c_uint32),
+ ('entries_cap', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 4),
+ ('entry', v4l2_enc_idx_entry * V4L2_ENC_IDX_ENTRIES),
+ ]
+
+
+V4L2_ENC_CMD_START = 0
+V4L2_ENC_CMD_STOP = 1
+V4L2_ENC_CMD_PAUSE = 2
+V4L2_ENC_CMD_RESUME = 3
+
+V4L2_ENC_CMD_STOP_AT_GOP_END = 1 << 0
+
+
+class v4l2_encoder_cmd(ctypes.Structure):
+ class _u(ctypes.Union):
+ class _s(ctypes.Structure):
+ _fields_ = [
+ ('data', ctypes.c_uint32 * 8),
+ ]
+
+ _fields_ = [
+ ('raw', _s),
+ ]
+
+ _fields_ = [
+ ('cmd', ctypes.c_uint32),
+ ('flags', ctypes.c_uint32),
+ ('_u', _u),
+ ]
+
+ _anonymous_ = ('_u',)
+
+
+#
+# Data services (VBI)
+#
+
+class v4l2_vbi_format(ctypes.Structure):
+ _fields_ = [
+ ('sampling_rate', ctypes.c_uint32),
+ ('offset', ctypes.c_uint32),
+ ('samples_per_line', ctypes.c_uint32),
+ ('sample_format', ctypes.c_uint32),
+ ('start', ctypes.c_int32 * 2),
+ ('count', ctypes.c_uint32 * 2),
+ ('flags', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 2),
+ ]
+
+
+V4L2_VBI_UNSYNC = 1 << 0
+V4L2_VBI_INTERLACED = 1 << 1
+
+
+class v4l2_sliced_vbi_format(ctypes.Structure):
+ _fields_ = [
+ ('service_set', ctypes.c_uint16),
+ ('service_lines', ctypes.c_uint16 * 2 * 24),
+ ('io_size', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32 * 2),
+ ]
+
+
+V4L2_SLICED_TELETEXT_B = 0x0001
+V4L2_SLICED_VPS = 0x0400
+V4L2_SLICED_CAPTION_525 = 0x1000
+V4L2_SLICED_WSS_625 = 0x4000
+V4L2_SLICED_VBI_525 = V4L2_SLICED_CAPTION_525
+V4L2_SLICED_VBI_625 = (
+ V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625)
+
+
+class v4l2_sliced_vbi_cap(ctypes.Structure):
+ _fields_ = [
+ ('service_set', ctypes.c_uint16),
+ ('service_lines', ctypes.c_uint16 * 2 * 24),
+ ('type', v4l2_buf_type),
+ ('reserved', ctypes.c_uint32 * 3),
+ ]
+
+
+class v4l2_sliced_vbi_data(ctypes.Structure):
+ _fields_ = [
+ ('id', ctypes.c_uint32),
+ ('field', ctypes.c_uint32),
+ ('line', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32),
+ ('data', ctypes.c_char * 48),
+ ]
+
+
+#
+# Sliced VBI data inserted into MPEG Streams
+#
+
+
+V4L2_MPEG_VBI_IVTV_TELETEXT_B = 1
+V4L2_MPEG_VBI_IVTV_CAPTION_525 = 4
+V4L2_MPEG_VBI_IVTV_WSS_625 = 5
+V4L2_MPEG_VBI_IVTV_VPS = 7
+
+
+class v4l2_mpeg_vbi_itv0_line(ctypes.Structure):
+ _fields_ = [
+ ('id', ctypes.c_char),
+ ('data', ctypes.c_char * 42),
+ ]
+
+ _pack_ = True
+
+
+class v4l2_mpeg_vbi_itv0(ctypes.Structure):
+ _fields_ = [
+ ('linemask', ctypes.c_uint32 * 2), # how to define __le32 in ctypes?
+ ('line', v4l2_mpeg_vbi_itv0_line * 35),
+ ]
+
+ _pack_ = True
+
+
+class v4l2_mpeg_vbi_ITV0(ctypes.Structure):
+ _fields_ = [
+ ('line', v4l2_mpeg_vbi_itv0_line * 36),
+ ]
+
+ _pack_ = True
+
+
+V4L2_MPEG_VBI_IVTV_MAGIC0 = "itv0"
+V4L2_MPEG_VBI_IVTV_MAGIC1 = "ITV0"
+
+
+class v4l2_mpeg_vbi_fmt_ivtv(ctypes.Structure):
+ class _u(ctypes.Union):
+ _fields_ = [
+ ('itv0', v4l2_mpeg_vbi_itv0),
+ ('ITV0', v4l2_mpeg_vbi_ITV0),
+ ]
+
+ _fields_ = [
+ ('magic', ctypes.c_char * 4),
+ ('_u', _u)
+ ]
+
+ _anonymous_ = ('_u',)
+ _pack_ = True
+
+
+#
+# Aggregate structures
+#
+
+class v4l2_format(ctypes.Structure):
+ class _u(ctypes.Union):
+ _fields_ = [
+ ('pix', v4l2_pix_format),
+ ('win', v4l2_window),
+ ('vbi', v4l2_vbi_format),
+ ('sliced', v4l2_sliced_vbi_format),
+ ('raw_data', ctypes.c_char * 200),
+ ]
+
+ _fields_ = [
+ ('type', v4l2_buf_type),
+ ('fmt', _u),
+ ]
+
+
+class v4l2_streamparm(ctypes.Structure):
+ class _u(ctypes.Union):
+ _fields_ = [
+ ('capture', v4l2_captureparm),
+ ('output', v4l2_outputparm),
+ ('raw_data', ctypes.c_char * 200),
+ ]
+
+ _fields_ = [
+ ('type', v4l2_buf_type),
+ ('parm', _u)
+ ]
+
+
+#
+# Advanced debugging
+#
+
+V4L2_CHIP_MATCH_HOST = 0
+V4L2_CHIP_MATCH_I2C_DRIVER = 1
+V4L2_CHIP_MATCH_I2C_ADDR = 2
+V4L2_CHIP_MATCH_AC97 = 3
+
+
+class v4l2_dbg_match(ctypes.Structure):
+ class _u(ctypes.Union):
+ _fields_ = [
+ ('addr', ctypes.c_uint32),
+ ('name', ctypes.c_char * 32),
+ ]
+
+ _fields_ = [
+ ('type', ctypes.c_uint32),
+ ('_u', _u),
+ ]
+
+ _anonymous_ = ('_u',)
+ _pack_ = True
+
+
+class v4l2_dbg_register(ctypes.Structure):
+ _fields_ = [
+ ('match', v4l2_dbg_match),
+ ('size', ctypes.c_uint32),
+ ('reg', ctypes.c_uint64),
+ ('val', ctypes.c_uint64),
+ ]
+
+ _pack_ = True
+
+
+class v4l2_dbg_chip_ident(ctypes.Structure):
+ _fields_ = [
+ ('match', v4l2_dbg_match),
+ ('ident', ctypes.c_uint32),
+ ('revision', ctypes.c_uint32),
+ ]
+
+ _pack_ = True
+
+
+#
+# ioctl codes for video devices
+#
+
+VIDIOC_QUERYCAP = _IOR('V', 0, v4l2_capability)
+VIDIOC_RESERVED = _IO('V', 1)
+VIDIOC_ENUM_FMT = _IOWR('V', 2, v4l2_fmtdesc)
+VIDIOC_G_FMT = _IOWR('V', 4, v4l2_format)
+VIDIOC_S_FMT = _IOWR('V', 5, v4l2_format)
+VIDIOC_REQBUFS = _IOWR('V', 8, v4l2_requestbuffers)
+VIDIOC_QUERYBUF = _IOWR('V', 9, v4l2_buffer)
+VIDIOC_G_FBUF = _IOR('V', 10, v4l2_framebuffer)
+VIDIOC_S_FBUF = _IOW('V', 11, v4l2_framebuffer)
+VIDIOC_OVERLAY = _IOW('V', 14, ctypes.c_int)
+VIDIOC_QBUF = _IOWR('V', 15, v4l2_buffer)
+VIDIOC_DQBUF = _IOWR('V', 17, v4l2_buffer)
+VIDIOC_STREAMON = _IOW('V', 18, ctypes.c_int)
+VIDIOC_STREAMOFF = _IOW('V', 19, ctypes.c_int)
+VIDIOC_G_PARM = _IOWR('V', 21, v4l2_streamparm)
+VIDIOC_S_PARM = _IOWR('V', 22, v4l2_streamparm)
+VIDIOC_G_STD = _IOR('V', 23, v4l2_std_id)
+VIDIOC_S_STD = _IOW('V', 24, v4l2_std_id)
+VIDIOC_ENUMSTD = _IOWR('V', 25, v4l2_standard)
+VIDIOC_ENUMINPUT = _IOWR('V', 26, v4l2_input)
+VIDIOC_G_CTRL = _IOWR('V', 27, v4l2_control)
+VIDIOC_S_CTRL = _IOWR('V', 28, v4l2_control)
+VIDIOC_G_TUNER = _IOWR('V', 29, v4l2_tuner)
+VIDIOC_S_TUNER = _IOW('V', 30, v4l2_tuner)
+VIDIOC_G_AUDIO = _IOR('V', 33, v4l2_audio)
+VIDIOC_S_AUDIO = _IOW('V', 34, v4l2_audio)
+VIDIOC_QUERYCTRL = _IOWR('V', 36, v4l2_queryctrl)
+VIDIOC_QUERYMENU = _IOWR('V', 37, v4l2_querymenu)
+VIDIOC_G_INPUT = _IOR('V', 38, ctypes.c_int)
+VIDIOC_S_INPUT = _IOWR('V', 39, ctypes.c_int)
+VIDIOC_G_OUTPUT = _IOR('V', 46, ctypes.c_int)
+VIDIOC_S_OUTPUT = _IOWR('V', 47, ctypes.c_int)
+VIDIOC_ENUMOUTPUT = _IOWR('V', 48, v4l2_output)
+VIDIOC_G_AUDOUT = _IOR('V', 49, v4l2_audioout)
+VIDIOC_S_AUDOUT = _IOW('V', 50, v4l2_audioout)
+VIDIOC_G_MODULATOR = _IOWR('V', 54, v4l2_modulator)
+VIDIOC_S_MODULATOR = _IOW('V', 55, v4l2_modulator)
+VIDIOC_G_FREQUENCY = _IOWR('V', 56, v4l2_frequency)
+VIDIOC_S_FREQUENCY = _IOW('V', 57, v4l2_frequency)
+VIDIOC_CROPCAP = _IOWR('V', 58, v4l2_cropcap)
+VIDIOC_G_CROP = _IOWR('V', 59, v4l2_crop)
+VIDIOC_S_CROP = _IOW('V', 60, v4l2_crop)
+VIDIOC_G_JPEGCOMP = _IOR('V', 61, v4l2_jpegcompression)
+VIDIOC_S_JPEGCOMP = _IOW('V', 62, v4l2_jpegcompression)
+VIDIOC_QUERYSTD = _IOR('V', 63, v4l2_std_id)
+VIDIOC_TRY_FMT = _IOWR('V', 64, v4l2_format)
+VIDIOC_ENUMAUDIO = _IOWR('V', 65, v4l2_audio)
+VIDIOC_ENUMAUDOUT = _IOWR('V', 66, v4l2_audioout)
+VIDIOC_G_PRIORITY = _IOR('V', 67, v4l2_priority)
+VIDIOC_S_PRIORITY = _IOW('V', 68, v4l2_priority)
+VIDIOC_G_SLICED_VBI_CAP = _IOWR('V', 69, v4l2_sliced_vbi_cap)
+VIDIOC_LOG_STATUS = _IO('V', 70)
+VIDIOC_G_EXT_CTRLS = _IOWR('V', 71, v4l2_ext_controls)
+VIDIOC_S_EXT_CTRLS = _IOWR('V', 72, v4l2_ext_controls)
+VIDIOC_TRY_EXT_CTRLS = _IOWR('V', 73, v4l2_ext_controls)
+
+VIDIOC_ENUM_FRAMESIZES = _IOWR('V', 74, v4l2_frmsizeenum)
+VIDIOC_ENUM_FRAMEINTERVALS = _IOWR('V', 75, v4l2_frmivalenum)
+VIDIOC_G_ENC_INDEX = _IOR('V', 76, v4l2_enc_idx)
+VIDIOC_ENCODER_CMD = _IOWR('V', 77, v4l2_encoder_cmd)
+VIDIOC_TRY_ENCODER_CMD = _IOWR('V', 78, v4l2_encoder_cmd)
+
+VIDIOC_DBG_S_REGISTER = _IOW('V', 79, v4l2_dbg_register)
+VIDIOC_DBG_G_REGISTER = _IOWR('V', 80, v4l2_dbg_register)
+
+VIDIOC_DBG_G_CHIP_IDENT = _IOWR('V', 81, v4l2_dbg_chip_ident)
+
+VIDIOC_S_HW_FREQ_SEEK = _IOW('V', 82, v4l2_hw_freq_seek)
+VIDIOC_ENUM_DV_PRESETS = _IOWR('V', 83, v4l2_dv_enum_preset)
+VIDIOC_S_DV_PRESET = _IOWR('V', 84, v4l2_dv_preset)
+VIDIOC_G_DV_PRESET = _IOWR('V', 85, v4l2_dv_preset)
+VIDIOC_QUERY_DV_PRESET = _IOR('V', 86, v4l2_dv_preset)
+VIDIOC_S_DV_TIMINGS = _IOWR('V', 87, v4l2_dv_timings)
+VIDIOC_G_DV_TIMINGS = _IOWR('V', 88, v4l2_dv_timings)
+
+VIDIOC_OVERLAY_OLD = _IOWR('V', 14, ctypes.c_int)
+VIDIOC_S_PARM_OLD = _IOW('V', 22, v4l2_streamparm)
+VIDIOC_S_CTRL_OLD = _IOW('V', 28, v4l2_control)
+VIDIOC_G_AUDIO_OLD = _IOWR('V', 33, v4l2_audio)
+VIDIOC_G_AUDOUT_OLD = _IOWR('V', 49, v4l2_audioout)
+VIDIOC_CROPCAP_OLD = _IOR('V', 58, v4l2_cropcap)
+
+BASE_VIDIOC_PRIVATE = 192
diff --git a/plugins/rfid/__init__.py b/plugins/rfid/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/rfid/__init__.py
diff --git a/plugins/rfid/device.py b/plugins/rfid/device.py
new file mode 100644
index 0000000..04a82b2
--- /dev/null
+++ b/plugins/rfid/device.py
@@ -0,0 +1,61 @@
+import gobject
+
+class RFIDDevice(gobject.GObject):
+ """
+ Ancestor class for every supported device.
+ The main class for the device driver must be called "RFIDReader".
+ """
+ # signal "tag-read" has to be emitted when a tag has been read.
+ # The handler must receive the ISO-11784 hex value of the tag.
+ # signal "disconnected" has to be emitted when the device is
+ # unplugged or an error has been detected.
+ __gsignals__ = {
+ 'tag-read': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_STRING,)),
+ 'disconnected': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_STRING,))
+ }
+ def __init__(self):
+ """
+ Initializer. Subclasses must call this method.
+ """
+ self.__gobject_init__()
+
+ def get_present(self):
+ """
+ This method must detect if the device is present, returning True if so,
+ or False otherwise.
+ """
+ raise NotImplementedError
+
+ def get_version(self):
+ """
+ Returns a descriptive text of the device.
+ """
+ raise NotImplementedError
+
+ def do_connect(self):
+ """
+ Connects to the device.
+ Must return True if successfull, False otherwise.
+ """
+ raise NotImplementedError
+
+ def do_disconnect(self):
+ """
+ Disconnects from the device.
+ """
+ raise NotImplementedError
+
+ def read_tag(self):
+ """
+ Returns the 64 bit data in hex format of the last read tag.
+ """
+ raise NotImplementedError
+
+ def write_tag(self, hex_val):
+ """
+ Could be implemented if the device is capable of writing tags.
+ Receives the hex value (according to ISO 11784) to be written.
+ Returns True if successfull or False if something went wrong.
+ """
diff --git a/plugins/rfid/rfid.py b/plugins/rfid/rfid.py
new file mode 100644
index 0000000..a83bae0
--- /dev/null
+++ b/plugins/rfid/rfid.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+#Copyright (C) 2010,11 Emiliano Pastorino <epastorino@plan.ceibal.edu.uy>
+#Copyright (c) 2011 Walter Bender
+#
+# 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
+from dbus.mainloop.glib import DBusGMainLoop
+
+from plugins.rfid.rfidutils import strhex2bin, strbin2dec, find_device
+
+from plugins.plugin import Plugin
+
+from TurtleArt.tapalette import make_palette
+from TurtleArt.talogo import primitive_dictionary
+from TurtleArt.tautils import debug_output
+
+import logging
+_logger = logging.getLogger('turtleart-activity RFID plugin')
+
+HAL_SERVICE = 'org.freedesktop.Hal'
+HAL_MGR_PATH = '/org/freedesktop/Hal/Manager'
+HAL_MGR_IFACE = 'org.freedesktop.Hal.Manager'
+HAL_DEV_IFACE = 'org.freedesktop.Hal.Device'
+REGEXP_SERUSB = '\/org\/freedesktop\/Hal\/devices\/usb_device['\
+ 'a-z,A-Z,0-9,_]*serial_usb_[0-9]'
+
+
+class Rfid(Plugin):
+
+ def __init__(self, parent):
+ self._parent = parent
+ self._status = False
+
+ """
+ The following code will initialize a USB RFID reader. Please note that
+ in order to make this initialization function work, it is necessary to
+ set the permission for the ttyUSB device to 0666. You can do this by
+ adding a rule to /etc/udev/rules.d
+
+ As root (using sudo or su), copy the following text into a new file in
+ /etc/udev/rules.d/94-ttyUSB-rules
+
+ KERNEL=="ttyUSB[0-9]",MODE="0666"
+
+ You only have to do this once.
+ """
+
+ self.rfid_connected = False
+ self.rfid_device = find_device()
+ self.rfid_idn = ''
+
+ if self.rfid_device is not None:
+ _logger.info("RFID device found")
+ self.rfid_connected = self.rfid_device.do_connect()
+ if self.rfid_connected:
+ self.rfid_device.connect("tag-read", self._tag_read_cb)
+ self.rfid_device.connect("disconnected", self._disconnected_cb)
+
+ loop = DBusGMainLoop()
+ bus = dbus.SystemBus(mainloop=loop)
+ hmgr_iface = dbus.Interface(bus.get_object(HAL_SERVICE,
+ HAL_MGR_PATH), HAL_MGR_IFACE)
+
+ hmgr_iface.connect_to_signal('DeviceAdded', self._device_added_cb)
+
+ self._status = True
+
+ def setup(self):
+ # set up RFID-specific blocks
+ if self._status:
+ palette = make_palette('sensor',
+ colors=["#FF6060", "#A06060"],
+ help_string=_('Palette of sensor blocks'))
+
+ primitive_dictionary['rfid'] = self.prim_read_camera
+ palette.add_block('rfid',
+ palette='sensor',
+ style='box-style',
+ label=_('RFID'),
+ help_string=_('read value from RFID device'),
+ value_block=True,
+ prim_name='rfid')
+ self._parent.lc.def_prim('rfid', 0,
+ lambda self: primitive_dictionary['rfid'](True))
+
+ def _status_report(self):
+ debug_output('Reporting RFID status: %s' % (str(self._status)))
+ return self._status
+
+ def _device_added_cb(self, path):
+ """
+ Called from hal connection when a new device is plugged.
+ """
+ if not self.rfid_connected:
+ self.rfid_device = find_device()
+ _logger.debug("DEVICE_ADDED: %s" % self.rfid_device)
+ if self.rfid_device is not None:
+ _logger.debug("DEVICE_ADDED: RFID device is not None!")
+ self.rfid_connected = self._device.do_connect()
+ if self.rfid_connected:
+ _logger.debug("DEVICE_ADDED: Connected!")
+ self.rfid_device.connect("tag-read", self._tag_read_cb)
+ self.rfid_device.connect("disconnected", self._disconnected_cb)
+
+ def _disconnected_cb(self, device, text):
+ """
+ Called when the device is disconnected.
+ """
+ self.rfid_connected = False
+ self.rfid_device = None
+
+ def _tag_read_cb(self, device, tagid):
+ """
+ Callback for "tag-read" signal. Receives the read tag id.
+ """
+ idbin = strhex2bin(tagid)
+ self.rfid_idn = strbin2dec(idbin[26:64])
+ while self.rfid_idn.__len__() < 9:
+ self.rfid_idn = '0' + self.rfid_idn
+ print tagid, idbin, self.rfid_idn
+ self.tw.lc.update_label_value('rfid', self.rfid_idn)
+
+ # Block primitives used in talogo
+
+ def prim_read_rfid(self):
+ if self._status:
+ return self.rfid_idn
diff --git a/plugins/rfid/rfidrweusb.py b/plugins/rfid/rfidrweusb.py
new file mode 100644
index 0000000..bd12631
--- /dev/null
+++ b/plugins/rfid/rfidrweusb.py
@@ -0,0 +1,200 @@
+from device import RFIDDevice
+from serial import Serial
+import dbus
+from dbus.mainloop.glib import DBusGMainLoop
+import gobject
+from time import sleep
+import utils
+
+HAL_SERVICE = 'org.freedesktop.Hal'
+HAL_MGR_PATH = '/org/freedesktop/Hal/Manager'
+HAL_MGR_IFACE = 'org.freedesktop.Hal.Manager'
+HAL_DEV_IFACE = 'org.freedesktop.Hal.Device'
+REGEXP_SERUSB = '\/org\/freedesktop\/Hal\/devices\/usb_device['\
+ 'a-z,A-Z,0-9,_]*serial_usb_[0-9]'
+
+VERSIONS = ['301']
+
+class RFIDReader(RFIDDevice):
+ """
+ RFIDRW-E-W interface.
+ """
+
+ def __init__(self):
+
+ RFIDDevice.__init__(self)
+ self.last_tag = ""
+ self.tags = []
+ self.ser = Serial()
+ self.device = ''
+ self.device_path = ''
+ self._connected = False
+
+ loop = DBusGMainLoop()
+ self.bus = dbus.SystemBus(mainloop=loop)
+ hmgr_iface = dbus.Interface(self.bus.get_object(HAL_SERVICE,
+ HAL_MGR_PATH), HAL_MGR_IFACE)
+
+ hmgr_iface.connect_to_signal('DeviceRemoved', self._device_removed_cb)
+
+ def get_present(self):
+ """
+ Checks if RFID-RW-USB device is present.
+ Returns True if so, False otherwise.
+ """
+ hmgr_if = dbus.Interface(self.bus.get_object(HAL_SERVICE, HAL_MGR_PATH),
+ HAL_MGR_IFACE)
+ serialusb_devices = set(hmgr_if.FindDeviceStringMatch('serial.type',
+ 'usb')) & set(hmgr_if.FindDeviceStringMatch(
+ 'info.subsystem', 'tty'))
+ for i in serialusb_devices:
+ serialusb_if = dbus.Interface(self.bus.get_object(HAL_SERVICE, i),
+ HAL_DEV_IFACE)
+ if serialusb_if.PropertyExists('info.parent'):
+ parent_udi = str(serialusb_if.GetProperty('info.parent'))
+ parent = dbus.Interface(self.bus.get_object(HAL_SERVICE,
+ parent_udi), HAL_DEV_IFACE)
+ if parent.PropertyExists('info.linux.driver') and \
+ str(parent.GetProperty('info.linux.driver')) == 'ftdi_sio':
+ device = str(serialusb_if.GetProperty('linux.device_file'))
+ ser = Serial(device, 9600, timeout=0.1)
+ ser.read(100)
+ ser.write('v')
+ ser.write('e')
+ ser.write('r')
+ ser.write('\x0D')
+ resp = ser.read(4)
+ if resp[0:-1] in VERSIONS:
+ self.device = device
+ self.device_path = i
+ return True
+ return False
+
+ def do_connect(self):
+ """
+ Connects to the device.
+ Returns True if successfull, False otherwise.
+ """
+ retval = False
+ if self.get_present():
+ try:
+ self.ser = Serial(self.device, 9600, timeout=0.1)
+ self._connected = True
+ if self._select_animal_tag:
+ #gobject.idle_add(self._loop)
+ gobject.timeout_add(1000, self._loop)
+ retval = True
+ except:
+ self._connected = False
+ return retval
+
+ def do_disconnect(self):
+ """
+ Disconnect from the device.
+ """
+ self.ser.close()
+ self._connected = False
+
+ def read_tag(self):
+ """
+ Returns the last read value.
+ """
+ return self.last_tag
+
+ def _select_animal_tag(self):
+ """
+ Sends the "Select Tag 2" (animal tag) command to the device.
+ """
+ self.ser.read(100)
+ self.ser.write('s')
+ self.ser.write('t')
+ self.ser.write('2')
+ self.ser.write('\x0d')
+ resp = self.ser.read(3)[0:-1]
+ if resp == 'OK':
+ return True
+ return False
+
+ def get_version(self):
+ """
+ Sends the version command to the device and returns
+ a string with the device version.
+ """
+ #self.ser.flushInput()
+ ver = "???"
+ self.ser.read(100)
+ self.ser.write('v')
+ self.ser.write('e')
+ self.ser.write('r')
+ self.ser.write('\x0d')
+ resp = self.ser.read(4)[0:-1]
+ if resp in VERSIONS:
+ return "RFIDRW-E-USB " + resp
+ return ver
+
+ def _device_removed_cb(self, path):
+ """
+ Called when a device is removed.
+ Checks if the removed device is itself and emits the "disconnected"
+ signal if so.
+ """
+ if path == self.device_path:
+ self.device_path = ''
+ self.ser.close()
+ self._connected = False
+ self.tags = []
+ self.emit("disconnected","RFID-RW-USB")
+
+ def _loop(self):
+ """
+ Threaded loop for reading data from the device.
+ """
+ if not self._connected:
+ return False
+
+ self.ser.read(100)
+ self.ser.write('r')
+ self.ser.write('a')
+ self.ser.write('t')
+ self.ser.write('\x0d')
+ resp = self.ser.read(33)[0:-1].split('_')
+ if resp.__len__() is not 6 or resp in self.tags:
+ return True
+
+ self.tags.append(resp)
+ anbit_bin = utils.dec2bin(int(resp[2]))
+ reserved_bin = '00000000000000'
+ databit_bin = utils.dec2bin(int(resp[3]))
+ country_bin = utils.dec2bin(int(resp[0]))
+ while country_bin.__len__() < 10:
+ country_bin = '0' + country_bin
+ id_bin = utils.dec2bin(int(resp[1]))
+ while id_bin.__len__() < 10:
+ id_bin = '0' + id_bin
+
+ tag_bin = anbit_bin + reserved_bin + databit_bin + country_bin + id_bin
+ data = utils.bin2hex(tag_bin)
+ self.emit("tag-read", data)
+ self.last_tag = data
+ #sleep(1)
+ return True
+
+# Testing
+#if __name__ == '__main__':
+# def handler(device, idhex):
+# """
+# Handler for "tag-read" signal.
+# Prints the tag id.
+# """
+# print "ID: ", idhex
+#
+# dev = RFIDReader()
+# if dev.get_present():
+# print "SIPI!"
+# dev.do_connect()
+# dev.connect('tag-read', handler)
+# else:
+# print "Not connected"
+#
+# mloop = gobject.MainLoop()
+# mloop.run()
diff --git a/plugins/rfid/rfidutils.py b/plugins/rfid/rfidutils.py
new file mode 100644
index 0000000..4e02619
--- /dev/null
+++ b/plugins/rfid/rfidutils.py
@@ -0,0 +1,123 @@
+# utils.py - Helper functions for tis2000.py
+# Copyright (C) 2010 Emiliano Pastorino <epastorino@plan.ceibal.edu.uy>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+import os
+import logging
+
+def find_device():
+ """
+ Search for devices.
+ Return a device instance or None.
+ """
+ device = None
+ for i in os.listdir(os.path.join('.', 'plugins/rfid')):
+ if not os.path.isdir(os.path.join('.', 'plugins/rfid', i)):
+ try:
+ _tempmod = __import__('rfid.%s'%i.split('.')[0], globals(),
+ locals(), ['RFIDReader'], -1)
+ devtemp = _tempmod.RFIDReader()
+ if devtemp.get_present() == True:
+ device = devtemp
+ except Exception, e:
+ # logging.error("FIND_DEVICE: %s: %s"%(i, e))
+ pass
+ if device is None:
+ logging.debug("No RFID device found")
+ return device
+
+
+def strhex2bin(strhex):
+ """
+ Convert a string representing an hex value into a
+ string representing the same value in binary format.
+ """
+ dic = { '0':"0000",
+ '1':"0001",
+ '2':"0010",
+ '3':"0011",
+ '4':"0100",
+ '5':"0101",
+ '6':"0110",
+ '7':"0111",
+ '8':"1000",
+ '9':"1001",
+ 'A':"1010",
+ 'B':"1011",
+ 'C':"1100",
+ 'D':"1101",
+ 'E':"1110",
+ 'F':"1111"
+ }
+ binstr = ""
+ for i in strhex:
+ binstr = binstr + dic[i.upper()]
+ return binstr
+
+def strbin2dec(strbin):
+ """
+ Convert a string representing a binary value into a
+ string representing the same value in decimal format.
+ """
+ strdec = "0"
+ for i in range(1, strbin.__len__()+1):
+ strdec = str(int(strdec)+int(strbin[-i])*int(pow(2, i-1)))
+ return strdec
+
+def dec2bin(ndec):
+ """
+ Convert a decimal number into a string representing
+ the same value in binary format.
+ """
+ if ndec < 1:
+ return "0"
+ binary = []
+ while ndec != 0:
+ binary.append(ndec%2)
+ ndec = ndec/2
+ strbin = ""
+ binary.reverse()
+ for i in binary:
+ strbin = strbin+str(i)
+ return strbin
+
+def bin2hex(strbin):
+ """
+ Convert a string representing a binary number into a string
+ representing the same value in hexadecimal format.
+ """
+ dic = { "0000":"0",
+ "0001":"1",
+ "0010":"2",
+ "0011":"3",
+ "0100":"4",
+ "0101":"5",
+ "0110":"6",
+ "0111":"7",
+ "1000":"8",
+ "1001":"9",
+ "1010":"A",
+ "1011":"B",
+ "1100":"C",
+ "1101":"D",
+ "1110":"E",
+ "1111":"F"
+ }
+ while strbin.__len__()%4 != 0:
+ strbin = '0' + strbin
+ strh = ""
+ for i in range(0, strbin.__len__()/4):
+ strh = strh + dic[str(strbin[i*4:i*4+4])]
+ return strh
diff --git a/plugins/rfid/serial/__init__.py b/plugins/rfid/serial/__init__.py
new file mode 100644
index 0000000..681ad5c
--- /dev/null
+++ b/plugins/rfid/serial/__init__.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+#portable serial port access with python
+#this is a wrapper module for different platform implementations
+#
+# (C)2001-2002 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+VERSION = '2.4'
+
+import sys
+
+if sys.platform == 'cli':
+ from serialcli import *
+else:
+ import os
+ #chose an implementation, depending on os
+ if os.name == 'nt': #sys.platform == 'win32':
+ from serialwin32 import *
+ elif os.name == 'posix':
+ from serialposix import *
+ elif os.name == 'java':
+ from serialjava import *
+ else:
+ raise Exception("Sorry: no implementation for your platform ('%s') available" % os.name)
+
diff --git a/plugins/rfid/serial/serialposix.py b/plugins/rfid/serial/serialposix.py
new file mode 100644
index 0000000..174e2f7
--- /dev/null
+++ b/plugins/rfid/serial/serialposix.py
@@ -0,0 +1,492 @@
+#!/usr/bin/env python
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# module for serial IO for POSIX compatible systems, like Linux
+# see __init__.py
+#
+# (C) 2001-2008 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+#
+# parts based on code from Grant B. Edwards <grante@visi.com>:
+# ftp://ftp.visi.com/users/grante/python/PosixSerial.py
+# references: http://www.easysw.com/~mike/serial/serial.html
+
+import sys, os, fcntl, termios, struct, select, errno
+from serialutil import *
+
+#Do check the Python version as some constants have moved.
+if (sys.hexversion < 0x020100f0):
+ import TERMIOS
+else:
+ TERMIOS = termios
+
+if (sys.hexversion < 0x020200f0):
+ import FCNTL
+else:
+ FCNTL = fcntl
+
+#try to detect the os so that a device can be selected...
+plat = sys.platform.lower()
+
+if plat[:5] == 'linux': #Linux (confirmed)
+ def device(port):
+ return '/dev/ttyS%d' % port
+
+elif plat == 'cygwin': #cywin/win32 (confirmed)
+ def device(port):
+ return '/dev/com%d' % (port + 1)
+
+elif plat == 'openbsd3': #BSD (confirmed)
+ def device(port):
+ return '/dev/ttyp%d' % port
+
+elif plat[:3] == 'bsd' or \
+ plat[:7] == 'freebsd' or \
+ plat[:7] == 'openbsd' or \
+ plat[:6] == 'darwin': #BSD (confirmed for freebsd4: cuaa%d)
+ def device(port):
+ return '/dev/cuad%d' % port
+
+elif plat[:6] == 'netbsd': #NetBSD 1.6 testing by Erk
+ def device(port):
+ return '/dev/dty%02d' % port
+
+elif plat[:4] == 'irix': #IRIX (partialy tested)
+ def device(port):
+ return '/dev/ttyf%d' % (port+1) #XXX different device names depending on flow control
+
+elif plat[:2] == 'hp': #HP-UX (not tested)
+ def device(port):
+ return '/dev/tty%dp0' % (port+1)
+
+elif plat[:5] == 'sunos': #Solaris/SunOS (confirmed)
+ def device(port):
+ return '/dev/tty%c' % (ord('a')+port)
+
+elif plat[:3] == 'aix': #aix
+ def device(port):
+ return '/dev/tty%d' % (port)
+
+else:
+ #platform detection has failed...
+ print """don't know how to number ttys on this system.
+! Use an explicit path (eg /dev/ttyS1) or send this information to
+! the author of this module:
+
+sys.platform = %r
+os.name = %r
+serialposix.py version = %s
+
+also add the device name of the serial port and where the
+counting starts for the first serial port.
+e.g. 'first serial port: /dev/ttyS0'
+and with a bit luck you can get this module running...
+""" % (sys.platform, os.name, VERSION)
+ #no exception, just continue with a brave attempt to build a device name
+ #even if the device name is not correct for the platform it has chances
+ #to work using a string with the real device name as port paramter.
+ def device(portum):
+ return '/dev/ttyS%d' % portnum
+ #~ raise Exception, "this module does not run on this platform, sorry."
+
+#whats up with "aix", "beos", ....
+#they should work, just need to know the device names.
+
+
+#load some constants for later use.
+#try to use values from TERMIOS, use defaults from linux otherwise
+TIOCMGET = hasattr(TERMIOS, 'TIOCMGET') and TERMIOS.TIOCMGET or 0x5415
+TIOCMBIS = hasattr(TERMIOS, 'TIOCMBIS') and TERMIOS.TIOCMBIS or 0x5416
+TIOCMBIC = hasattr(TERMIOS, 'TIOCMBIC') and TERMIOS.TIOCMBIC or 0x5417
+TIOCMSET = hasattr(TERMIOS, 'TIOCMSET') and TERMIOS.TIOCMSET or 0x5418
+
+#TIOCM_LE = hasattr(TERMIOS, 'TIOCM_LE') and TERMIOS.TIOCM_LE or 0x001
+TIOCM_DTR = hasattr(TERMIOS, 'TIOCM_DTR') and TERMIOS.TIOCM_DTR or 0x002
+TIOCM_RTS = hasattr(TERMIOS, 'TIOCM_RTS') and TERMIOS.TIOCM_RTS or 0x004
+#TIOCM_ST = hasattr(TERMIOS, 'TIOCM_ST') and TERMIOS.TIOCM_ST or 0x008
+#TIOCM_SR = hasattr(TERMIOS, 'TIOCM_SR') and TERMIOS.TIOCM_SR or 0x010
+
+TIOCM_CTS = hasattr(TERMIOS, 'TIOCM_CTS') and TERMIOS.TIOCM_CTS or 0x020
+TIOCM_CAR = hasattr(TERMIOS, 'TIOCM_CAR') and TERMIOS.TIOCM_CAR or 0x040
+TIOCM_RNG = hasattr(TERMIOS, 'TIOCM_RNG') and TERMIOS.TIOCM_RNG or 0x080
+TIOCM_DSR = hasattr(TERMIOS, 'TIOCM_DSR') and TERMIOS.TIOCM_DSR or 0x100
+TIOCM_CD = hasattr(TERMIOS, 'TIOCM_CD') and TERMIOS.TIOCM_CD or TIOCM_CAR
+TIOCM_RI = hasattr(TERMIOS, 'TIOCM_RI') and TERMIOS.TIOCM_RI or TIOCM_RNG
+#TIOCM_OUT1 = hasattr(TERMIOS, 'TIOCM_OUT1') and TERMIOS.TIOCM_OUT1 or 0x2000
+#TIOCM_OUT2 = hasattr(TERMIOS, 'TIOCM_OUT2') and TERMIOS.TIOCM_OUT2 or 0x4000
+TIOCINQ = hasattr(TERMIOS, 'FIONREAD') and TERMIOS.FIONREAD or 0x541B
+
+TIOCM_zero_str = struct.pack('I', 0)
+TIOCM_RTS_str = struct.pack('I', TIOCM_RTS)
+TIOCM_DTR_str = struct.pack('I', TIOCM_DTR)
+
+TIOCSBRK = hasattr(TERMIOS, 'TIOCSBRK') and TERMIOS.TIOCSBRK or 0x5427
+TIOCCBRK = hasattr(TERMIOS, 'TIOCCBRK') and TERMIOS.TIOCCBRK or 0x5428
+
+ASYNC_SPD_MASK = 0x1030
+ASYNC_SPD_CUST = 0x0030
+
+baudrate_constants = {
+ 0: 0000000, # hang up
+ 50: 0000001,
+ 75: 0000002,
+ 110: 0000003,
+ 134: 0000004,
+ 150: 0000005,
+ 200: 0000006,
+ 300: 0000007,
+ 600: 0000010,
+ 1200: 0000011,
+ 1800: 0000012,
+ 2400: 0000013,
+ 4800: 0000014,
+ 9600: 0000015,
+ 19200: 0000016,
+ 38400: 0000017,
+ 57600: 0010001,
+ 115200: 0010002,
+ 230400: 0010003,
+ 460800: 0010004,
+ 500000: 0010005,
+ 576000: 0010006,
+ 921600: 0010007,
+ 1000000: 0010010,
+ 1152000: 0010011,
+ 1500000: 0010012,
+ 2000000: 0010013,
+ 2500000: 0010014,
+ 3000000: 0010015,
+ 3500000: 0010016,
+ 4000000: 0010017
+}
+
+
+class Serial(SerialBase):
+ """Serial port class POSIX implementation. Serial port configuration is
+ done with termios and fcntl. Runs on Linux and many other Un*x like
+ systems."""
+
+ def open(self):
+ """Open port with current settings. This may throw a SerialException
+ if the port cannot be opened."""
+ if self._port is None:
+ raise SerialException("Port must be configured before it can be used.")
+ self.fd = None
+ #open
+ try:
+ self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK)
+ except Exception, msg:
+ self.fd = None
+ raise SerialException("could not open port %s: %s" % (self._port, msg))
+ #~ fcntl.fcntl(self.fd, FCNTL.F_SETFL, 0) #set blocking
+
+ try:
+ self._reconfigurePort()
+ except:
+ os.close(self.fd)
+ self.fd = None
+ else:
+ self._isOpen = True
+ #~ self.flushInput()
+
+
+ def _reconfigurePort(self):
+ """Set communication parameters on opened port."""
+ if self.fd is None:
+ raise SerialException("Can only operate on a valid port handle")
+ custom_baud = None
+
+ vmin = vtime = 0 #timeout is done via select
+ if self._interCharTimeout is not None:
+ vmin = 1
+ vtime = int(self._interCharTimeout * 10)
+ try:
+ iflag, oflag, cflag, lflag, ispeed, ospeed, cc = termios.tcgetattr(self.fd)
+ except termios.error, msg: #if a port is nonexistent but has a /dev file, it'll fail here
+ raise SerialException("Could not configure port: %s" % msg)
+ #set up raw mode / no echo / binary
+ cflag |= (TERMIOS.CLOCAL|TERMIOS.CREAD)
+ lflag &= ~(TERMIOS.ICANON|TERMIOS.ECHO|TERMIOS.ECHOE|TERMIOS.ECHOK|TERMIOS.ECHONL|
+ TERMIOS.ISIG|TERMIOS.IEXTEN) #|TERMIOS.ECHOPRT
+ for flag in ('ECHOCTL', 'ECHOKE'): #netbsd workaround for Erk
+ if hasattr(TERMIOS, flag):
+ lflag &= ~getattr(TERMIOS, flag)
+
+ oflag &= ~(TERMIOS.OPOST)
+ iflag &= ~(TERMIOS.INLCR|TERMIOS.IGNCR|TERMIOS.ICRNL|TERMIOS.IGNBRK)
+ if hasattr(TERMIOS, 'IUCLC'):
+ iflag &= ~TERMIOS.IUCLC
+ if hasattr(TERMIOS, 'PARMRK'):
+ iflag &= ~TERMIOS.PARMRK
+
+ #setup baudrate
+ try:
+ ispeed = ospeed = getattr(TERMIOS,'B%s' % (self._baudrate))
+ except AttributeError:
+ try:
+ ispeed = ospeed = baudrate_constants[self._baudrate]
+ except KeyError:
+ #~ raise ValueError('Invalid baud rate: %r' % self._baudrate)
+ # may need custom baud rate, it isnt in our list.
+ ispeed = ospeed = getattr(TERMIOS, 'B38400')
+ custom_baud = int(self._baudrate) # store for later
+
+ #setup char len
+ cflag &= ~TERMIOS.CSIZE
+ if self._bytesize == 8:
+ cflag |= TERMIOS.CS8
+ elif self._bytesize == 7:
+ cflag |= TERMIOS.CS7
+ elif self._bytesize == 6:
+ cflag |= TERMIOS.CS6
+ elif self._bytesize == 5:
+ cflag |= TERMIOS.CS5
+ else:
+ raise ValueError('Invalid char len: %r' % self._bytesize)
+ #setup stopbits
+ if self._stopbits == STOPBITS_ONE:
+ cflag &= ~(TERMIOS.CSTOPB)
+ elif self._stopbits == STOPBITS_TWO:
+ cflag |= (TERMIOS.CSTOPB)
+ else:
+ raise ValueError('Invalid stopit specification: %r' % self._stopbits)
+ #setup parity
+ iflag &= ~(TERMIOS.INPCK|TERMIOS.ISTRIP)
+ if self._parity == PARITY_NONE:
+ cflag &= ~(TERMIOS.PARENB|TERMIOS.PARODD)
+ elif self._parity == PARITY_EVEN:
+ cflag &= ~(TERMIOS.PARODD)
+ cflag |= (TERMIOS.PARENB)
+ elif self._parity == PARITY_ODD:
+ cflag |= (TERMIOS.PARENB|TERMIOS.PARODD)
+ else:
+ raise ValueError('Invalid parity: %r' % self._parity)
+ #setup flow control
+ #xonxoff
+ if hasattr(TERMIOS, 'IXANY'):
+ if self._xonxoff:
+ iflag |= (TERMIOS.IXON|TERMIOS.IXOFF) #|TERMIOS.IXANY)
+ else:
+ iflag &= ~(TERMIOS.IXON|TERMIOS.IXOFF|TERMIOS.IXANY)
+ else:
+ if self._xonxoff:
+ iflag |= (TERMIOS.IXON|TERMIOS.IXOFF)
+ else:
+ iflag &= ~(TERMIOS.IXON|TERMIOS.IXOFF)
+ #rtscts
+ if hasattr(TERMIOS, 'CRTSCTS'):
+ if self._rtscts:
+ cflag |= (TERMIOS.CRTSCTS)
+ else:
+ cflag &= ~(TERMIOS.CRTSCTS)
+ elif hasattr(TERMIOS, 'CNEW_RTSCTS'): #try it with alternate constant name
+ if self._rtscts:
+ cflag |= (TERMIOS.CNEW_RTSCTS)
+ else:
+ cflag &= ~(TERMIOS.CNEW_RTSCTS)
+ #XXX should there be a warning if setting up rtscts (and xonxoff etc) fails??
+
+ #buffer
+ #vmin "minimal number of characters to be read. = for non blocking"
+ if vmin < 0 or vmin > 255:
+ raise ValueError('Invalid vmin: %r ' % vmin)
+ cc[TERMIOS.VMIN] = vmin
+ #vtime
+ if vtime < 0 or vtime > 255:
+ raise ValueError('Invalid vtime: %r' % vtime)
+ cc[TERMIOS.VTIME] = vtime
+ #activate settings
+ termios.tcsetattr(self.fd, TERMIOS.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
+
+ # apply custom baud rate, if any
+ if custom_baud is not None:
+ import array
+ buf = array.array('i', [0] * 32)
+
+ # get serial_struct
+ FCNTL.ioctl(self.fd, TERMIOS.TIOCGSERIAL, buf)
+
+ # set custom divisor
+ buf[6] = buf[7] / custom_baud
+
+ # update flags
+ buf[4] &= ~ASYNC_SPD_MASK
+ buf[4] |= ASYNC_SPD_CUST
+
+ # set serial_struct
+ try:
+ res = FCNTL.ioctl(self.fd, TERMIOS.TIOCSSERIAL, buf)
+ except IOError:
+ raise ValueError('Failed to set custom baud rate: %r' % self._baudrate)
+
+ def close(self):
+ """Close port"""
+ if self._isOpen:
+ if self.fd is not None:
+ os.close(self.fd)
+ self.fd = None
+ self._isOpen = False
+
+ def makeDeviceName(self, port):
+ return device(port)
+
+ # - - - - - - - - - - - - - - - - - - - - - - - -
+
+ def inWaiting(self):
+ """Return the number of characters currently in the input buffer."""
+ #~ s = fcntl.ioctl(self.fd, TERMIOS.FIONREAD, TIOCM_zero_str)
+ s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str)
+ return struct.unpack('I',s)[0]
+
+ def read(self, size=1):
+ """Read size bytes from the serial port. If a timeout is set it may
+ return less characters as requested. With no timeout it will block
+ until the requested number of bytes is read."""
+ if self.fd is None: raise portNotOpenError
+ read = ''
+ inp = None
+ if size > 0:
+ while len(read) < size:
+ #print "\tread(): size",size, "have", len(read) #debug
+ ready,_,_ = select.select([self.fd],[],[], self._timeout)
+ if not ready:
+ break #timeout
+ buf = os.read(self.fd, size-len(read))
+ read = read + buf
+ if (self._timeout >= 0 or self._interCharTimeout > 0) and not buf:
+ break #early abort on timeout
+ return read
+
+ def write(self, data):
+ """Output the given string over the serial port."""
+ if self.fd is None: raise portNotOpenError
+ if not isinstance(data, str):
+ raise TypeError('expected str, got %s' % type(data))
+ t = len(data)
+ d = data
+ while t > 0:
+ try:
+ if self._writeTimeout is not None and self._writeTimeout > 0:
+ _,ready,_ = select.select([],[self.fd],[], self._writeTimeout)
+ if not ready:
+ raise writeTimeoutError
+ n = os.write(self.fd, d)
+ if self._writeTimeout is not None and self._writeTimeout > 0:
+ _,ready,_ = select.select([],[self.fd],[], self._writeTimeout)
+ if not ready:
+ raise writeTimeoutError
+ d = d[n:]
+ t = t - n
+ except OSError,v:
+ if v.errno != errno.EAGAIN:
+ raise
+
+ def flush(self):
+ """Flush of file like objects. In this case, wait until all data
+ is written."""
+ self.drainOutput()
+
+ def flushInput(self):
+ """Clear input buffer, discarding all that is in the buffer."""
+ if self.fd is None:
+ raise portNotOpenError
+ termios.tcflush(self.fd, TERMIOS.TCIFLUSH)
+
+ def flushOutput(self):
+ """Clear output buffer, aborting the current output and
+ discarding all that is in the buffer."""
+ if self.fd is None:
+ raise portNotOpenError
+ termios.tcflush(self.fd, TERMIOS.TCOFLUSH)
+
+ def sendBreak(self, duration=0.25):
+ """Send break condition. Timed, returns to idle state after given duration."""
+ if self.fd is None:
+ raise portNotOpenError
+ termios.tcsendbreak(self.fd, int(duration/0.25))
+
+ def setBreak(self, level=1):
+ """Set break: Controls TXD. When active, to transmitting is possible."""
+ if self.fd is None: raise portNotOpenError
+ if level:
+ fcntl.ioctl(self.fd, TIOCSBRK)
+ else:
+ fcntl.ioctl(self.fd, TIOCCBRK)
+
+ def setRTS(self, level=1):
+ """Set terminal status line: Request To Send"""
+ if self.fd is None: raise portNotOpenError
+ if level:
+ fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str)
+ else:
+ fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str)
+
+ def setDTR(self, level=1):
+ """Set terminal status line: Data Terminal Ready"""
+ if self.fd is None: raise portNotOpenError
+ if level:
+ fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str)
+ else:
+ fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str)
+
+ def getCTS(self):
+ """Read terminal status line: Clear To Send"""
+ if self.fd is None: raise portNotOpenError
+ s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+ return struct.unpack('I',s)[0] & TIOCM_CTS != 0
+
+ def getDSR(self):
+ """Read terminal status line: Data Set Ready"""
+ if self.fd is None: raise portNotOpenError
+ s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+ return struct.unpack('I',s)[0] & TIOCM_DSR != 0
+
+ def getRI(self):
+ """Read terminal status line: Ring Indicator"""
+ if self.fd is None: raise portNotOpenError
+ s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+ return struct.unpack('I',s)[0] & TIOCM_RI != 0
+
+ def getCD(self):
+ """Read terminal status line: Carrier Detect"""
+ if self.fd is None: raise portNotOpenError
+ s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
+ return struct.unpack('I',s)[0] & TIOCM_CD != 0
+
+ # - - platform specific - - - -
+
+ def drainOutput(self):
+ """internal - not portable!"""
+ if self.fd is None: raise portNotOpenError
+ termios.tcdrain(self.fd)
+
+ def nonblocking(self):
+ """internal - not portable!"""
+ if self.fd is None:
+ raise portNotOpenError
+ fcntl.fcntl(self.fd, FCNTL.F_SETFL, FCNTL.O_NONBLOCK)
+
+ def fileno(self):
+ """For easier of the serial port instance with select.
+ WARNING: this function is not portable to different platforms!"""
+ if self.fd is None: raise portNotOpenError
+ return self.fd
+
+if __name__ == '__main__':
+ s = Serial(0,
+ baudrate=19200, #baudrate
+ bytesize=EIGHTBITS, #number of databits
+ parity=PARITY_EVEN, #enable parity checking
+ stopbits=STOPBITS_ONE, #number of stopbits
+ timeout=3, #set a timeout value, None for waiting forever
+ xonxoff=0, #enable software flow control
+ rtscts=0, #enable RTS/CTS flow control
+ )
+ s.setRTS(1)
+ s.setDTR(1)
+ s.flushInput()
+ s.flushOutput()
+ s.write('hello')
+ print repr(s.read(5))
+ print s.inWaiting()
+ del s
+
diff --git a/plugins/rfid/serial/serialutil.py b/plugins/rfid/serial/serialutil.py
new file mode 100644
index 0000000..fd466f2
--- /dev/null
+++ b/plugins/rfid/serial/serialutil.py
@@ -0,0 +1,400 @@
+#! python
+# Python Serial Port Extension for Win32, Linux, BSD, Jython
+# see __init__.py
+#
+# (C) 2001-2008 Chris Liechti <cliechti@gmx.net>
+# this is distributed under a free software license, see license.txt
+
+PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S'
+STOPBITS_ONE, STOPBITS_TWO = (1, 2)
+FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5,6,7,8)
+
+PARITY_NAMES = {
+ PARITY_NONE: 'None',
+ PARITY_EVEN: 'Even',
+ PARITY_ODD: 'Odd',
+ PARITY_MARK: 'Mark',
+ PARITY_SPACE:'Space',
+}
+
+XON = chr(17)
+XOFF = chr(19)
+
+#Python < 2.2.3 compatibility
+try:
+ True
+except:
+ True = 1
+ False = not True
+
+class SerialException(Exception):
+ """Base class for serial port related exceptions."""
+
+portNotOpenError = SerialException('Port not open')
+
+class SerialTimeoutException(SerialException):
+ """Write timeouts give an exception"""
+
+writeTimeoutError = SerialTimeoutException("Write timeout")
+
+class FileLike(object):
+ """An abstract file like class.
+
+ This class implements readline and readlines based on read and
+ writelines based on write.
+ This class is used to provide the above functions for to Serial
+ port objects.
+
+ Note that when the serial port was opened with _NO_ timeout that
+ readline blocks until it sees a newline (or the specified size is
+ reached) and that readlines would never return and therefore
+ refuses to work (it raises an exception in this case)!
+ """
+
+ def read(self, size): raise NotImplementedError
+ def write(self, s): raise NotImplementedError
+
+ def readline(self, size=None, eol='\n'):
+ """read a line which is terminated with end-of-line (eol) character
+ ('\n' by default) or until timeout"""
+ line = ''
+ while 1:
+ c = self.read(1)
+ if c:
+ line += c #not very efficient but lines are usually not that long
+ if c == eol:
+ break
+ if size is not None and len(line) >= size:
+ break
+ else:
+ break
+ return line
+
+ def readlines(self, sizehint=None, eol='\n'):
+ """read a list of lines, until timeout
+ sizehint is ignored"""
+ if self.timeout is None:
+ raise ValueError, "Serial port MUST have enabled timeout for this function!"
+ lines = []
+ while 1:
+ line = self.readline(eol=eol)
+ if line:
+ lines.append(line)
+ if line[-1] != eol: #was the line received with a timeout?
+ break
+ else:
+ break
+ return lines
+
+ def xreadlines(self, sizehint=None):
+ """just call readlines - here for compatibility"""
+ return self.readlines()
+
+ def writelines(self, sequence):
+ for line in sequence:
+ self.write(line)
+
+ def flush(self):
+ """flush of file like objects"""
+ pass
+
+ # iterator for e.g. "for line in Serial(0): ..." usage
+ def next(self):
+ line = self.readline()
+ if not line: raise StopIteration
+ return line
+
+ def __iter__(self):
+ return self
+
+
+class SerialBase(FileLike):
+ """Serial port base class. Provides __init__ function and properties to
+ get/set port settings."""
+
+ #default values, may be overriden in subclasses that do not support all values
+ BAUDRATES = (50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600,
+ 19200,38400,57600,115200,230400,460800,500000,576000,921600,
+ 1000000,1152000,1500000,2000000,2500000,3000000,3500000,4000000)
+ BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS)
+ PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD)
+ STOPBITS = (STOPBITS_ONE, STOPBITS_TWO)
+
+ def __init__(self,
+ port = None, #number of device, numbering starts at
+ #zero. if everything fails, the user
+ #can specify a device string, note
+ #that this isn't portable anymore
+ #port will be opened if one is specified
+ baudrate=9600, #baudrate
+ bytesize=EIGHTBITS, #number of databits
+ parity=PARITY_NONE, #enable parity checking
+ stopbits=STOPBITS_ONE, #number of stopbits
+ timeout=None, #set a timeout value, None to wait forever
+ xonxoff=0, #enable software flow control
+ rtscts=0, #enable RTS/CTS flow control
+ writeTimeout=None, #set a timeout for writes
+ dsrdtr=None, #None: use rtscts setting, dsrdtr override if true or false
+ interCharTimeout=None #Inter-character timeout, None to disable
+ ):
+ """Initialize comm port object. If a port is given, then the port will be
+ opened immediately. Otherwise a Serial port object in closed state
+ is returned."""
+
+ self._isOpen = False
+ self._port = None #correct value is assigned below trough properties
+ self._baudrate = None #correct value is assigned below trough properties
+ self._bytesize = None #correct value is assigned below trough properties
+ self._parity = None #correct value is assigned below trough properties
+ self._stopbits = None #correct value is assigned below trough properties
+ self._timeout = None #correct value is assigned below trough properties
+ self._writeTimeout = None #correct value is assigned below trough properties
+ self._xonxoff = None #correct value is assigned below trough properties
+ self._rtscts = None #correct value is assigned below trough properties
+ self._dsrdtr = None #correct value is assigned below trough properties
+ self._interCharTimeout = None #correct value is assigned below trough properties
+
+ #assign values using get/set methods using the properties feature
+ self.port = port
+ self.baudrate = baudrate
+ self.bytesize = bytesize
+ self.parity = parity
+ self.stopbits = stopbits
+ self.timeout = timeout
+ self.writeTimeout = writeTimeout
+ self.xonxoff = xonxoff
+ self.rtscts = rtscts
+ self.dsrdtr = dsrdtr
+ self.interCharTimeout = interCharTimeout
+
+ if port is not None:
+ self.open()
+
+ def isOpen(self):
+ """Check if the port is opened."""
+ return self._isOpen
+
+ # - - - - - - - - - - - - - - - - - - - - - - - -
+
+ #TODO: these are not realy needed as the is the BAUDRATES etc attribute...
+ #maybe i remove them before the final release...
+
+ def getSupportedBaudrates(self):
+ return [(str(b), b) for b in self.BAUDRATES]
+
+ def getSupportedByteSizes(self):
+ return [(str(b), b) for b in self.BYTESIZES]
+
+ def getSupportedStopbits(self):
+ return [(str(b), b) for b in self.STOPBITS]
+
+ def getSupportedParities(self):
+ return [(PARITY_NAMES[b], b) for b in self.PARITIES]
+
+ # - - - - - - - - - - - - - - - - - - - - - - - -
+
+ def setPort(self, port):
+ """Change the port. The attribute portstr is set to a string that
+ contains the name of the port."""
+
+ was_open = self._isOpen
+ if was_open: self.close()
+ if port is not None:
+ if type(port) in [type(''), type(u'')]: #strings are taken directly
+ self.portstr = port
+ else:
+ self.portstr = self.makeDeviceName(port)
+ else:
+ self.portstr = None
+ self._port = port
+ if was_open: self.open()
+
+ def getPort(self):
+ """Get the current port setting. The value that was passed on init or using
+ setPort() is passed back. See also the attribute portstr which contains
+ the name of the port as a string."""
+ return self._port
+
+ port = property(getPort, setPort, doc="Port setting")
+
+
+ def setBaudrate(self, baudrate):
+ """Change baudrate. It raises a ValueError if the port is open and the
+ baudrate is not possible. If the port is closed, then tha value is
+ accepted and the exception is raised when the port is opened."""
+ #~ if baudrate not in self.BAUDRATES: raise ValueError("Not a valid baudrate: %r" % baudrate)
+ try:
+ self._baudrate = int(baudrate)
+ except TypeError:
+ raise ValueError("Not a valid baudrate: %r" % (baudrate,))
+ else:
+ if self._isOpen: self._reconfigurePort()
+
+ def getBaudrate(self):
+ """Get the current baudrate setting."""
+ return self._baudrate
+
+ baudrate = property(getBaudrate, setBaudrate, doc="Baudrate setting")
+
+
+ def setByteSize(self, bytesize):
+ """Change byte size."""
+ if bytesize not in self.BYTESIZES: raise ValueError("Not a valid byte size: %r" % (bytesize,))
+ self._bytesize = bytesize
+ if self._isOpen: self._reconfigurePort()
+
+ def getByteSize(self):
+ """Get the current byte size setting."""
+ return self._bytesize
+
+ bytesize = property(getByteSize, setByteSize, doc="Byte size setting")
+
+
+ def setParity(self, parity):
+ """Change parity setting."""
+ if parity not in self.PARITIES: raise ValueError("Not a valid parity: %r" % (parity,))
+ self._parity = parity
+ if self._isOpen: self._reconfigurePort()
+
+ def getParity(self):
+ """Get the current parity setting."""
+ return self._parity
+
+ parity = property(getParity, setParity, doc="Parity setting")
+
+
+ def setStopbits(self, stopbits):
+ """Change stopbits size."""
+ if stopbits not in self.STOPBITS: raise ValueError("Not a valid stopbit size: %r" % (stopbits,))
+ self._stopbits = stopbits
+ if self._isOpen: self._reconfigurePort()
+
+ def getStopbits(self):
+ """Get the current stopbits setting."""
+ return self._stopbits
+
+ stopbits = property(getStopbits, setStopbits, doc="Stopbits setting")
+
+
+ def setTimeout(self, timeout):
+ """Change timeout setting."""
+ if timeout is not None:
+ if timeout < 0: raise ValueError("Not a valid timeout: %r" % (timeout,))
+ try:
+ timeout + 1 #test if it's a number, will throw a TypeError if not...
+ except TypeError:
+ raise ValueError("Not a valid timeout: %r" % (timeout,))
+
+ self._timeout = timeout
+ if self._isOpen: self._reconfigurePort()
+
+ def getTimeout(self):
+ """Get the current timeout setting."""
+ return self._timeout
+
+ timeout = property(getTimeout, setTimeout, doc="Timeout setting for read()")
+
+
+ def setWriteTimeout(self, timeout):
+ """Change timeout setting."""
+ if timeout is not None:
+ if timeout < 0: raise ValueError("Not a valid timeout: %r" % (timeout,))
+ try:
+ timeout + 1 #test if it's a number, will throw a TypeError if not...
+ except TypeError:
+ raise ValueError("Not a valid timeout: %r" % timeout)
+
+ self._writeTimeout = timeout
+ if self._isOpen: self._reconfigurePort()
+
+ def getWriteTimeout(self):
+ """Get the current timeout setting."""
+ return self._writeTimeout
+
+ writeTimeout = property(getWriteTimeout, setWriteTimeout, doc="Timeout setting for write()")
+
+
+ def setXonXoff(self, xonxoff):
+ """Change XonXoff setting."""
+ self._xonxoff = xonxoff
+ if self._isOpen: self._reconfigurePort()
+
+ def getXonXoff(self):
+ """Get the current XonXoff setting."""
+ return self._xonxoff
+
+ xonxoff = property(getXonXoff, setXonXoff, doc="Xon/Xoff setting")
+
+ def setRtsCts(self, rtscts):
+ """Change RtsCts flow control setting."""
+ self._rtscts = rtscts
+ if self._isOpen: self._reconfigurePort()
+
+ def getRtsCts(self):
+ """Get the current RtsCts flow control setting."""
+ return self._rtscts
+
+ rtscts = property(getRtsCts, setRtsCts, doc="RTS/CTS flow control setting")
+
+ def setDsrDtr(self, dsrdtr=None):
+ """Change DsrDtr flow control setting."""
+ if dsrdtr is None:
+ #if not set, keep backwards compatibility and follow rtscts setting
+ self._dsrdtr = self._rtscts
+ else:
+ #if defined independently, follow its value
+ self._dsrdtr = dsrdtr
+ if self._isOpen: self._reconfigurePort()
+
+ def getDsrDtr(self):
+ """Get the current DsrDtr flow control setting."""
+ return self._dsrdtr
+
+ dsrdtr = property(getDsrDtr, setDsrDtr, "DSR/DTR flow control setting")
+
+ def setInterCharTimeout(self, interCharTimeout):
+ """Change inter-character timeout setting."""
+ if interCharTimeout is not None:
+ if interCharTimeout < 0: raise ValueError("Not a valid timeout: %r" % interCharTimeout)
+ try:
+ interCharTimeout + 1 #test if it's a number, will throw a TypeError if not...
+ except TypeError:
+ raise ValueError("Not a valid timeout: %r" % interCharTimeout)
+
+ self._interCharTimeout = interCharTimeout
+ if self._isOpen: self._reconfigurePort()
+
+ def getInterCharTimeout(self):
+ """Get the current inter-character timeout setting."""
+ return self._interCharTimeout
+
+ interCharTimeout = property(getInterCharTimeout, setInterCharTimeout, doc="Inter-character timeout setting for read()")
+
+
+ # - - - - - - - - - - - - - - - - - - - - - - - -
+
+ def __repr__(self):
+ """String representation of the current port settings and its state."""
+ return "%s<id=0x%x, open=%s>(port=%r, baudrate=%r, bytesize=%r, parity=%r, stopbits=%r, timeout=%r, xonxoff=%r, rtscts=%r, dsrdtr=%r)" % (
+ self.__class__.__name__,
+ id(self),
+ self._isOpen,
+ self.portstr,
+ self.baudrate,
+ self.bytesize,
+ self.parity,
+ self.stopbits,
+ self.timeout,
+ self.xonxoff,
+ self.rtscts,
+ self.dsrdtr,
+ )
+
+if __name__ == '__main__':
+ s = SerialBase()
+ print s.portstr
+ print s.getSupportedBaudrates()
+ print s.getSupportedByteSizes()
+ print s.getSupportedParities()
+ print s.getSupportedStopbits()
+ print s
diff --git a/plugins/rfid/tis2000.py b/plugins/rfid/tis2000.py
new file mode 100644
index 0000000..91d1991
--- /dev/null
+++ b/plugins/rfid/tis2000.py
@@ -0,0 +1,252 @@
+from device import RFIDDevice
+from serial import Serial
+import dbus
+from dbus.mainloop.glib import DBusGMainLoop
+import gobject
+import re
+from time import sleep
+
+HAL_SERVICE = 'org.freedesktop.Hal'
+HAL_MGR_PATH = '/org/freedesktop/Hal/Manager'
+HAL_MGR_IFACE = 'org.freedesktop.Hal.Manager'
+HAL_DEV_IFACE = 'org.freedesktop.Hal.Device'
+REGEXP_SERUSB = '\/org\/freedesktop\/Hal\/devices\/usb_device['\
+ 'a-z,A-Z,0-9,_]*serial_usb_[0-9]'
+
+STATE_WAITING = 0
+STATE_WAITING2 = 1
+STATE_READING = 2
+
+class RFIDReader(RFIDDevice):
+ """
+ TIS-2000 interface.
+ """
+
+ def __init__(self):
+
+ RFIDDevice.__init__(self)
+ self.last_tag = ""
+ self.ser = Serial()
+ self.device = ''
+ self.device_path = ''
+ self._connected = False
+ self._state = STATE_WAITING
+
+ loop = DBusGMainLoop()
+ self.bus = dbus.SystemBus(mainloop=loop)
+ hmgr_iface = dbus.Interface(self.bus.get_object(HAL_SERVICE,
+ HAL_MGR_PATH), HAL_MGR_IFACE)
+
+ hmgr_iface.connect_to_signal('DeviceRemoved', self._device_removed_cb)
+
+ def get_present(self):
+ """
+ Checks if TI-S2000 device is present.
+ Returns True if so, False otherwise.
+ """
+ hmgr_if = dbus.Interface(self.bus.get_object(HAL_SERVICE, HAL_MGR_PATH),
+ HAL_MGR_IFACE)
+ tiusb_devices = set(hmgr_if.FindDeviceStringMatch('serial.type',
+ 'usb')) & set(hmgr_if.FindDeviceStringMatch(
+ 'info.product', 'TUSB3410 Microcontroller'))
+ for i in tiusb_devices:
+ tiusb_if = dbus.Interface(self.bus.get_object(HAL_SERVICE, i),
+ HAL_DEV_IFACE)
+ if tiusb_if.PropertyExists('linux.device_file'):
+ self.device = str(tiusb_if.GetProperty('linux.device_file'))
+ self.device_path = i
+ return True
+ return False
+
+ def do_connect(self):
+ """
+ Connects to the device.
+ Returns True if successfull, False otherwise.
+ """
+ retval = False
+ if self.get_present():
+ try:
+ self.ser = Serial(self.device, 9600, timeout=0.1)
+ self._connected = True
+ self._escape()
+ self._clear()
+ self._format()
+ gobject.idle_add(self._loop)
+ retval = True
+ except:
+ self._connected = False
+ return retval
+
+ def do_disconnect(self):
+ """
+ Disconnect from the device.
+ """
+ self.ser.close()
+ self._connected = False
+
+ def read_tag(self):
+ """
+ Returns the last read value.
+ """
+ return self.last_tag
+
+ def write_tag(self, hexval):
+ """
+ Usage: write_tag(hexval)
+
+ Writes the hexadecimal string "hexval" into the tag.
+ Returns True if successfull, False otherwise.
+ """
+ #self.ser.flushInput()
+ reg = re.compile('([^0-9A-F]+)')
+ if not (hexval.__len__() == 16 and reg.findall(hexval) == []):
+ return False
+ self.ser.read(100)
+ self.ser.write('P')
+ for i in hexval:
+ self.ser.write(i)
+ sleep(1)
+ resp = self.ser.read(64)
+ resp = resp.split()[0]
+ if resp == "P0":
+ return True
+ else:
+ return False
+
+ def _escape(self):
+ """
+ Sends the scape command to the TIS-2000 device.
+ """
+ try:
+ #self.ser.flushInput()
+ self.ser.read(100)
+ self.ser.write('\x1B')
+ resp = self.ser.read()
+ if resp == 'E':
+ return True
+ else:
+ return False
+ except:
+ return False
+
+ def _format(self):
+ """
+ Sends the format command to the TIS-2000 device.
+ """
+ try:
+ #self.ser.flushInput()
+ self.ser.read(100)
+ self.ser.write('F')
+ resp = self.ser.read()
+ if resp == 'F':
+ return True
+ else:
+ return False
+ except:
+ return False
+
+ def _clear(self):
+ """
+ Sends the clear command to the TIS-2000 device.
+ """
+ try:
+ #self.ser.flushInput()
+ self.ser.read(100)
+ self.ser.write('C')
+ resp = self.ser.read()
+ if resp == 'C':
+ return True
+ else:
+ return False
+ except:
+ return False
+
+ def get_version(self):
+ """
+ Sends the version command to the TIS-2000 device and returns
+ a string with the device version.
+ """
+ #self.ser.flushInput()
+ self.ser.read(100)
+ self.ser.write('V')
+ version = []
+ tver = ""
+ while 1:
+ resp = self.ser.read()
+ if resp == '\x0A' or resp == '':
+ break
+ if resp != '\n' and resp != '\r':
+ version.append(resp)
+ for i in version:
+ tver = tver + i
+ if tver != "":
+ return tver
+ return "Unknown"
+
+ def _device_removed_cb(self, path):
+ """
+ Called when a device is removed.
+ Checks if the removed device is itself and emits the "disconnected"
+ signal if so.
+ """
+ if path == self.device_path:
+ self.device_path = ''
+ self.ser.close()
+ self._connected = False
+ self.emit("disconnected","TIS-2000")
+
+ def _loop(self):
+ """
+ Threaded loop for reading data sent from the TIS-2000.
+ """
+ if not self._connected:
+ return False
+
+ if self._state is STATE_WAITING:
+ data = self.ser.read()
+ if data in ['W', 'R']:
+ self._state = STATE_WAITING2
+ return True
+
+ elif self._state is STATE_WAITING2:
+ data = self.ser.read()
+ if data.isspace():
+ self._state = STATE_READING
+ else:
+ self._clear()
+ self._state = STATE_WAITING
+ return True
+
+ elif self._state is STATE_READING:
+ data = self.ser.read(16)
+ if data.__len__() < 16:
+ self._clear()
+ self._state = STATE_WAITING
+ else:
+ reg = re.compile('([^0-9A-F]+)')
+ if reg.findall(data) == []:
+ self.emit("tag-read", data)
+ self.last_tag = data
+ self._clear()
+ self._state = STATE_WAITING
+ return True
+ return True
+
+# Testing
+#if __name__ == '__main__':
+# def handler(device, idhex):
+# """
+# Handler for "tag-read" signal.
+# Prints the tag id.
+# """
+# print "ID: ", idhex
+#
+# dev = RFIDReader()
+# if dev.get_present():
+# dev.do_connect()
+# dev.connect('tag-read', handler)
+# else:
+# print "Not connected"
+#
+# mloop = gobject.MainLoop()
+# mloop.run()
diff --git a/plugins/rfid/utils.py b/plugins/rfid/utils.py
new file mode 100644
index 0000000..94e5540
--- /dev/null
+++ b/plugins/rfid/utils.py
@@ -0,0 +1,98 @@
+# utils.py - Helper functions for tis2000.py
+# Copyright (C) 2010 Emiliano Pastorino <epastorino@plan.ceibal.edu.uy>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+def strhex2bin(strhex):
+ """
+ Convert a string representing an hex value into a
+ string representing the same value in binary format.
+ """
+ dic = { '0':"0000",
+ '1':"0001",
+ '2':"0010",
+ '3':"0011",
+ '4':"0100",
+ '5':"0101",
+ '6':"0110",
+ '7':"0111",
+ '8':"1000",
+ '9':"1001",
+ 'A':"1010",
+ 'B':"1011",
+ 'C':"1100",
+ 'D':"1101",
+ 'E':"1110",
+ 'F':"1111"
+ }
+ binstr = ""
+ for i in strhex:
+ binstr = binstr + dic[i.upper()]
+ return binstr
+
+def strbin2dec(strbin):
+ """
+ Convert a string representing a binary value into a
+ string representing the same value in decimal format.
+ """
+ strdec = "0"
+ for i in range(1, strbin.__len__()+1):
+ strdec = str(int(strdec)+int(strbin[-i])*int(pow(2, i-1)))
+ return strdec
+
+def dec2bin(ndec):
+ """
+ Convert a decimal number into a string representing
+ the same value in binary format.
+ """
+ if ndec < 1:
+ return "0"
+ binary = []
+ while ndec != 0:
+ binary.append(ndec%2)
+ ndec = ndec/2
+ strbin = ""
+ binary.reverse()
+ for i in binary:
+ strbin = strbin+str(i)
+ return strbin
+
+def bin2hex(strbin):
+ """
+ Convert a string representing a binary number into a string
+ representing the same value in hexadecimal format.
+ """
+ dic = { "0000":"0",
+ "0001":"1",
+ "0010":"2",
+ "0011":"3",
+ "0100":"4",
+ "0101":"5",
+ "0110":"6",
+ "0111":"7",
+ "1000":"8",
+ "1001":"9",
+ "1010":"A",
+ "1011":"B",
+ "1100":"C",
+ "1101":"D",
+ "1110":"E",
+ "1111":"F"
+ }
+ while strbin.__len__()%4 != 0:
+ strbin = '0' + strbin
+ strh = ""
+ for i in range(0, strbin.__len__()/4):
+ strh = strh + dic[str(strbin[i*4:i*4+4])]
+ return strh
diff --git a/plugins/turtle_blocks_extras/__init__.py b/plugins/turtle_blocks_extras/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/turtle_blocks_extras/__init__.py
diff --git a/plugins/turtle_blocks_extras/__init__.pyc b/plugins/turtle_blocks_extras/__init__.pyc
new file mode 100644
index 0000000..d5c417b
--- /dev/null
+++ b/plugins/turtle_blocks_extras/__init__.pyc
Binary files differ
diff --git a/plugins/turtle_blocks_extras/icons/extrasoff.svg b/plugins/turtle_blocks_extras/icons/extrasoff.svg
new file mode 100644
index 0000000..7975a9d
--- /dev/null
+++ b/plugins/turtle_blocks_extras/icons/extrasoff.svg
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <rect
+ width="55"
+ height="55"
+ x="0"
+ y="0"
+ id="rect3269"
+ style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ <g
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="g3">
+ <polygon
+ points="20.555,2.531 26.891,12.363 17.238,16.369 14.659,4.975 "
+ id="polygon5"
+ style="fill:#ffffff" />
+ <polygon
+ points="52.477,20.55 42.646,26.88 38.649,17.228 50.04,14.654 "
+ id="polygon7"
+ style="fill:#ffffff" />
+ <polygon
+ points="34.453,52.471 28.117,42.645 37.775,38.645 40.349,50.029 "
+ id="polygon9"
+ style="fill:#ffffff" />
+ <polygon
+ points="40.295,4.882 37.824,16.315 28.171,12.309 34.394,2.439 "
+ id="polygon11"
+ style="fill:#ffffff" />
+ <polygon
+ points="50.055,40.258 38.628,37.791 42.623,28.139 52.493,34.365 "
+ id="polygon13"
+ style="fill:#ffffff" />
+ <polygon
+ points="4.953,40.234 16.385,37.76 12.391,28.105 2.515,34.34 "
+ id="polygon15"
+ style="fill:#ffffff" />
+ <polygon
+ points="2.493,20.539 12.319,26.875 16.32,17.216 4.936,14.643 "
+ id="polygon17"
+ style="fill:#ffffff" />
+ <polygon
+ points="20.609,52.461 26.939,42.623 17.287,38.629 14.719,50.018 "
+ id="polygon19"
+ style="fill:#ffffff" />
+ <path
+ d="m 39.925,22.352 c 2.845,6.863 -0.412,14.728 -7.274,17.574 -6.867,2.85 -14.734,-0.418 -17.578,-7.281 -2.84,-6.862 0.418,-14.733 7.279,-17.572 6.862,-2.845 14.734,0.417 17.573,7.279 z"
+ id="path21"
+ style="fill:none;stroke:#ffffff;stroke-width:11.69439983" />
+ </g>
+</svg>
diff --git a/plugins/turtle_blocks_extras/icons/extrason.svg b/plugins/turtle_blocks_extras/icons/extrason.svg
new file mode 100644
index 0000000..7ee08bf
--- /dev/null
+++ b/plugins/turtle_blocks_extras/icons/extrason.svg
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <metadata
+ id="metadata20">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs5">
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2431"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,32.193732)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2428"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,45.064925)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient3172"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient3166">
+ <stop
+ id="stop3168"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3170"
+ style="stop-color:#ffff00;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2557"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,9.2560985,9.9123239)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2561"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,8.962951,22.783517)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2461"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,32.193732)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2463"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,45.064925)" />
+ </defs>
+ <rect
+ width="55"
+ height="55"
+ rx="0"
+ x="0"
+ y="0"
+ id="rect2839"
+ style="fill:#ffd200;fill-opacity:1;fill-rule:evenodd;stroke:none" />
+ <g
+ id="g3799">
+ <polygon
+ points="20.555,2.531 26.891,12.363 17.238,16.369 14.659,4.975 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon5"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="52.477,20.55 42.646,26.88 38.649,17.228 50.04,14.654 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon7"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="34.453,52.471 28.117,42.645 37.775,38.645 40.349,50.029 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon9"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="40.295,4.882 37.824,16.315 28.171,12.309 34.394,2.439 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon11"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="50.055,40.258 38.628,37.791 42.623,28.139 52.493,34.365 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon13"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="4.953,40.234 16.385,37.76 12.391,28.105 2.515,34.34 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon15"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="2.493,20.539 12.319,26.875 16.32,17.216 4.936,14.643 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon17"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="20.609,52.461 26.939,42.623 17.287,38.629 14.719,50.018 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon19"
+ style="fill:#ff0000;fill-opacity:1" />
+ <path
+ d="m 35.45648,24.236169 c 1.8208,4.389511 -0.26368,9.419891 -4.65536,11.240166 -4.39488,1.822833 -9.42976,-0.267349 -11.24992,-4.65686 -1.8176,-4.388871 0.26752,-9.423089 4.65856,-11.238887 4.39168,-1.819635 9.42976,0.26671 11.24672,4.655581 z"
+ id="path21"
+ style="fill:none;stroke:#ff0000;stroke-width:7.48202181;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/plugins/turtle_blocks_extras/icons/mediaoff.svg b/plugins/turtle_blocks_extras/icons/mediaoff.svg
new file mode 100644
index 0000000..b2f460a
--- /dev/null
+++ b/plugins/turtle_blocks_extras/icons/mediaoff.svg
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <rect
+ width="55"
+ height="55"
+ x="0"
+ y="0"
+ id="rect3269"
+ style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+` <g
+ transform="matrix(0.67,0,0,0.67,9.075,9.075)"
+ id="g2855">
+ <path
+ d="m 46.7405,44.669 c 0,2.511 -1.528,4.331 -4.332,4.331 l -29.457,0 0,-43 29.458,0 c 2.15,0 4.332,2.154 4.332,4.33 l -0.001,34.339 0,0 z"
+ id="path2458"
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <line
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ x1="22.2155"
+ x2="22.2155"
+ y1="6.1209998"
+ y2="48.881001"
+ id="line2460" />
+ <path
+ d="m 8.2585,14.464 c 0,0 2.084,0.695 4.17,0.695 2.086,0 4.173,-0.695 4.173,-0.695"
+ id="path2462"
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 8.2585,28.021 c 0,0 1.912,0.695 4.345,0.695 2.433,0 3.999,-0.695 3.999,-0.695"
+ id="path2464"
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 8.2585,41.232 c 0,0 1.736,0.695 4.518,0.695 2.781,0 3.825,-0.695 3.825,-0.695"
+ id="path2466"
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/plugins/turtle_blocks_extras/icons/mediaon.svg b/plugins/turtle_blocks_extras/icons/mediaon.svg
new file mode 100644
index 0000000..27dd516
--- /dev/null
+++ b/plugins/turtle_blocks_extras/icons/mediaon.svg
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <rect
+ width="55"
+ height="55"
+ rx="0"
+ x="0"
+ y="0"
+ id="rect2839"
+ style="fill:#ffd200;fill-opacity:1;fill-rule:evenodd;stroke:none" />
+ <path
+ d="m 40.391135,39.00323 c 0,1.68237 -1.02376,2.90177 -2.90244,2.90177 l -19.73619,0 0,-28.81 19.73686,0 c 1.4405,0 2.90244,1.44318 2.90244,2.9011 l -6.7e-4,23.00713 0,0 z"
+ id="path2458"
+ style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <line
+ id="line2460"
+ y2="41.825272"
+ y1="13.17607"
+ x2="23.959385"
+ x1="23.959385"
+ style="fill:none;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 14.608195,18.76588 c 0,0 1.39628,0.46565 2.7939,0.46565 1.39762,0 2.79591,-0.46565 2.79591,-0.46565"
+ id="path2462"
+ style="fill:none;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 14.608195,27.84907 c 0,0 1.28104,0.46565 2.91115,0.46565 1.63011,0 2.67933,-0.46565 2.67933,-0.46565"
+ id="path2464"
+ style="fill:none;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 14.608195,36.70044 c 0,0 1.16312,0.46565 3.02706,0.46565 1.86327,0 2.56275,-0.46565 2.56275,-0.46565"
+ id="path2466"
+ style="fill:none;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+</svg>
diff --git a/plugins/turtle_blocks_extras/icons/portfoliooff.svg b/plugins/turtle_blocks_extras/icons/portfoliooff.svg
new file mode 100644
index 0000000..d404bab
--- /dev/null
+++ b/plugins/turtle_blocks_extras/icons/portfoliooff.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2"><metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+</metadata>
+<defs
+ id="defs12" />
+<rect
+ width="55"
+ height="55"
+ x="0"
+ y="0"
+ id="rect3269"
+ style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+
+
+` <g
+ transform="matrix(0.8501594,0,0,0.8501594,4.1206165,2.9055499)"
+ id="g3041">
+ <rect
+ width="40"
+ height="30"
+ x="7.5"
+ y="5.8274999"
+ id="rect2996"
+ style="fill:none;stroke:#ffffff;stroke-width:2.34500003;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <g
+ transform="translate(-1.0846148,0)"
+ id="g3793">
+ <line
+ id="line2460"
+ y2="52.31316"
+ y1="36.358479"
+ x2="23.419287"
+ x1="27.69433"
+ style="fill:none;stroke:#ffffff;stroke-width:1.78056884;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <line
+ id="line2460-2"
+ y2="52.313156"
+ y1="36.358479"
+ x2="33.749943"
+ x1="29.474899"
+ style="fill:none;stroke:#ffffff;stroke-width:1.78056884;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ </g>
+ <line
+ style="fill:none;stroke:#ffffff;stroke-width:2.66399026;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ x1="9.0131855"
+ x2="45.986813"
+ y1="8.168005"
+ y2="8.168005"
+ id="line2460-0" />
+</g>
+</svg>
diff --git a/plugins/turtle_blocks_extras/icons/portfolioon.svg b/plugins/turtle_blocks_extras/icons/portfolioon.svg
new file mode 100644
index 0000000..e174ee2
--- /dev/null
+++ b/plugins/turtle_blocks_extras/icons/portfolioon.svg
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <metadata
+ id="metadata3983">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs5">
+ <linearGradient
+ x1="66.800423"
+ y1="23.707363"
+ x2="203.4543"
+ y2="23.237999"
+ id="linearGradient2467"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.2497164,0,0,0.2514503,-11.493525,14.124173)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient3172"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient3166">
+ <stop
+ id="stop3168"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3170"
+ style="stop-color:#0000ff;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2557"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,9.2560985,9.9123239)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2561"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,8.962951,22.783517)" />
+ <linearGradient
+ x1="66.800423"
+ y1="23.707363"
+ x2="203.4543"
+ y2="23.237999"
+ id="linearGradient2567"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.2497164,0,0,0.2514503,-11.743525,13.874173)" />
+ <linearGradient
+ id="linearGradient2527">
+ <stop
+ id="stop2529"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop2531"
+ style="stop-color:#ffff00;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="0.94254935"
+ y1="-31.669659"
+ x2="104.37702"
+ y2="20.434471"
+ id="linearGradient2533"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.7083638,0,0,1.0012565,0.1338084,32.632067)" />
+ <linearGradient
+ id="linearGradient4342">
+ <stop
+ id="stop3259"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4345"
+ style="stop-color:#ff00ff;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="0"
+ x2="104"
+ y2="21"
+ id="linearGradient4340"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0139238,0,0,1.0946487,31.741439,7.7561892)" />
+ <linearGradient
+ x1="0"
+ y1="19.625"
+ x2="320.75"
+ y2="19.625"
+ id="linearGradient3170"
+ xlink:href="#linearGradient3164"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient3164">
+ <stop
+ id="stop3166"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4336"
+ style="stop-color:#ff00ff;stop-opacity:0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="66.800423"
+ y1="23.707363"
+ x2="203.4543"
+ y2="23.237999"
+ id="linearGradient4362"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.9988658,0,0,1.0058014,-66.724103,-0.0033087)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient4488"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient4482">
+ <stop
+ id="stop4484"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop4486"
+ style="stop-color:#ff00ff;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2550"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,8.962951,22.783517)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2548"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,9.2560985,9.9123239)" />
+ <linearGradient
+ id="linearGradient2540">
+ <stop
+ id="stop2542"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop2544"
+ style="stop-color:#ffff00;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2538"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ x1="66.800423"
+ y1="23.707363"
+ x2="203.4543"
+ y2="23.237999"
+ id="linearGradient2874"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.2497164,0,0,0.2514503,-5.118553,13.374173)" />
+ <linearGradient
+ x1="0.94254935"
+ y1="-31.669659"
+ x2="104.37702"
+ y2="20.434471"
+ id="linearGradient3172-9"
+ xlink:href="#linearGradient3166-6"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.7083638,0,0,1.0012565,0.1338084,32.632067)" />
+ <linearGradient
+ id="linearGradient3166-6">
+ <stop
+ id="stop3168-5"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3170-6"
+ style="stop-color:#ffff00;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ </defs>
+ <rect
+ width="55"
+ height="55"
+ rx="0"
+ x="0"
+ y="0"
+ id="rect2839"
+ style="fill:#ffd200;fill-opacity:1;fill-rule:evenodd;stroke:none" />
+ <g
+ transform="matrix(0.8501594,0,0,0.8501594,4.1206165,2.9055499)"
+ id="g3816"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-opacity:1">
+ <rect
+ width="40"
+ height="30"
+ x="7.5"
+ y="5.8274999"
+ id="rect2996"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-width:2.34500003;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <g
+ transform="translate(-1.0846148,0)"
+ id="g3793"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-opacity:1">
+ <line
+ id="line2460-5"
+ y2="52.31316"
+ y1="36.358479"
+ x2="23.419287"
+ x1="27.69433"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-width:1.78056884;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <line
+ id="line2460-2"
+ y2="52.313156"
+ y1="36.358479"
+ x2="33.749943"
+ x1="29.474899"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-width:1.78056884;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ </g>
+ <line
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-width:2.66399026;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ x1="9.0131855"
+ x2="45.986813"
+ y1="8.168005"
+ y2="8.168005"
+ id="line2460-0" />
+ </g>
+</svg>
diff --git a/plugins/turtle_blocks_extras/icons/sensoroff.svg b/plugins/turtle_blocks_extras/icons/sensoroff.svg
new file mode 100644
index 0000000..ec55f03
--- /dev/null
+++ b/plugins/turtle_blocks_extras/icons/sensoroff.svg
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ width="55"
+ height="55"
+ viewBox="0 0 55 55"
+ id="svg2"
+ xml:space="preserve"><metadata
+ id="metadata15"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs13" /><rect
+ width="55"
+ height="55"
+ x="0"
+ y="0"
+ id="rect3269"
+ style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
+ transform="matrix(0.67,0,0,0.67,9.075,9.0749996)"
+ id="toolbar_x5F_view"
+ style="fill:#ffffff;fill-opacity:1;display:block">
+ <g
+ id="g5"
+ style="fill:#ffffff;fill-opacity:1">
+ <path
+ d="m 21.937,27.499 c 0,3.07 2.492,5.562 5.562,5.562 3.067,0 5.559,-2.492 5.559,-5.562 0,-3.067 -2.491,-5.564 -5.559,-5.564 -3.07,0 -5.562,2.497 -5.562,5.564 z"
+ id="path7"
+ style="fill:#ffffff;fill-opacity:1" />
+ <path
+ d="m 27.499,13.037 c -14.476,0 -26.531,14.432 -26.531,14.432 0,0 12.056,14.504 26.531,14.494 14.474,-0.01 26.533,-14.513 26.533,-14.513 0,0 -12.059,-14.425 -26.533,-14.413 z M 27.5,36.562 c -5.003,0 -9.063,-4.061 -9.063,-9.063 0,-4.998 4.061,-9.066 9.063,-9.066 4.997,0 9.058,4.068 9.058,9.066 0,5.003 -4.061,9.063 -9.058,9.063 z"
+ id="path9"
+ style="fill:#ffffff;fill-opacity:1" />
+ </g>
+</g></svg> \ No newline at end of file
diff --git a/plugins/turtle_blocks_extras/icons/sensoron.svg b/plugins/turtle_blocks_extras/icons/sensoron.svg
new file mode 100644
index 0000000..e902656
--- /dev/null
+++ b/plugins/turtle_blocks_extras/icons/sensoron.svg
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.1"
+ width="55"
+ height="55"
+ viewBox="0 0 55 55"
+ id="svg2"
+ xml:space="preserve"><metadata
+ id="metadata15"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs13" /><rect
+ width="55"
+ height="55"
+ x="0"
+ y="0"
+ id="rect3269"
+ style="fill:#ffd200;fill-opacity:1;fill-rule:nonzero;stroke:none" /><g
+ transform="matrix(0.67,0,0,0.67,9.075,9.0749996)"
+ id="toolbar_x5F_view"
+ style="fill:#ff0000;fill-opacity:1;display:block">
+ <g
+ id="g5"
+ style="fill:#ff0000;fill-opacity:1">
+ <path
+ d="m 21.937,27.499 c 0,3.07 2.492,5.562 5.562,5.562 3.067,0 5.559,-2.492 5.559,-5.562 0,-3.067 -2.491,-5.564 -5.559,-5.564 -3.07,0 -5.562,2.497 -5.562,5.564 z"
+ id="path7"
+ style="fill:#ff0000;fill-opacity:1" />
+ <path
+ d="m 27.499,13.037 c -14.476,0 -26.531,14.432 -26.531,14.432 0,0 12.056,14.504 26.531,14.494 14.474,-0.01 26.533,-14.513 26.533,-14.513 0,0 -12.059,-14.425 -26.533,-14.413 z M 27.5,36.562 c -5.003,0 -9.063,-4.061 -9.063,-9.063 0,-4.998 4.061,-9.066 9.063,-9.066 4.997,0 9.058,4.068 9.058,9.066 0,5.003 -4.061,9.063 -9.058,9.063 z"
+ id="path9"
+ style="fill:#ff0000;fill-opacity:1" />
+ </g>
+</g></svg> \ No newline at end of file
diff --git a/plugins/turtle_blocks_extras/turtle_blocks_extras.py b/plugins/turtle_blocks_extras/turtle_blocks_extras.py
new file mode 100644
index 0000000..fbd5db5
--- /dev/null
+++ b/plugins/turtle_blocks_extras/turtle_blocks_extras.py
@@ -0,0 +1,1213 @@
+# -*- coding: utf-8 -*-
+#Copyright (c) 2011, Walter Bender
+#
+# 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
+from time import time
+import os.path
+
+try:
+ from sugar.datastore import datastore
+except ImportError:
+ pass
+
+from plugins.plugin import Plugin
+from TurtleArt.tapalette import make_palette
+from TurtleArt.talogo import primitive_dictionary, logoerror, \
+ media_blocks_dictionary
+from TurtleArt.taconstants import DEFAULT_SCALE, ICON_SIZE, CONSTANTS
+from TurtleArt.tautils import convert, round_int, debug_output
+from TurtleArt.tajail import myfunc, myfunc_import
+
+
+def _num_type(x):
+ """ Is x a number type? """
+ if type(x) == int:
+ return True
+ if type(x) == float:
+ return True
+ if type(x) == ord:
+ return True
+ return False
+
+
+def _string_to_num(x):
+ """ Try to comvert a string to a number """
+ xx = convert(x.replace(self.tw.decimal_point, '.'), float)
+ if type(xx) is float:
+ return xx
+ else:
+ xx, xflag = chr_to_ord(x)
+ if xflag:
+ return xx
+ else:
+ raise logoerror("#syntaxerror")
+
+
+def _millisecond():
+ """ Current time in milliseconds """
+ return time() * 1000
+
+
+class Turtle_blocks_extras(Plugin):
+ """ a class for defining the extra palettes that distinguish Turtle Blocks
+ from Turtle Art """
+
+ def __init__(self, parent):
+ self.tw = parent
+
+ def setup(self):
+ self.heap = self.tw.lc.heap
+ self.keyboard = self.tw.lc.keyboard
+ self.title_height = int((self.tw.canvas.height / 20) * self.tw.scale)
+
+ # set up Turtle Block palettes
+ self._flow_palette()
+
+ self._media_palette()
+
+ self._sensor_palette()
+
+ self._extras_palette()
+
+ self._portfolio_palette()
+
+ # Palette definitions
+
+ def _flow_palette(self):
+ palette = make_palette('flow',
+ colors=["#FFC000", "#A08000"],
+ help_string=_('Palette of flow operators'))
+
+ # macro
+ palette.add_block('while',
+ style='flow-style-boolean',
+ label=_('while'),
+ help_string=_('do-while-True operator that uses \
+boolean operators from Numbers palette'))
+
+ # macro
+ palette.add_block('until',
+ style='flow-style-boolean',
+ label=_('until'),
+ help_string=_('do-until-True operator that uses \
+boolean operators from Numbers palette'))
+
+ def _media_palette(self):
+
+ palette = make_palette('media',
+ colors=["#A0FF00", "#80A000"],
+ help_string=_('Palette of media objects'))
+
+ palette.add_block('journal',
+ style='box-style-media',
+ label=' ',
+ default='None',
+ special_name=_('journal'),
+ help_string=_('Sugar Journal media object'))
+
+ palette.add_block('audio',
+ style='box-style-media',
+ label=' ',
+ special_name=_('audio'),
+ default='None',
+ help_string=_('Sugar Journal audio object'))
+
+ palette.add_block('video',
+ style='box-style-media',
+ label=' ',
+ special_name=_('video'),
+ default='None',
+ help_string=_('Sugar Journal video object'))
+
+ palette.add_block('description',
+ style='box-style-media',
+ label=' ',
+ special_name=_('description'),
+ default='None',
+ help_string=_('Sugar Journal description field'))
+
+ palette.add_block('string',
+ style='box-style',
+ label=_('text'),
+ default=_('text'),
+ special_name='',
+ help_string=_('string value'))
+
+ primitive_dictionary['show'] = self._prim_show
+ palette.add_block('show',
+ style='basic-style-1arg',
+ label=_('show'),
+ default=_('text'),
+ prim_name='show',
+ logo_command='label',
+ help_string=_('draws text or show media from the \
+Journal'))
+ self.tw.lc.def_prim('show', 1,
+ lambda self, x: primitive_dictionary['show'](x, True))
+
+ palette.add_block('showaligned',
+ hidden=True,
+ style='basic-style-1arg',
+ label=_('show aligned'),
+ default=_('text'),
+ prim_name='showaligned',
+ logo_command='label',
+ help_string=_('draws text or show media from the \
+Journal'))
+ self.tw.lc.def_prim('showaligned', 1,
+ lambda self, x: primitive_dictionary['show'](x, False))
+
+ # deprecated
+ primitive_dictionary['write'] = self._prim_write
+ palette.add_block('write',
+ hidden=True,
+ style='basic-style-1arg',
+ label=_('show'),
+ default=[_('text'), 32],
+ prim_name='write',
+ logo_command='label',
+ help_string=_('draws text or show media from the \
+Journal'))
+ self.tw.lc.def_prim('write', 2,
+ lambda self, x, y: primitive_dictionary['write'](x, y))
+
+ primitive_dictionary['setscale'] = self._prim_setscale
+ palette.add_block('setscale',
+ style='basic-style-1arg',
+ label=_('set scale'),
+ prim_name='setscale',
+ default=33,
+ logo_command='setlabelheight',
+ help_string=_('sets the scale of media'))
+ self.tw.lc.def_prim('setscale', 1,
+ lambda self, x: primitive_dictionary['setscale'](x))
+
+ primitive_dictionary['savepix'] = self._prim_save_picture
+ palette.add_block('savepix',
+ style='basic-style-1arg',
+ label=_('save picture'),
+ prim_name='savepix',
+ default=_('picture name'),
+ help_string=_('saves a picture to the Sugar \
+Journal'))
+ self.tw.lc.def_prim('savepix', 1,
+ lambda self, x: primitive_dictionary['savepix'](x))
+
+ primitive_dictionary['savesvg'] = self._prim_save_svg
+ palette.add_block('savesvg',
+ style='basic-style-1arg',
+ label=_('save SVG'),
+ prim_name='savesvg',
+ default=_('picture name'),
+ help_string=_('saves turtle graphics as an SVG file \
+in the Sugar Journal'))
+ self.tw.lc.def_prim('savesvg', 1,
+ lambda self, x: primitive_dictionary['savesvg'](x))
+
+ palette.add_block('scale',
+ style='box-style',
+ label=_('scale'),
+ prim_name='scale',
+ value_block=True,
+ logo_command='labelsize',
+ help_string=_('holds current scale value'))
+ self.tw.lc.def_prim('scale', 0, lambda self: self.tw.lc.scale)
+
+ palette.add_block('mediawait',
+ style='basic-style-extended-vertical',
+ label=_('media wait'),
+ prim_name='mediawait',
+ help_string=_('wait for current video or audio to \
+complete'))
+ self.tw.lc.def_prim('mediawait', 0, self.tw.lc.media_wait, True)
+
+ def _sensor_palette(self):
+
+ palette = make_palette('sensor',
+ colors=["#FF6060", "#A06060"],
+ help_string=_('Palette of sensor blocks'))
+
+ primitive_dictionary['kbinput'] = self._prim_kbinput
+ palette.add_block('kbinput',
+ style='basic-style-extended-vertical',
+ label=_('query keyboard'),
+ prim_name='kbinput',
+ help_string=_('query for keyboard input (results \
+stored in keyboard block)'))
+ self.tw.lc.def_prim('kbinput', 0,
+ lambda self: primitive_dictionary['kbinput']())
+
+ palette.add_block('keyboard',
+ style='box-style',
+ label=_('keyboard'),
+ prim_name='keyboard',
+ value_block=True,
+ logo_command='make "keyboard readchar',
+ help_string=_('holds results of query-keyboard \
+block'))
+ self.tw.lc.def_prim('keyboard', 0, lambda self: self.tw.lc.keyboard)
+
+ primitive_dictionary['readpixel'] = self._prim_readpixel
+ palette.add_block('readpixel',
+ style='basic-style-extended-vertical',
+ label=_('read pixel'),
+ prim_name='readpixel',
+ logo_command=':keyboard',
+ help_string=_('RGB color under the turtle is pushed \
+to the stack'))
+ self.tw.lc.def_prim('readpixel', 0,
+ lambda self: primitive_dictionary['readpixel']())
+
+ primitive_dictionary['see'] = self._prim_see
+ palette.add_block('see',
+ style='box-style',
+ label=_('turtle sees'),
+ prim_name='see',
+ help_string=_('returns the color that the turtle \
+"sees"'))
+ self.tw.lc.def_prim('see', 0,
+ lambda self: primitive_dictionary['see']())
+
+ primitive_dictionary['time'] = self._prim_time
+ palette.add_block('time',
+ style='box-style',
+ label=_('time'),
+ prim_name='time',
+ value_block=True,
+ help_string=_('elapsed time (in seconds) since \
+program started'))
+ self.tw.lc.def_prim('time', 0,
+ lambda self: primitive_dictionary['time']())
+
+ def _extras_palette(self):
+
+ palette = make_palette('extras',
+ colors=["#FF0000", "#A00000"],
+ help_string=_('Palette of extra options'))
+
+ primitive_dictionary['push'] = self._prim_push
+ palette.add_block('push',
+ style='basic-style-1arg',
+ label=_('push'),
+ prim_name='push',
+ logo_command='tapush',
+ help_string=_('pushes value onto FILO (first-in \
+last-out heap)'))
+ self.tw.lc.def_prim('push', 1,
+ lambda self, x: primitive_dictionary['push'](x))
+
+ primitive_dictionary['printheap'] = self._prim_printheap
+ palette.add_block('printheap',
+ style='basic-style-extended-vertical',
+ label=_('show heap'),
+ prim_name='printheap',
+ logo_command='taprintheap',
+ help_string=_('shows values in FILO (first-in \
+last-out heap)'))
+ self.tw.lc.def_prim('printheap', 0,
+ lambda self: primitive_dictionary['printheap']())
+
+ primitive_dictionary['clearheap'] = self._prim_emptyheap
+ palette.add_block('clearheap',
+ style='basic-style-extended-vertical',
+ label=_('empty heap'),
+ prim_name='clearheap',
+ logo_command='taclearheap',
+ help_string=_('emptys FILO (first-in-last-out \
+heap)'))
+ self.tw.lc.def_prim('clearheap', 0,
+ lambda self: primitive_dictionary['clearheap']())
+
+ primitive_dictionary['pop'] = self._prim_pop
+ palette.add_block('pop',
+ style='box-style',
+ label=_('pop'),
+ prim_name='pop',
+ value_block=True,
+ logo_command='tapop',
+ help_string=_('pops value off FILO (first-in \
+last-out heap)'))
+ self.tw.lc.def_prim('pop', 0,
+ lambda self: primitive_dictionary['pop']())
+
+ primitive_dictionary['print'] = self._prim_print
+ palette.add_block('comment',
+ style='basic-style-1arg',
+ label=_('comment'),
+ prim_name='comment',
+ default=_('comment'),
+ help_string=_('places a comment in your code'))
+ self.tw.lc.def_prim('comment', 1,
+ lambda self, x: primitive_dictionary['print'](x, True))
+
+ palette.add_block('print',
+ style='basic-style-1arg',
+ label=_('print'),
+ prim_name='print',
+ logo_command='label',
+ help_string=_('prints value in status block at \
+bottom of the screen'))
+ self.tw.lc.def_prim('print', 1,
+ lambda self, x: primitive_dictionary['print'](x, False))
+
+ primitive_dictionary['myfunction'] = self._prim_myfunction
+ palette.add_block('myfunc1arg',
+ style='number-style-var-arg',
+ label=[_('Python'), 'f(x)', 'x'],
+ prim_name='myfunction',
+ default=['x', 100],
+ help_string=_('a programmable block: used to add \
+advanced single-variable math equations, e.g., sin(x)'))
+ self.tw.lc.def_prim('myfunction', 2,
+ lambda self, f, x: primitive_dictionary['myfunction'](f, [x]))
+
+ palette.add_block('myfunc2arg',
+ hidden=True,
+ style='number-style-var-arg',
+ label=[_('Python'), 'f(x,y)', 'x'],
+ prim_name='myfunction2',
+ default=['x+y', 100, 100],
+ help_string=_('a programmable block: used to add \
+advanced multi-variable math equations, e.g., sqrt(x*x+y*y)'))
+ self.tw.lc.def_prim('myfunction2', 3,
+ lambda self, f, x, y: primitive_dictionary['myfunction'](
+ f, [x, y]))
+
+ palette.add_block('myfunc3arg',
+ hidden=True,
+ style='number-style-var-arg',
+ label=[_('Python'), 'f(x,y,z)', 'x'],
+ prim_name='myfunction3',
+ default=['x+y+z', 100, 100, 100],
+ help_string=_('a programmable block: used to add \
+advanced multi-variable math equations, e.g., sin(x+y+z)'))
+ self.tw.lc.def_prim('myfunction3', 4,
+ lambda self, f, x, y, z: primitive_dictionary['myfunction'](
+ f, [x, y, z]))
+
+ primitive_dictionary['userdefined'] = self._prim_myblock
+ palette.add_block('userdefined',
+ style='basic-style-var-arg',
+ label=' ',
+ prim_name='userdefined',
+ special_name=_('Python block'),
+ default=100,
+ help_string=_('runs code found in the tamyblock.py \
+module found in the Journal'))
+ self.tw.lc.def_prim('userdefined', 1,
+ lambda self, x: primitive_dictionary['userdefined']([x]))
+
+ palette.add_block('userdefined2args',
+ hidden=True,
+ style='basic-style-var-arg',
+ label=' ',
+ prim_name='userdefined2',
+ special_name=_('Python block'),
+ default=[100, 100],
+ help_string=_('runs code found in the tamyblock.py \
+module found in the Journal'))
+ self.tw.lc.def_prim('userdefined2', 2,
+ lambda self, x, y: primitive_dictionary['userdefined']([x, y]))
+
+ palette.add_block('userdefined3args',
+ hidden=True,
+ style='basic-style-var-arg',
+ label=' ',
+ prim_name='userdefined3',
+ special_name=_('Python block'),
+ default=[100, 100, 100],
+ help_string=_('runs code found in the tamyblock.py \
+module found in the Journal'))
+ self.tw.lc.def_prim('userdefined3', 3,
+ lambda self, x, y, z: primitive_dictionary['userdefined'](
+ [x, y, z]))
+
+ palette.add_block('cartesian',
+ style='basic-style-extended-vertical',
+ label=_('Cartesian'),
+ prim_name='cartesian',
+ help_string=_('displays Cartesian coordinates'))
+ self.tw.lc.def_prim('cartesian', 0,
+ lambda self: self.tw.set_cartesian(True))
+
+ palette.add_block('polar',
+ style='basic-style-extended-vertical',
+ label=_('polar'),
+ prim_name='polar',
+ help_string=_('displays polar coordinates'))
+ self.tw.lc.def_prim('polar', 0,
+ lambda self: self.tw.set_polar(True))
+
+ palette.add_block('addturtle',
+ style='basic-style-1arg',
+ label=_('turtle'),
+ prim_name='turtle',
+ default=1,
+ help_string=_('chooses which turtle to command'))
+ self.tw.lc.def_prim('turtle', 1,
+ lambda self, x: self.tw.canvas.set_turtle(x))
+
+ primitive_dictionary['skin'] = self._prim_reskin
+ palette.add_block('skin',
+ hidden=True,
+ style='basic-style-1arg',
+ label=_('turtle shell'),
+ prim_name='skin',
+ help_string=_("put a custom 'shell' on the turtle"))
+ self.tw.lc.def_prim('skin', 1,
+ lambda self, x: primitive_dictionary['skin'](x))
+
+ # macro
+ palette.add_block('reskin',
+ style='basic-style-1arg',
+ label=_('turtle shell'),
+ help_string=_("put a custom 'shell' on the turtle"))
+
+ palette.add_block('sandwichtop_no_label',
+ style='collapsible-top-no-label',
+ label=[' ', ' '],
+ special_name=_('top'),
+ prim_name='nop',
+ help_string=_('top of a collapsed stack'))
+
+ palette.add_block('sandwichbottom',
+ style='collapsible-bottom',
+ label=[' ', ' '],
+ prim_name='nop',
+ special_name=_('bottom'),
+ help_string=_('bottom of a collapsible stack'))
+
+ palette.add_block('sandwichcollapsed',
+ hidden=True,
+ style='invisible',
+ label=' ',
+ prim_name='nop',
+ help_string=_('bottom block in a collapsed stack: \
+click to open'))
+
+ # deprecated blocks
+ palette.add_block('sandwichtop',
+ hidden=True,
+ style='collapsible-top',
+ label=_('top of stack'),
+ default=_('label'),
+ prim_name='comment',
+ help_string=_('top of stack'))
+
+ palette.add_block('sandwichtop_no_arm',
+ hidden=True,
+ style='collapsible-top-no-arm',
+ label=_('top of a collapsible stack'),
+ default=_('label'),
+ prim_name='comment',
+ help_string=_('top of stack'))
+
+ palette.add_block('sandwichtop_no_arm_no_label',
+ hidden=True,
+ style='collapsible-top-no-arm-no-label',
+ label=[' ', _('click to open')],
+ prim_name='nop',
+ help_string=_('top of stack'))
+
+ def _portfolio_palette(self):
+
+ palette = make_palette('portfolio',
+ colors=["#0606FF", "#0606A0"],
+ help_string=_('Palette of presentation templates'))
+
+ palette.add_block('hideblocks',
+ style='basic-style-extended-vertical',
+ label=_('hide blocks'),
+ prim_name='hideblocks',
+ help_string=_('declutters canvas by hiding blocks'))
+ self.tw.lc.def_prim('hideblocks', 0, lambda self: self.tw.hideblocks())
+
+ palette.add_block('showblocks',
+ style='basic-style-extended-vertical',
+ label=_('show blocks'),
+ prim_name='showblocks',
+ help_string=_('restores hidden blocks'))
+ self.tw.lc.def_prim('showblocks', 0, lambda self: self.tw.showblocks())
+
+ palette.add_block('fullscreen',
+ style='basic-style-extended-vertical',
+ label=_('full screen'),
+ prim_name='fullscreen',
+ help_string=_('hides the Sugar toolbars'))
+ self.tw.lc.def_prim('fullscreen', 0,
+ lambda self: self.tw.set_fullscreen())
+
+ primitive_dictionary['bulletlist'] = self._prim_list
+ palette.add_block('list',
+ hidden=True,
+ style='bullet-style',
+ label=_('list'),
+ prim_name='bulletlist',
+ default=['∙ ', '∙ '],
+ help_string=_('presentation bulleted list'))
+ self.tw.lc.def_prim('bulletlist', 1,
+ primitive_dictionary['bulletlist'], True)
+
+ # macros
+ palette.add_block('picturelist',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: list of \
+bullets'))
+
+ palette.add_block('picture1x1a',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select \
+Journal object (no description)'))
+
+ palette.add_block('picture1x1',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select \
+Journal object (with description)'))
+
+ palette.add_block('picture2x2',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select four \
+Journal objects'))
+
+ palette.add_block('picture2x1',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select two \
+Journal objects'))
+
+ palette.add_block('picture1x2',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select two \
+Journal objects'))
+
+ # Display-dependent constants
+ palette.add_block('leftpos',
+ style='box-style',
+ label=_('left'),
+ prim_name='lpos',
+ logo_command='lpos',
+ help_string=_('xcor of left of screen'))
+ self.tw.lc.def_prim('lpos', 0, lambda self: CONSTANTS['leftpos'])
+
+ palette.add_block('bottompos',
+ style='box-style',
+ label=_('bottom'),
+ prim_name='bpos',
+ logo_command='bpos',
+ help_string=_('ycor of bottom of screen'))
+ self.tw.lc.def_prim('bpos', 0, lambda self: CONSTANTS['bottompos'])
+
+ palette.add_block('width',
+ style='box-style',
+ label=_('width'),
+ prim_name='hres',
+ logo_command='width',
+ help_string=_('the canvas width'))
+ self.tw.lc.def_prim('hres', 0, lambda self: CONSTANTS['width'])
+
+ palette.add_block('rightpos',
+ style='box-style',
+ label=_('right'),
+ prim_name='rpos',
+ logo_command='rpos',
+ help_string=_('xcor of right of screen'))
+ self.tw.lc.def_prim('rpos', 0, lambda self: CONSTANTS['rightpos'])
+
+ palette.add_block('toppos',
+ style='box-style',
+ label=_('top'),
+ prim_name='tpos',
+ logo_command='tpos',
+ help_string=_('ycor of top of screen'))
+ self.tw.lc.def_prim('tpos', 0, lambda self: CONSTANTS['toppos'])
+
+ palette.add_block('height',
+ style='box-style',
+ label=_('height'),
+ prim_name='vres',
+ logo_command='height',
+ help_string=_('the canvas height'))
+ self.tw.lc.def_prim('vres', 0, lambda self: CONSTANTS['height'])
+
+ palette.add_block('titlex',
+ hidden=True,
+ style='box-style',
+ label=_('title x'),
+ logo_command='titlex',
+ prim_name='titlex')
+ self.tw.lc.def_prim('titlex', 0, lambda self: CONSTANTS['titlex'])
+
+ palette.add_block('titley',
+ hidden=True,
+ style='box-style',
+ label=_('title y'),
+ logo_command='titley',
+ prim_name='titley')
+ self.tw.lc.def_prim('titley', 0, lambda self: CONSTANTS['titley'])
+
+ palette.add_block('leftx',
+ hidden=True,
+ style='box-style',
+ label=_('left x'),
+ prim_name='leftx',
+ logo_command='leftx')
+ self.tw.lc.def_prim('leftx', 0, lambda self: CONSTANTS['leftx'])
+
+ palette.add_block('topy',
+ hidden=True,
+ style='box-style',
+ label=_('top y'),
+ prim_name='topy',
+ logo_command='topy')
+ self.tw.lc.def_prim('topy', 0, lambda self: CONSTANTS['topy'])
+
+ palette.add_block('rightx',
+ hidden=True,
+ style='box-style',
+ label=_('right x'),
+ prim_name='rightx',
+ logo_command='rightx')
+ self.tw.lc.def_prim('rightx', 0, lambda self: CONSTANTS['rightx'])
+
+ palette.add_block('bottomy',
+ hidden=True,
+ style='box-style',
+ label=_('bottom y'),
+ prim_name='bottomy',
+ logo_command='bottomy')
+ self.tw.lc.def_prim('bottomy', 0, lambda self: CONSTANTS['bottomy'])
+
+ # deprecated blocks
+
+ primitive_dictionary['t1x1'] = self._prim_t1x1
+ palette.add_block('template1x1',
+ hidden=True,
+ style='portfolio-style-1x1',
+ label=' ',
+ prim_name='t1x1',
+ default=[_('Title'), 'None'],
+ special_name=_('presentation 1x1'),
+ help_string=_('presentation template: select \
+Journal object (with description)'))
+ self.tw.lc.def_prim('t1x1', 2,
+ lambda self, a, b: primitive_dictionary['t1x1'](a, b))
+
+ primitive_dictionary['t1x1a'] = self._prim_t1x1a
+ palette.add_block('template1x1a',
+ hidden=True,
+ style='portfolio-style-1x1',
+ label=' ',
+ prim_name='t1x1a',
+ default=[_('Title'), 'None'],
+ special_name=_('presentation 1x1'),
+ help_string=_('presentation template: select \
+Journal object (no description)'))
+ self.tw.lc.def_prim('t1x1a', 2,
+ lambda self, a, b: primitive_dictionary['t1x1a'](a, b))
+
+ primitive_dictionary['2x1'] = self._prim_t2x1
+ palette.add_block('template2x1',
+ hidden=True,
+ style='portfolio-style-2x1',
+ label=' ',
+ prim_name='t2x1',
+ default=[_('Title'), 'None', 'None'],
+ special_name=_('presentation 2x1'),
+ help_string=_("presentation template: select two \
+Journal objects"))
+ self.tw.lc.def_prim('t2x1', 3,
+ lambda self, a, b, c: primitive_dictionary['t2x1'](a, b, c))
+
+ primitive_dictionary['1x2'] = self._prim_t1x2
+ palette.add_block('template1x2',
+ hidden=True,
+ style='portfolio-style-1x2',
+ label=' ',
+ prim_name='t1x2',
+ default=[_('Title'), 'None', 'None'],
+ special_name=_('presentation 1x2'),
+ help_string=_("presentation template: select two \
+Journal objects"))
+ self.tw.lc.def_prim('t1x2', 3,
+ lambda self, a, b, c: primitive_dictionary['t1x2'](a, b, c))
+
+ primitive_dictionary['t2x2'] = self._prim_t2x2
+ palette.add_block('template2x2',
+ hidden=True,
+ style='portfolio-style-2x2',
+ label=' ',
+ prim_name='t2x2',
+ default=[_('Title'), 'None', 'None', 'None', 'None'],
+ special_name=_('presentation 2x2'),
+ help_string=_("presentation template: select four \
+Journal objects"))
+ self.tw.lc.def_prim('t2x2', 5,
+ lambda self, a, b, c, d, e: primitive_dictionary['t2x2'](
+ a, b, c, d, e))
+
+ palette.add_block('templatelist',
+ hidden=True,
+ style='bullet-style',
+ label=' ',
+ prim_name='bullet',
+ default=[_('Title'), '∙ '],
+ special_name=_('presentation bulleted list'),
+ help_string=_('presentation template: list of \
+bullets'))
+ self.tw.lc.def_prim('bullet', 1, self._prim_list, True)
+
+ # Block primitives
+
+ def _prim_emptyheap(self):
+ """ Empty FILO """
+ self.tw.lc.heap = []
+
+ def _prim_kbinput(self):
+ """ Query keyboard """
+ if len(self.tw.keypress) == 1:
+ self.tw.lc.keyboard = ord(self.tw.keypress[0])
+ else:
+ try:
+ self.tw.lc.keyboard = {'Escape': 27, 'space': 32, ' ': 32,
+ 'Return': 13, 'KP_Up': 2, 'KP_Down': 4, 'KP_Left': 1,
+ 'KP_Right': 3}[self.tw.keypress]
+ except KeyError:
+ self.tw.lc.keyboard = 0
+ self.tw.lc.update_label_value('keyboard', self.tw.lc.keyboard)
+ self.tw.keypress = ''
+
+ def _prim_list(self, blklist):
+ """ Expandable list block """
+ self._prim_showlist(blklist)
+ self.tw.lc.ireturn()
+ yield True
+
+ def _prim_myblock(self, x):
+ """ Run Python code imported from Journal """
+ if self.tw.lc.bindex is not None and \
+ self.tw.lc.bindex in self.tw.myblock:
+ try:
+ if len(x) == 1:
+ myfunc_import(self, self.tw.myblock[self.tw.lc.bindex],
+ x[0])
+ else:
+ myfunc_import(self, self.tw.myblock[self.tw.lc.bindex], x)
+ except:
+ raise logoerror("#syntaxerror")
+
+ def _prim_myfunction(self, f, x):
+ """ Programmable block """
+ try:
+ y = myfunc(f, x)
+ if str(y) == 'nan':
+ debug_output('Python function returned NAN',
+ self.tw.running_sugar)
+ self.tw.lc.stop_logo()
+ raise logoerror("#notanumber")
+ else:
+ return y
+ except ZeroDivisionError:
+ self.tw.lc.stop_logo()
+ raise logoerror("#zerodivide")
+ except ValueError, e:
+ self.tw.lc.stop_logo()
+ raise logoerror('#' + str(e))
+ except SyntaxError, e:
+ self.tw.lc.stop_logo()
+ raise logoerror('#' + str(e))
+ except NameError, e:
+ self.tw.lc.stop_logo()
+ raise logoerror('#' + str(e))
+ except OverflowError:
+ self.tw.lc.stop_logo()
+ raise logoerror("#overflowerror")
+ except TypeError:
+ self.tw.lc.stop_logo()
+ raise logoerror("#notanumber")
+
+ def _prim_pop(self):
+ """ Pop value off of FILO """
+ if len(self.tw.lc.heap) == 0:
+ raise logoerror("#emptyheap")
+ else:
+ if len(self.tw.lc.heap) == 1:
+ self.tw.lc.update_label_value('pop')
+ else:
+ self.tw.lc.update_label_value('pop', self.tw.lc.heap[-2])
+ return self.tw.lc.heap.pop(-1)
+
+ def _prim_print(self, n, flag):
+ """ Print object n """
+ if flag and (self.tw.hide or self.tw.step_time == 0):
+ return
+ if type(n) == str or type(n) == unicode:
+ if n[0:6] == 'media_' and \
+ n[6:].lower not in media_blocks_dictionary:
+ try:
+ if self.tw.running_sugar:
+ try:
+ dsobject = datastore.get(n[6:])
+ except:
+ debug_output("Couldn't open %s" % (n[6:]),
+ self.tw.running_sugar)
+ self.tw.showlabel('status', dsobject.metadata['title'])
+ dsobject.destroy()
+ else:
+ self.tw.showlabel('status', n[6:])
+ except IOError:
+ self.tw.showlabel('status', n)
+ else:
+ self.tw.showlabel('status', n)
+ elif type(n) == int:
+ self.tw.showlabel('status', n)
+ else:
+ self.tw.showlabel('status',
+ str(round_int(n)).replace('.', self.tw.decimal_point))
+
+ def _prim_printheap(self):
+ """ Display contents of heap """
+ heap_as_string = str(self.tw.lc.heap)
+ if len(heap_as_string) > 80:
+ self.tw.showlabel('status', str(self.tw.lc.heap)[0:79] + '…')
+ else:
+ self.tw.showlabel('status', str(self.tw.lc.heap))
+
+ def _prim_push(self, val):
+ """ Push value onto FILO """
+ self.tw.lc.heap.append(val)
+ self.tw.lc.update_label_value('pop', val)
+
+ def _prim_readpixel(self):
+ """ Read r, g, b, a from the canvas and push b, g, r to the stack """
+ r, g, b, a = self.tw.canvas.get_pixel()
+ self.tw.lc.heap.append(b)
+ self.tw.lc.heap.append(g)
+ self.tw.lc.heap.append(r)
+
+ def _prim_reskin(self, media):
+ """ Reskin the turtle with an image from a file """
+ scale = int(ICON_SIZE * float(self.tw.lc.scale) / DEFAULT_SCALE)
+ if scale < 1:
+ return
+ self.tw.lc.filepath = None
+ dsobject = None
+ if os.path.exists(media[6:]): # is it a path?
+ self.tw.lc.filepath = media[6:]
+ elif self.tw.running_sugar: # is it a datastore object?
+ try:
+ dsobject = datastore.get(media[6:])
+ except:
+ debug_output("Couldn't open skin %s" % (media[6:]),
+ self.tw.running_sugar)
+ if dsobject is not None:
+ self.tw.lc.filepath = dsobject.file_path
+ if self.tw.lc.filepath == None:
+ self.tw.showlabel('nojournal', self.tw.lc.filepath)
+ return
+ pixbuf = None
+ try:
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self.tw.lc.filepath,
+ scale, scale)
+ except:
+ self.tw.showlabel('nojournal', self.tw.lc.filepath)
+ debug_output("Couldn't open skin %s" % (self.tw.lc.filepath),
+ self.tw.running_sugar)
+ if pixbuf is not None:
+ self.tw.active_turtle.set_shapes([pixbuf])
+ pen_state = self.tw.active_turtle.get_pen_state()
+ if pen_state:
+ self.tw.canvas.setpen(False)
+ self.tw.canvas.forward(0)
+ if pen_state:
+ self.tw.canvas.setpen(True)
+
+ def _prim_save_picture(self, name):
+ """ Save canvas to file as PNG """
+ self.tw.save_as_image(name)
+
+ def _prim_save_svg(self, name):
+ """ Save SVG to file """
+ self.tw.canvas.svg_close()
+ self.tw.save_as_image(name, True)
+
+ def _prim_see(self):
+ """ Read r, g, b from the canvas and return a corresponding palette
+ color """
+ r, g, b, a = self.tw.canvas.get_pixel()
+ color_index = self.tw.canvas.get_color_index(r, g, b)
+ self.tw.lc.update_label_value('see', color_index)
+ return color_index
+
+ def _prim_setscale(self, scale):
+ """ Set the scale used by the show block """
+ self.tw.lc.scale = scale
+ self.tw.lc.update_label_value('scale', scale)
+
+ def _prim_show(self, string, center=False):
+ """ Show is the general-purpose media-rendering block. """
+ if type(string) == str or type(string) == unicode:
+ if string in ['media_', 'descr_', 'audio_', 'video_',
+ 'media_None', 'descr_None', 'audio_None',
+ 'video_None']:
+ pass
+ elif string[0:6] in ['media_', 'descr_', 'audio_', 'video_']:
+ self.tw.lc.filepath = None
+ self.tw.lc.dsobject = None
+ if string[6:].lower() in media_blocks_dictionary:
+ media_blocks_dictionary[string[6:].lower()]()
+ elif os.path.exists(string[6:]): # is it a path?
+ self.tw.lc.filepath = string[6:]
+ elif self.tw.running_sugar: # is it a datastore object?
+ try:
+ self.tw.lc.dsobject = datastore.get(string[6:])
+ except:
+ debug_output("Couldn't find dsobject %s" % (
+ string[6:]), self.tw.running_sugar)
+ if self.tw.lc.dsobject is not None:
+ self.tw.lc.filepath = self.tw.lc.dsobject.file_path
+ if self.tw.lc.filepath == None:
+ if self.tw.lc.dsobject is not None:
+ self.tw.showlabel('nojournal',
+ self.tw.lc.dsobject.metadata['title'])
+ else:
+ self.tw.showlabel('nojournal', string[6:])
+ debug_output("Couldn't open %s" % (string[6:]),
+ self.tw.running_sugar)
+ elif string[0:6] == 'media_':
+ self.tw.lc.insert_image(center)
+ elif string[0:6] == 'descr_':
+ mimetype = None
+ if self.tw.lc.dsobject is not None and \
+ 'mime_type' in self.tw.lc.dsobject.metadata:
+ mimetype = self.tw.lc.dsobject.metadata['mime_type']
+ description = None
+ if self.tw.lc.dsobject is not None and \
+ 'description' in self.tw.lc.dsobject.metadata:
+ description = self.tw.lc.dsobject.metadata[
+ 'description']
+ self.tw.lc.insert_desc(mimetype, description)
+ elif string[0:6] == 'audio_':
+ self.tw.lc.play_sound()
+ elif string[0:6] == 'video_':
+ self.tw.lc.play_video()
+ if self.tw.lc.dsobject is not None:
+ self.tw.lc.dsobject.destroy()
+ else: # assume it is text to display
+ x, y = self.tw.lc.x2tx(), self.tw.lc.y2ty()
+ if center:
+ y -= self.tw.canvas.textsize
+ self.tw.canvas.draw_text(string, x, y,
+ int(self.tw.canvas.textsize * \
+ self.tw.lc.scale / 100.),
+ self.tw.canvas.width - x)
+ elif type(string) == float or type(string) == int:
+ string = round_int(string)
+ x, y = self.tw.lc.x2tx(), self.tw.lc.y2ty()
+ if center:
+ y -= self.tw.canvas.textsize
+ self.tw.canvas.draw_text(string, x, y,
+ int(self.tw.canvas.textsize * \
+ self.tw.lc.scale / 100.),
+ self.tw.canvas.width - x)
+
+ def _prim_showlist(self, sarray):
+ """ Display list of media objects """
+ x = self.tw.canvas.xcor / self.tw.coord_scale
+ y = self.tw.canvas.ycor / self.tw.coord_scale
+ for s in sarray:
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(s)
+ y -= int(self.tw.canvas.textsize * self.tw.lead)
+
+ def _prim_time(self):
+ """ Number of seconds since program execution has started or
+ clean (prim_clear) block encountered """
+ elapsed_time = int(time() - self.tw.lc.start_time)
+ self.tw.lc.update_label_value('time', elapsed_time)
+ return elapsed_time
+
+ # Deprecated blocks
+
+ def _prim_t1x1(self, title, media):
+ """ title, one image, and description """
+ xo = self.tw.calc_position('t1x1')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 45 * (self.tw.canvas.height - self.title_height * 2) \
+ / self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render media object
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media)
+ if self.tw.running_sugar:
+ x = 0
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media.replace('media_', 'descr_'))
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_t2x1(self, title, media1, media2):
+ """ title, two images (horizontal), two descriptions """
+ xo = self.tw.calc_position('t2x1')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \
+ self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render four quadrents
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1)
+ x = 0
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2)
+ y = -self.title_height
+ if self.tw.running_sugar:
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2.replace('media_', 'descr_'))
+ x = -(self.tw.canvas.width / 2) + xo
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1.replace('media_', 'descr_'))
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_t1x2(self, title, media1, media2):
+ """ title, two images (vertical), two desciptions """
+ xo = self.tw.calc_position('t1x2')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \
+ self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render four quadrents
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1)
+ if self.tw.running_sugar:
+ x = 0
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1.replace('media_', 'descr_'))
+ y = -self.title_height
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2.replace('media_', 'descr_'))
+ x = -(self.tw.canvas.width / 2) + xo
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2)
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_t2x2(self, title, media1, media2, media3, media4):
+ """ title and four images """
+ xo = self.tw.calc_position('t2x2')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \
+ self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render four quadrents
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1)
+ x = 0
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2)
+ y = -self.title_height
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media4)
+ x = -(self.tw.canvas.width / 2) + xo
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media3)
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_t1x1a(self, title, media1):
+ """ title, one media object """
+ xo = self.tw.calc_position('t1x1a')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 90 * (self.tw.canvas.height - self.title_height * 2) / \
+ self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render media object
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1)
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_write(self, string, fsize):
+ """ Write string at size """
+ x = self.tw.canvas.width / 2 + int(self.tw.canvas.xcor)
+ y = self.tw.canvas.height / 2 - int(self.tw.canvas.ycor)
+ self.tw.canvas.draw_text(string, x, y - 15, int(fsize),
+ self.tw.canvas.width)
diff --git a/plugins/turtle_blocks_extras/turtle_blocks_extras.pyc b/plugins/turtle_blocks_extras/turtle_blocks_extras.pyc
new file mode 100644
index 0000000..ffa7609
--- /dev/null
+++ b/plugins/turtle_blocks_extras/turtle_blocks_extras.pyc
Binary files differ
diff --git a/plugins/turtle_blocks_extras/turtle_blocks_extras.py~ b/plugins/turtle_blocks_extras/turtle_blocks_extras.py~
new file mode 100644
index 0000000..5158c97
--- /dev/null
+++ b/plugins/turtle_blocks_extras/turtle_blocks_extras.py~
@@ -0,0 +1,1213 @@
+# -*- coding: utf-8 -*-
+#Copyright (c) 2011, Walter Bender
+#
+# 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
+from time import time
+import os.path
+
+try:
+ from sugar.datastore import datastore
+except ImportError:
+ pass
+
+from plugins.plugin import Plugin
+from TurtleArt.tapalette import make_palette
+from TurtleArt.talogo import primitive_dictionary, logoerror, \
+ media_blocks_dictionary
+from TurtleArt.taconstants import DEFAULT_SCALE, ICON_SIZE, CONSTANTS
+from TurtleArt.tautils import convert, round_int, debug_output
+from TurtleArt.tajail import myfunc, myfunc_import
+
+
+def _num_type(x):
+ """ Is x a number type? """
+ if type(x) == int:
+ return True
+ if type(x) == float:
+ return True
+ if type(x) == ord:
+ return True
+ return False
+
+
+def _string_to_num(x):
+ """ Try to comvert a string to a number """
+ xx = convert(x.replace(self.tw.decimal_point, '.'), float)
+ if type(xx) is float:
+ return xx
+ else:
+ xx, xflag = chr_to_ord(x)
+ if xflag:
+ return xx
+ else:
+ raise logoerror("#syntaxerror")
+
+
+def _millisecond():
+ """ Current time in milliseconds """
+ return time() * 1000
+
+
+class Turtle_blocks(Plugin):
+ """ a class for defining the extra palettes that distinguish Turtle Blocks
+ from Turtle Art """
+
+ def __init__(self, parent):
+ self.tw = parent
+
+ def setup(self):
+ self.heap = self.tw.lc.heap
+ self.keyboard = self.tw.lc.keyboard
+ self.title_height = int((self.tw.canvas.height / 20) * self.tw.scale)
+
+ # set up Turtle Block palettes
+ self._flow_palette()
+
+ self._media_palette()
+
+ self._sensor_palette()
+
+ self._extras_palette()
+
+ self._portfolio_palette()
+
+ # Palette definitions
+
+ def _flow_palette(self):
+ palette = make_palette('flow',
+ colors=["#FFC000", "#A08000"],
+ help_string=_('Palette of flow operators'))
+
+ # macro
+ palette.add_block('while',
+ style='flow-style-boolean',
+ label=_('while'),
+ help_string=_('do-while-True operator that uses \
+boolean operators from Numbers palette'))
+
+ # macro
+ palette.add_block('until',
+ style='flow-style-boolean',
+ label=_('until'),
+ help_string=_('do-until-True operator that uses \
+boolean operators from Numbers palette'))
+
+ def _media_palette(self):
+
+ palette = make_palette('media',
+ colors=["#A0FF00", "#80A000"],
+ help_string=_('Palette of media objects'))
+
+ palette.add_block('journal',
+ style='box-style-media',
+ label=' ',
+ default='None',
+ special_name=_('journal'),
+ help_string=_('Sugar Journal media object'))
+
+ palette.add_block('audio',
+ style='box-style-media',
+ label=' ',
+ special_name=_('audio'),
+ default='None',
+ help_string=_('Sugar Journal audio object'))
+
+ palette.add_block('video',
+ style='box-style-media',
+ label=' ',
+ special_name=_('video'),
+ default='None',
+ help_string=_('Sugar Journal video object'))
+
+ palette.add_block('description',
+ style='box-style-media',
+ label=' ',
+ special_name=_('description'),
+ default='None',
+ help_string=_('Sugar Journal description field'))
+
+ palette.add_block('string',
+ style='box-style',
+ label=_('text'),
+ default=_('text'),
+ special_name='',
+ help_string=_('string value'))
+
+ primitive_dictionary['show'] = self._prim_show
+ palette.add_block('show',
+ style='basic-style-1arg',
+ label=_('show'),
+ default=_('text'),
+ prim_name='show',
+ logo_command='label',
+ help_string=_('draws text or show media from the \
+Journal'))
+ self.tw.lc.def_prim('show', 1,
+ lambda self, x: primitive_dictionary['show'](x, True))
+
+ palette.add_block('showaligned',
+ hidden=True,
+ style='basic-style-1arg',
+ label=_('show aligned'),
+ default=_('text'),
+ prim_name='showaligned',
+ logo_command='label',
+ help_string=_('draws text or show media from the \
+Journal'))
+ self.tw.lc.def_prim('showaligned', 1,
+ lambda self, x: primitive_dictionary['show'](x, False))
+
+ # deprecated
+ primitive_dictionary['write'] = self._prim_write
+ palette.add_block('write',
+ hidden=True,
+ style='basic-style-1arg',
+ label=_('show'),
+ default=[_('text'), 32],
+ prim_name='write',
+ logo_command='label',
+ help_string=_('draws text or show media from the \
+Journal'))
+ self.tw.lc.def_prim('write', 2,
+ lambda self, x, y: primitive_dictionary['write'](x, y))
+
+ primitive_dictionary['setscale'] = self._prim_setscale
+ palette.add_block('setscale',
+ style='basic-style-1arg',
+ label=_('set scale'),
+ prim_name='setscale',
+ default=33,
+ logo_command='setlabelheight',
+ help_string=_('sets the scale of media'))
+ self.tw.lc.def_prim('setscale', 1,
+ lambda self, x: primitive_dictionary['setscale'](x))
+
+ primitive_dictionary['savepix'] = self._prim_save_picture
+ palette.add_block('savepix',
+ style='basic-style-1arg',
+ label=_('save picture'),
+ prim_name='savepix',
+ default=_('picture name'),
+ help_string=_('saves a picture to the Sugar \
+Journal'))
+ self.tw.lc.def_prim('savepix', 1,
+ lambda self, x: primitive_dictionary['savepix'](x))
+
+ primitive_dictionary['savesvg'] = self._prim_save_svg
+ palette.add_block('savesvg',
+ style='basic-style-1arg',
+ label=_('save SVG'),
+ prim_name='savesvg',
+ default=_('picture name'),
+ help_string=_('saves turtle graphics as an SVG file \
+in the Sugar Journal'))
+ self.tw.lc.def_prim('savesvg', 1,
+ lambda self, x: primitive_dictionary['savesvg'](x))
+
+ palette.add_block('scale',
+ style='box-style',
+ label=_('scale'),
+ prim_name='scale',
+ value_block=True,
+ logo_command='labelsize',
+ help_string=_('holds current scale value'))
+ self.tw.lc.def_prim('scale', 0, lambda self: self.tw.lc.scale)
+
+ palette.add_block('mediawait',
+ style='basic-style-extended-vertical',
+ label=_('media wait'),
+ prim_name='mediawait',
+ help_string=_('wait for current video or audio to \
+complete'))
+ self.tw.lc.def_prim('mediawait', 0, self.tw.lc.media_wait, True)
+
+ def _sensor_palette(self):
+
+ palette = make_palette('sensor',
+ colors=["#FF6060", "#A06060"],
+ help_string=_('Palette of sensor blocks'))
+
+ primitive_dictionary['kbinput'] = self._prim_kbinput
+ palette.add_block('kbinput',
+ style='basic-style-extended-vertical',
+ label=_('query keyboard'),
+ prim_name='kbinput',
+ help_string=_('query for keyboard input (results \
+stored in keyboard block)'))
+ self.tw.lc.def_prim('kbinput', 0,
+ lambda self: primitive_dictionary['kbinput']())
+
+ palette.add_block('keyboard',
+ style='box-style',
+ label=_('keyboard'),
+ prim_name='keyboard',
+ value_block=True,
+ logo_command='make "keyboard readchar',
+ help_string=_('holds results of query-keyboard \
+block'))
+ self.tw.lc.def_prim('keyboard', 0, lambda self: self.tw.lc.keyboard)
+
+ primitive_dictionary['readpixel'] = self._prim_readpixel
+ palette.add_block('readpixel',
+ style='basic-style-extended-vertical',
+ label=_('read pixel'),
+ prim_name='readpixel',
+ logo_command=':keyboard',
+ help_string=_('RGB color under the turtle is pushed \
+to the stack'))
+ self.tw.lc.def_prim('readpixel', 0,
+ lambda self: primitive_dictionary['readpixel']())
+
+ primitive_dictionary['see'] = self._prim_see
+ palette.add_block('see',
+ style='box-style',
+ label=_('turtle sees'),
+ prim_name='see',
+ help_string=_('returns the color that the turtle \
+"sees"'))
+ self.tw.lc.def_prim('see', 0,
+ lambda self: primitive_dictionary['see']())
+
+ primitive_dictionary['time'] = self._prim_time
+ palette.add_block('time',
+ style='box-style',
+ label=_('time'),
+ prim_name='time',
+ value_block=True,
+ help_string=_('elapsed time (in seconds) since \
+program started'))
+ self.tw.lc.def_prim('time', 0,
+ lambda self: primitive_dictionary['time']())
+
+ def _extras_palette(self):
+
+ palette = make_palette('extras',
+ colors=["#FF0000", "#A00000"],
+ help_string=_('Palette of extra options'))
+
+ primitive_dictionary['push'] = self._prim_push
+ palette.add_block('push',
+ style='basic-style-1arg',
+ label=_('push'),
+ prim_name='push',
+ logo_command='tapush',
+ help_string=_('pushes value onto FILO (first-in \
+last-out heap)'))
+ self.tw.lc.def_prim('push', 1,
+ lambda self, x: primitive_dictionary['push'](x))
+
+ primitive_dictionary['printheap'] = self._prim_printheap
+ palette.add_block('printheap',
+ style='basic-style-extended-vertical',
+ label=_('show heap'),
+ prim_name='printheap',
+ logo_command='taprintheap',
+ help_string=_('shows values in FILO (first-in \
+last-out heap)'))
+ self.tw.lc.def_prim('printheap', 0,
+ lambda self: primitive_dictionary['printheap']())
+
+ primitive_dictionary['clearheap'] = self._prim_emptyheap
+ palette.add_block('clearheap',
+ style='basic-style-extended-vertical',
+ label=_('empty heap'),
+ prim_name='clearheap',
+ logo_command='taclearheap',
+ help_string=_('emptys FILO (first-in-last-out \
+heap)'))
+ self.tw.lc.def_prim('clearheap', 0,
+ lambda self: primitive_dictionary['clearheap']())
+
+ primitive_dictionary['pop'] = self._prim_pop
+ palette.add_block('pop',
+ style='box-style',
+ label=_('pop'),
+ prim_name='pop',
+ value_block=True,
+ logo_command='tapop',
+ help_string=_('pops value off FILO (first-in \
+last-out heap)'))
+ self.tw.lc.def_prim('pop', 0,
+ lambda self: primitive_dictionary['pop']())
+
+ primitive_dictionary['print'] = self._prim_print
+ palette.add_block('comment',
+ style='basic-style-1arg',
+ label=_('comment'),
+ prim_name='comment',
+ default=_('comment'),
+ help_string=_('places a comment in your code'))
+ self.tw.lc.def_prim('comment', 1,
+ lambda self, x: primitive_dictionary['print'](x, True))
+
+ palette.add_block('print',
+ style='basic-style-1arg',
+ label=_('print'),
+ prim_name='print',
+ logo_command='label',
+ help_string=_('prints value in status block at \
+bottom of the screen'))
+ self.tw.lc.def_prim('print', 1,
+ lambda self, x: primitive_dictionary['print'](x, False))
+
+ primitive_dictionary['myfunction'] = self._prim_myfunction
+ palette.add_block('myfunc1arg',
+ style='number-style-var-arg',
+ label=[_('Python'), 'f(x)', 'x'],
+ prim_name='myfunction',
+ default=['x', 100],
+ help_string=_('a programmable block: used to add \
+advanced single-variable math equations, e.g., sin(x)'))
+ self.tw.lc.def_prim('myfunction', 2,
+ lambda self, f, x: primitive_dictionary['myfunction'](f, [x]))
+
+ palette.add_block('myfunc2arg',
+ hidden=True,
+ style='number-style-var-arg',
+ label=[_('Python'), 'f(x,y)', 'x'],
+ prim_name='myfunction2',
+ default=['x+y', 100, 100],
+ help_string=_('a programmable block: used to add \
+advanced multi-variable math equations, e.g., sqrt(x*x+y*y)'))
+ self.tw.lc.def_prim('myfunction2', 3,
+ lambda self, f, x, y: primitive_dictionary['myfunction'](
+ f, [x, y]))
+
+ palette.add_block('myfunc3arg',
+ hidden=True,
+ style='number-style-var-arg',
+ label=[_('Python'), 'f(x,y,z)', 'x'],
+ prim_name='myfunction3',
+ default=['x+y+z', 100, 100, 100],
+ help_string=_('a programmable block: used to add \
+advanced multi-variable math equations, e.g., sin(x+y+z)'))
+ self.tw.lc.def_prim('myfunction3', 4,
+ lambda self, f, x, y, z: primitive_dictionary['myfunction'](
+ f, [x, y, z]))
+
+ primitive_dictionary['userdefined'] = self._prim_myblock
+ palette.add_block('userdefined',
+ style='basic-style-var-arg',
+ label=' ',
+ prim_name='userdefined',
+ special_name=_('Python block'),
+ default=100,
+ help_string=_('runs code found in the tamyblock.py \
+module found in the Journal'))
+ self.tw.lc.def_prim('userdefined', 1,
+ lambda self, x: primitive_dictionary['userdefined']([x]))
+
+ palette.add_block('userdefined2args',
+ hidden=True,
+ style='basic-style-var-arg',
+ label=' ',
+ prim_name='userdefined2',
+ special_name=_('Python block'),
+ default=[100, 100],
+ help_string=_('runs code found in the tamyblock.py \
+module found in the Journal'))
+ self.tw.lc.def_prim('userdefined2', 2,
+ lambda self, x, y: primitive_dictionary['userdefined']([x, y]))
+
+ palette.add_block('userdefined3args',
+ hidden=True,
+ style='basic-style-var-arg',
+ label=' ',
+ prim_name='userdefined3',
+ special_name=_('Python block'),
+ default=[100, 100, 100],
+ help_string=_('runs code found in the tamyblock.py \
+module found in the Journal'))
+ self.tw.lc.def_prim('userdefined3', 3,
+ lambda self, x, y, z: primitive_dictionary['userdefined'](
+ [x, y, z]))
+
+ palette.add_block('cartesian',
+ style='basic-style-extended-vertical',
+ label=_('Cartesian'),
+ prim_name='cartesian',
+ help_string=_('displays Cartesian coordinates'))
+ self.tw.lc.def_prim('cartesian', 0,
+ lambda self: self.tw.set_cartesian(True))
+
+ palette.add_block('polar',
+ style='basic-style-extended-vertical',
+ label=_('polar'),
+ prim_name='polar',
+ help_string=_('displays polar coordinates'))
+ self.tw.lc.def_prim('polar', 0,
+ lambda self: self.tw.set_polar(True))
+
+ palette.add_block('addturtle',
+ style='basic-style-1arg',
+ label=_('turtle'),
+ prim_name='turtle',
+ default=1,
+ help_string=_('chooses which turtle to command'))
+ self.tw.lc.def_prim('turtle', 1,
+ lambda self, x: self.tw.canvas.set_turtle(x))
+
+ primitive_dictionary['skin'] = self._prim_reskin
+ palette.add_block('skin',
+ hidden=True,
+ style='basic-style-1arg',
+ label=_('turtle shell'),
+ prim_name='skin',
+ help_string=_("put a custom 'shell' on the turtle"))
+ self.tw.lc.def_prim('skin', 1,
+ lambda self, x: primitive_dictionary['skin'](x))
+
+ # macro
+ palette.add_block('reskin',
+ style='basic-style-1arg',
+ label=_('turtle shell'),
+ help_string=_("put a custom 'shell' on the turtle"))
+
+ palette.add_block('sandwichtop_no_label',
+ style='collapsible-top-no-label',
+ label=[' ', ' '],
+ special_name=_('top'),
+ prim_name='nop',
+ help_string=_('top of a collapsed stack'))
+
+ palette.add_block('sandwichbottom',
+ style='collapsible-bottom',
+ label=[' ', ' '],
+ prim_name='nop',
+ special_name=_('bottom'),
+ help_string=_('bottom of a collapsible stack'))
+
+ palette.add_block('sandwichcollapsed',
+ hidden=True,
+ style='invisible',
+ label=' ',
+ prim_name='nop',
+ help_string=_('bottom block in a collapsed stack: \
+click to open'))
+
+ # deprecated blocks
+ palette.add_block('sandwichtop',
+ hidden=True,
+ style='collapsible-top',
+ label=_('top of stack'),
+ default=_('label'),
+ prim_name='comment',
+ help_string=_('top of stack'))
+
+ palette.add_block('sandwichtop_no_arm',
+ hidden=True,
+ style='collapsible-top-no-arm',
+ label=_('top of a collapsible stack'),
+ default=_('label'),
+ prim_name='comment',
+ help_string=_('top of stack'))
+
+ palette.add_block('sandwichtop_no_arm_no_label',
+ hidden=True,
+ style='collapsible-top-no-arm-no-label',
+ label=[' ', _('click to open')],
+ prim_name='nop',
+ help_string=_('top of stack'))
+
+ def _portfolio_palette(self):
+
+ palette = make_palette('portfolio',
+ colors=["#0606FF", "#0606A0"],
+ help_string=_('Palette of presentation templates'))
+
+ palette.add_block('hideblocks',
+ style='basic-style-extended-vertical',
+ label=_('hide blocks'),
+ prim_name='hideblocks',
+ help_string=_('declutters canvas by hiding blocks'))
+ self.tw.lc.def_prim('hideblocks', 0, lambda self: self.tw.hideblocks())
+
+ palette.add_block('showblocks',
+ style='basic-style-extended-vertical',
+ label=_('show blocks'),
+ prim_name='showblocks',
+ help_string=_('restores hidden blocks'))
+ self.tw.lc.def_prim('showblocks', 0, lambda self: self.tw.showblocks())
+
+ palette.add_block('fullscreen',
+ style='basic-style-extended-vertical',
+ label=_('full screen'),
+ prim_name='fullscreen',
+ help_string=_('hides the Sugar toolbars'))
+ self.tw.lc.def_prim('fullscreen', 0,
+ lambda self: self.tw.set_fullscreen())
+
+ primitive_dictionary['bulletlist'] = self._prim_list
+ palette.add_block('list',
+ hidden=True,
+ style='bullet-style',
+ label=_('list'),
+ prim_name='bulletlist',
+ default=['∙ ', '∙ '],
+ help_string=_('presentation bulleted list'))
+ self.tw.lc.def_prim('bulletlist', 1,
+ primitive_dictionary['bulletlist'], True)
+
+ # macros
+ palette.add_block('picturelist',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: list of \
+bullets'))
+
+ palette.add_block('picture1x1a',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select \
+Journal object (no description)'))
+
+ palette.add_block('picture1x1',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select \
+Journal object (with description)'))
+
+ palette.add_block('picture2x2',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select four \
+Journal objects'))
+
+ palette.add_block('picture2x1',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select two \
+Journal objects'))
+
+ palette.add_block('picture1x2',
+ style='basic-style-extended',
+ label=' ',
+ help_string=_('presentation template: select two \
+Journal objects'))
+
+ # Display-dependent constants
+ palette.add_block('leftpos',
+ style='box-style',
+ label=_('left'),
+ prim_name='lpos',
+ logo_command='lpos',
+ help_string=_('xcor of left of screen'))
+ self.tw.lc.def_prim('lpos', 0, lambda self: CONSTANTS['leftpos'])
+
+ palette.add_block('bottompos',
+ style='box-style',
+ label=_('bottom'),
+ prim_name='bpos',
+ logo_command='bpos',
+ help_string=_('ycor of bottom of screen'))
+ self.tw.lc.def_prim('bpos', 0, lambda self: CONSTANTS['bottompos'])
+
+ palette.add_block('width',
+ style='box-style',
+ label=_('width'),
+ prim_name='hres',
+ logo_command='width',
+ help_string=_('the canvas width'))
+ self.tw.lc.def_prim('hres', 0, lambda self: CONSTANTS['width'])
+
+ palette.add_block('rightpos',
+ style='box-style',
+ label=_('right'),
+ prim_name='rpos',
+ logo_command='rpos',
+ help_string=_('xcor of right of screen'))
+ self.tw.lc.def_prim('rpos', 0, lambda self: CONSTANTS['rightpos'])
+
+ palette.add_block('toppos',
+ style='box-style',
+ label=_('top'),
+ prim_name='tpos',
+ logo_command='tpos',
+ help_string=_('ycor of top of screen'))
+ self.tw.lc.def_prim('tpos', 0, lambda self: CONSTANTS['toppos'])
+
+ palette.add_block('height',
+ style='box-style',
+ label=_('height'),
+ prim_name='vres',
+ logo_command='height',
+ help_string=_('the canvas height'))
+ self.tw.lc.def_prim('vres', 0, lambda self: CONSTANTS['height'])
+
+ palette.add_block('titlex',
+ hidden=True,
+ style='box-style',
+ label=_('title x'),
+ logo_command='titlex',
+ prim_name='titlex')
+ self.tw.lc.def_prim('titlex', 0, lambda self: CONSTANTS['titlex'])
+
+ palette.add_block('titley',
+ hidden=True,
+ style='box-style',
+ label=_('title y'),
+ logo_command='titley',
+ prim_name='titley')
+ self.tw.lc.def_prim('titley', 0, lambda self: CONSTANTS['titley'])
+
+ palette.add_block('leftx',
+ hidden=True,
+ style='box-style',
+ label=_('left x'),
+ prim_name='leftx',
+ logo_command='leftx')
+ self.tw.lc.def_prim('leftx', 0, lambda self: CONSTANTS['leftx'])
+
+ palette.add_block('topy',
+ hidden=True,
+ style='box-style',
+ label=_('top y'),
+ prim_name='topy',
+ logo_command='topy')
+ self.tw.lc.def_prim('topy', 0, lambda self: CONSTANTS['topy'])
+
+ palette.add_block('rightx',
+ hidden=True,
+ style='box-style',
+ label=_('right x'),
+ prim_name='rightx',
+ logo_command='rightx')
+ self.tw.lc.def_prim('rightx', 0, lambda self: CONSTANTS['rightx'])
+
+ palette.add_block('bottomy',
+ hidden=True,
+ style='box-style',
+ label=_('bottom y'),
+ prim_name='bottomy',
+ logo_command='bottomy')
+ self.tw.lc.def_prim('bottomy', 0, lambda self: CONSTANTS['bottomy'])
+
+ # deprecated blocks
+
+ primitive_dictionary['t1x1'] = self._prim_t1x1
+ palette.add_block('template1x1',
+ hidden=True,
+ style='portfolio-style-1x1',
+ label=' ',
+ prim_name='t1x1',
+ default=[_('Title'), 'None'],
+ special_name=_('presentation 1x1'),
+ help_string=_('presentation template: select \
+Journal object (with description)'))
+ self.tw.lc.def_prim('t1x1', 2,
+ lambda self, a, b: primitive_dictionary['t1x1'](a, b))
+
+ primitive_dictionary['t1x1a'] = self._prim_t1x1a
+ palette.add_block('template1x1a',
+ hidden=True,
+ style='portfolio-style-1x1',
+ label=' ',
+ prim_name='t1x1a',
+ default=[_('Title'), 'None'],
+ special_name=_('presentation 1x1'),
+ help_string=_('presentation template: select \
+Journal object (no description)'))
+ self.tw.lc.def_prim('t1x1a', 2,
+ lambda self, a, b: primitive_dictionary['t1x1a'](a, b))
+
+ primitive_dictionary['2x1'] = self._prim_t2x1
+ palette.add_block('template2x1',
+ hidden=True,
+ style='portfolio-style-2x1',
+ label=' ',
+ prim_name='t2x1',
+ default=[_('Title'), 'None', 'None'],
+ special_name=_('presentation 2x1'),
+ help_string=_("presentation template: select two \
+Journal objects"))
+ self.tw.lc.def_prim('t2x1', 3,
+ lambda self, a, b, c: primitive_dictionary['t2x1'](a, b, c))
+
+ primitive_dictionary['1x2'] = self._prim_t1x2
+ palette.add_block('template1x2',
+ hidden=True,
+ style='portfolio-style-1x2',
+ label=' ',
+ prim_name='t1x2',
+ default=[_('Title'), 'None', 'None'],
+ special_name=_('presentation 1x2'),
+ help_string=_("presentation template: select two \
+Journal objects"))
+ self.tw.lc.def_prim('t1x2', 3,
+ lambda self, a, b, c: primitive_dictionary['t1x2'](a, b, c))
+
+ primitive_dictionary['t2x2'] = self._prim_t2x2
+ palette.add_block('template2x2',
+ hidden=True,
+ style='portfolio-style-2x2',
+ label=' ',
+ prim_name='t2x2',
+ default=[_('Title'), 'None', 'None', 'None', 'None'],
+ special_name=_('presentation 2x2'),
+ help_string=_("presentation template: select four \
+Journal objects"))
+ self.tw.lc.def_prim('t2x2', 5,
+ lambda self, a, b, c, d, e: primitive_dictionary['t2x2'](
+ a, b, c, d, e))
+
+ palette.add_block('templatelist',
+ hidden=True,
+ style='bullet-style',
+ label=' ',
+ prim_name='bullet',
+ default=[_('Title'), '∙ '],
+ special_name=_('presentation bulleted list'),
+ help_string=_('presentation template: list of \
+bullets'))
+ self.tw.lc.def_prim('bullet', 1, self._prim_list, True)
+
+ # Block primitives
+
+ def _prim_emptyheap(self):
+ """ Empty FILO """
+ self.tw.lc.heap = []
+
+ def _prim_kbinput(self):
+ """ Query keyboard """
+ if len(self.tw.keypress) == 1:
+ self.tw.lc.keyboard = ord(self.tw.keypress[0])
+ else:
+ try:
+ self.tw.lc.keyboard = {'Escape': 27, 'space': 32, ' ': 32,
+ 'Return': 13, 'KP_Up': 2, 'KP_Down': 4, 'KP_Left': 1,
+ 'KP_Right': 3}[self.tw.keypress]
+ except KeyError:
+ self.tw.lc.keyboard = 0
+ self.tw.lc.update_label_value('keyboard', self.tw.lc.keyboard)
+ self.tw.keypress = ''
+
+ def _prim_list(self, blklist):
+ """ Expandable list block """
+ self._prim_showlist(blklist)
+ self.tw.lc.ireturn()
+ yield True
+
+ def _prim_myblock(self, x):
+ """ Run Python code imported from Journal """
+ if self.tw.lc.bindex is not None and \
+ self.tw.lc.bindex in self.tw.myblock:
+ try:
+ if len(x) == 1:
+ myfunc_import(self, self.tw.myblock[self.tw.lc.bindex],
+ x[0])
+ else:
+ myfunc_import(self, self.tw.myblock[self.tw.lc.bindex], x)
+ except:
+ raise logoerror("#syntaxerror")
+
+ def _prim_myfunction(self, f, x):
+ """ Programmable block """
+ try:
+ y = myfunc(f, x)
+ if str(y) == 'nan':
+ debug_output('Python function returned NAN',
+ self.tw.running_sugar)
+ self.tw.lc.stop_logo()
+ raise logoerror("#notanumber")
+ else:
+ return y
+ except ZeroDivisionError:
+ self.tw.lc.stop_logo()
+ raise logoerror("#zerodivide")
+ except ValueError, e:
+ self.tw.lc.stop_logo()
+ raise logoerror('#' + str(e))
+ except SyntaxError, e:
+ self.tw.lc.stop_logo()
+ raise logoerror('#' + str(e))
+ except NameError, e:
+ self.tw.lc.stop_logo()
+ raise logoerror('#' + str(e))
+ except OverflowError:
+ self.tw.lc.stop_logo()
+ raise logoerror("#overflowerror")
+ except TypeError:
+ self.tw.lc.stop_logo()
+ raise logoerror("#notanumber")
+
+ def _prim_pop(self):
+ """ Pop value off of FILO """
+ if len(self.tw.lc.heap) == 0:
+ raise logoerror("#emptyheap")
+ else:
+ if len(self.tw.lc.heap) == 1:
+ self.tw.lc.update_label_value('pop')
+ else:
+ self.tw.lc.update_label_value('pop', self.tw.lc.heap[-2])
+ return self.tw.lc.heap.pop(-1)
+
+ def _prim_print(self, n, flag):
+ """ Print object n """
+ if flag and (self.tw.hide or self.tw.step_time == 0):
+ return
+ if type(n) == str or type(n) == unicode:
+ if n[0:6] == 'media_' and \
+ n[6:].lower not in media_blocks_dictionary:
+ try:
+ if self.tw.running_sugar:
+ try:
+ dsobject = datastore.get(n[6:])
+ except:
+ debug_output("Couldn't open %s" % (n[6:]),
+ self.tw.running_sugar)
+ self.tw.showlabel('status', dsobject.metadata['title'])
+ dsobject.destroy()
+ else:
+ self.tw.showlabel('status', n[6:])
+ except IOError:
+ self.tw.showlabel('status', n)
+ else:
+ self.tw.showlabel('status', n)
+ elif type(n) == int:
+ self.tw.showlabel('status', n)
+ else:
+ self.tw.showlabel('status',
+ str(round_int(n)).replace('.', self.tw.decimal_point))
+
+ def _prim_printheap(self):
+ """ Display contents of heap """
+ heap_as_string = str(self.tw.lc.heap)
+ if len(heap_as_string) > 80:
+ self.tw.showlabel('status', str(self.tw.lc.heap)[0:79] + '…')
+ else:
+ self.tw.showlabel('status', str(self.tw.lc.heap))
+
+ def _prim_push(self, val):
+ """ Push value onto FILO """
+ self.tw.lc.heap.append(val)
+ self.tw.lc.update_label_value('pop', val)
+
+ def _prim_readpixel(self):
+ """ Read r, g, b, a from the canvas and push b, g, r to the stack """
+ r, g, b, a = self.tw.canvas.get_pixel()
+ self.tw.lc.heap.append(b)
+ self.tw.lc.heap.append(g)
+ self.tw.lc.heap.append(r)
+
+ def _prim_reskin(self, media):
+ """ Reskin the turtle with an image from a file """
+ scale = int(ICON_SIZE * float(self.tw.lc.scale) / DEFAULT_SCALE)
+ if scale < 1:
+ return
+ self.tw.lc.filepath = None
+ dsobject = None
+ if os.path.exists(media[6:]): # is it a path?
+ self.tw.lc.filepath = media[6:]
+ elif self.tw.running_sugar: # is it a datastore object?
+ try:
+ dsobject = datastore.get(media[6:])
+ except:
+ debug_output("Couldn't open skin %s" % (media[6:]),
+ self.tw.running_sugar)
+ if dsobject is not None:
+ self.tw.lc.filepath = dsobject.file_path
+ if self.tw.lc.filepath == None:
+ self.tw.showlabel('nojournal', self.tw.lc.filepath)
+ return
+ pixbuf = None
+ try:
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self.tw.lc.filepath,
+ scale, scale)
+ except:
+ self.tw.showlabel('nojournal', self.tw.lc.filepath)
+ debug_output("Couldn't open skin %s" % (self.tw.lc.filepath),
+ self.tw.running_sugar)
+ if pixbuf is not None:
+ self.tw.active_turtle.set_shapes([pixbuf])
+ pen_state = self.tw.active_turtle.get_pen_state()
+ if pen_state:
+ self.tw.canvas.setpen(False)
+ self.tw.canvas.forward(0)
+ if pen_state:
+ self.tw.canvas.setpen(True)
+
+ def _prim_save_picture(self, name):
+ """ Save canvas to file as PNG """
+ self.tw.save_as_image(name)
+
+ def _prim_save_svg(self, name):
+ """ Save SVG to file """
+ self.tw.canvas.svg_close()
+ self.tw.save_as_image(name, True)
+
+ def _prim_see(self):
+ """ Read r, g, b from the canvas and return a corresponding palette
+ color """
+ r, g, b, a = self.tw.canvas.get_pixel()
+ color_index = self.tw.canvas.get_color_index(r, g, b)
+ self.tw.lc.update_label_value('see', color_index)
+ return color_index
+
+ def _prim_setscale(self, scale):
+ """ Set the scale used by the show block """
+ self.tw.lc.scale = scale
+ self.tw.lc.update_label_value('scale', scale)
+
+ def _prim_show(self, string, center=False):
+ """ Show is the general-purpose media-rendering block. """
+ if type(string) == str or type(string) == unicode:
+ if string in ['media_', 'descr_', 'audio_', 'video_',
+ 'media_None', 'descr_None', 'audio_None',
+ 'video_None']:
+ pass
+ elif string[0:6] in ['media_', 'descr_', 'audio_', 'video_']:
+ self.tw.lc.filepath = None
+ self.tw.lc.dsobject = None
+ if string[6:].lower() in media_blocks_dictionary:
+ media_blocks_dictionary[string[6:].lower()]()
+ elif os.path.exists(string[6:]): # is it a path?
+ self.tw.lc.filepath = string[6:]
+ elif self.tw.running_sugar: # is it a datastore object?
+ try:
+ self.tw.lc.dsobject = datastore.get(string[6:])
+ except:
+ debug_output("Couldn't find dsobject %s" % (
+ string[6:]), self.tw.running_sugar)
+ if self.tw.lc.dsobject is not None:
+ self.tw.lc.filepath = self.tw.lc.dsobject.file_path
+ if self.tw.lc.filepath == None:
+ if self.tw.lc.dsobject is not None:
+ self.tw.showlabel('nojournal',
+ self.tw.lc.dsobject.metadata['title'])
+ else:
+ self.tw.showlabel('nojournal', string[6:])
+ debug_output("Couldn't open %s" % (string[6:]),
+ self.tw.running_sugar)
+ elif string[0:6] == 'media_':
+ self.tw.lc.insert_image(center)
+ elif string[0:6] == 'descr_':
+ mimetype = None
+ if self.tw.lc.dsobject is not None and \
+ 'mime_type' in self.tw.lc.dsobject.metadata:
+ mimetype = self.tw.lc.dsobject.metadata['mime_type']
+ description = None
+ if self.tw.lc.dsobject is not None and \
+ 'description' in self.tw.lc.dsobject.metadata:
+ description = self.tw.lc.dsobject.metadata[
+ 'description']
+ self.tw.lc.insert_desc(mimetype, description)
+ elif string[0:6] == 'audio_':
+ self.tw.lc.play_sound()
+ elif string[0:6] == 'video_':
+ self.tw.lc.play_video()
+ if self.tw.lc.dsobject is not None:
+ self.tw.lc.dsobject.destroy()
+ else: # assume it is text to display
+ x, y = self.tw.lc.x2tx(), self.tw.lc.y2ty()
+ if center:
+ y -= self.tw.canvas.textsize
+ self.tw.canvas.draw_text(string, x, y,
+ int(self.tw.canvas.textsize * \
+ self.tw.lc.scale / 100.),
+ self.tw.canvas.width - x)
+ elif type(string) == float or type(string) == int:
+ string = round_int(string)
+ x, y = self.tw.lc.x2tx(), self.tw.lc.y2ty()
+ if center:
+ y -= self.tw.canvas.textsize
+ self.tw.canvas.draw_text(string, x, y,
+ int(self.tw.canvas.textsize * \
+ self.tw.lc.scale / 100.),
+ self.tw.canvas.width - x)
+
+ def _prim_showlist(self, sarray):
+ """ Display list of media objects """
+ x = self.tw.canvas.xcor / self.tw.coord_scale
+ y = self.tw.canvas.ycor / self.tw.coord_scale
+ for s in sarray:
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(s)
+ y -= int(self.tw.canvas.textsize * self.tw.lead)
+
+ def _prim_time(self):
+ """ Number of seconds since program execution has started or
+ clean (prim_clear) block encountered """
+ elapsed_time = int(time() - self.tw.lc.start_time)
+ self.tw.lc.update_label_value('time', elapsed_time)
+ return elapsed_time
+
+ # Deprecated blocks
+
+ def _prim_t1x1(self, title, media):
+ """ title, one image, and description """
+ xo = self.tw.calc_position('t1x1')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 45 * (self.tw.canvas.height - self.title_height * 2) \
+ / self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render media object
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media)
+ if self.tw.running_sugar:
+ x = 0
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media.replace('media_', 'descr_'))
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_t2x1(self, title, media1, media2):
+ """ title, two images (horizontal), two descriptions """
+ xo = self.tw.calc_position('t2x1')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \
+ self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render four quadrents
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1)
+ x = 0
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2)
+ y = -self.title_height
+ if self.tw.running_sugar:
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2.replace('media_', 'descr_'))
+ x = -(self.tw.canvas.width / 2) + xo
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1.replace('media_', 'descr_'))
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_t1x2(self, title, media1, media2):
+ """ title, two images (vertical), two desciptions """
+ xo = self.tw.calc_position('t1x2')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \
+ self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render four quadrents
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1)
+ if self.tw.running_sugar:
+ x = 0
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1.replace('media_', 'descr_'))
+ y = -self.title_height
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2.replace('media_', 'descr_'))
+ x = -(self.tw.canvas.width / 2) + xo
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2)
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_t2x2(self, title, media1, media2, media3, media4):
+ """ title and four images """
+ xo = self.tw.calc_position('t2x2')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 45 * (self.tw.canvas.height - self.title_height * 2) / \
+ self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render four quadrents
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1)
+ x = 0
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media2)
+ y = -self.title_height
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media4)
+ x = -(self.tw.canvas.width / 2) + xo
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media3)
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_t1x1a(self, title, media1):
+ """ title, one media object """
+ xo = self.tw.calc_position('t1x1a')[2]
+ x = -(self.tw.canvas.width / 2) + xo
+ y = self.tw.canvas.height / 2
+ self.tw.canvas.setxy(x, y, pendown=False)
+ # save the text size so we can restore it later
+ save_text_size = self.tw.canvas.textsize
+ # set title text
+ self.tw.canvas.settextsize(self.title_height)
+ self._prim_show(title)
+ # calculate and set scale for media blocks
+ myscale = 90 * (self.tw.canvas.height - self.title_height * 2) / \
+ self.tw.canvas.height
+ self._prim_setscale(myscale)
+ # set body text size
+ self.tw.canvas.settextsize(self.tw.lc.body_height)
+ # render media object
+ # leave some space below the title
+ y -= int(self.title_height * 2 * self.tw.lead)
+ self.tw.canvas.setxy(x, y, pendown=False)
+ self._prim_show(media1)
+ # restore text size
+ self.tw.canvas.settextsize(save_text_size)
+
+ def _prim_write(self, string, fsize):
+ """ Write string at size """
+ x = self.tw.canvas.width / 2 + int(self.tw.canvas.xcor)
+ y = self.tw.canvas.height / 2 - int(self.tw.canvas.ycor)
+ self.tw.canvas.draw_text(string, x, y - 15, int(fsize),
+ self.tw.canvas.width)