Commits

Rune Halvorsen committed 09047e9

Added dependencies

Comments (0)

Files changed (3)

+## {{{ http://code.activestate.com/recipes/576693/ (r6)
+from UserDict import DictMixin
+
+class OrderedDict(dict, DictMixin):
+
+    def __init__(self, *args, **kwds):
+        if len(args) > 1:
+            raise TypeError('expected at most 1 arguments, got %d' % len(args))
+        try:
+            self.__end
+        except AttributeError:
+            self.clear()
+        self.update(*args, **kwds)
+
+    def clear(self):
+        self.__end = end = []
+        end += [None, end, end]         # sentinel node for doubly linked list
+        self.__map = {}                 # key --> [key, prev, next]
+        dict.clear(self)
+
+    def __setitem__(self, key, value):
+        if key not in self:
+            end = self.__end
+            curr = end[1]
+            curr[2] = end[1] = self.__map[key] = [key, curr, end]
+        dict.__setitem__(self, key, value)
+
+    def __delitem__(self, key):
+        dict.__delitem__(self, key)
+        key, prev, next = self.__map.pop(key)
+        prev[2] = next
+        next[1] = prev
+
+    def __iter__(self):
+        end = self.__end
+        curr = end[2]
+        while curr is not end:
+            yield curr[0]
+            curr = curr[2]
+
+    def __reversed__(self):
+        end = self.__end
+        curr = end[1]
+        while curr is not end:
+            yield curr[0]
+            curr = curr[1]
+
+    def popitem(self, last=True):
+        if not self:
+            raise KeyError('dictionary is empty')
+        if last:
+            key = reversed(self).next()
+        else:
+            key = iter(self).next()
+        value = self.pop(key)
+        return key, value
+
+    def __reduce__(self):
+        items = [[k, self[k]] for k in self]
+        tmp = self.__map, self.__end
+        del self.__map, self.__end
+        inst_dict = vars(self).copy()
+        self.__map, self.__end = tmp
+        if inst_dict:
+            return (self.__class__, (items,), inst_dict)
+        return self.__class__, (items,)
+
+    def keys(self):
+        return list(self)
+
+    setdefault = DictMixin.setdefault
+    update = DictMixin.update
+    pop = DictMixin.pop
+    values = DictMixin.values
+    items = DictMixin.items
+    iterkeys = DictMixin.iterkeys
+    itervalues = DictMixin.itervalues
+    iteritems = DictMixin.iteritems
+
+    def __repr__(self):
+        if not self:
+            return '%s()' % (self.__class__.__name__,)
+        return '%s(%r)' % (self.__class__.__name__, self.items())
+
+    def copy(self):
+        return self.__class__(self)
+
+    @classmethod
+    def fromkeys(cls, iterable, value=None):
+        d = cls()
+        for key in iterable:
+            d[key] = value
+        return d
+
+    def __eq__(self, other):
+        if isinstance(other, OrderedDict):
+            return len(self)==len(other) and self.items() == other.items()
+        return dict.__eq__(self, other)
+
+    def __ne__(self, other):
+        return not self == other
+## end of http://code.activestate.com/recipes/576693/ }}}
+import inspect
+
+
+# todo : implement boolean
+# todo : support nested msgs
+# todo : support enums
+# todo : some tests
+# todo : setters that do validation
+# todo : META for message name and strictnes
+# todo : Parse protodefs
+# todo : Proper exceptions 
+
+
+try:
+    from collections import OrderedDict # works on 2.7
+except ImportError:
+    from ordereddict import OrderedDict # fallback for 2.4-2.6 (From activstate recipies)
+
+# The encoding and decoding stuff is copied from Google's python pb lib.
+# The metaclass stuff copies a lot from django's db.model code
+
+
+# helper functions for encoding and decoding stuff
+
+def encode_varint(value):
+    if value == 0:
+        return "\0"
+    out = ""
+    value = value & 0xffffffffffffffff
+    while value:
+        part = value & 0x7f
+        value >>= 7
+        if value:
+            part |= 0x80
+        out += chr(part)
+    return out
+
+def decode_varint(buffer, pos=0):
+    result = 0
+    shift = 0
+    while True:
+        try:
+            b = ord(buffer[pos])
+        except IndexError:
+            raise DecodeError("Too few bytes when decoding varint")
+        result |= ((b & 0x7f) << shift)
+        pos += 1
+        if not (b & 0x80):
+            # result &= mask # todo: put back masks for size limiting
+            return result, pos
+        shift += 7
+        if shift >= 64:
+          #fixme: wrong exception class
+          raise DecodeError('Too many bytes when decoding varint.')
+
+    return result, pos
+
+def encode_lengthdelim(data):
+    return encode_varint(len(data)) + data
+
+def decode_lengthdelim(data, pos=0):
+    length, pos = decode_varint(data, pos)
+    return data[pos:pos+length], pos+length
+
+# Exception classes
+
+class ProtoBufError(Exception):
+    """Base class for all protobuf errors"""
+    pass
+
+class DecodeError(ProtoBufError):
+    pass
+
+class InvalidTagError(ProtoBufError):
+    """Raised when an invalid tag is used in a protobuf definition.
+    Invalid can be a tag already used or a reserved number"""
+    pass
+
+
+# Protobuf Data type classes
+
+class ProtoBufType(object):
+    """Base class for all protobuf types. uint, string etc all derive
+    from this class."""
+    def __init__(self, tag, type="required", default=None):
+        if tag >= 19000 and tag <= 19999:
+            # thus spake the docs
+            raise InvalidTagError("Tag %s is invalid. Tags must be in the range 19000-19999" % tag)
+
+        self.name = None
+        self.tag = tag
+        self.fieldtype = type
+        self.has_default = False # is there an explicit default
+
+        if default is not None:
+            self.default = default
+            self.has_default=True
+
+    def serialize(self, value):
+        if value == self.default:
+            return ""
+        else:
+            values = [value]
+            if self.fieldtype == "repeated":
+                values = value
+            return "".join([self._serialize_field(e) for e in values])
+
+
+    def parse(self, buf, pos=0):
+        raise NotImplementedError("""No parser for "%s" implemented""" % self.typename)
+
+    def to_proto_def(self):
+        data = dict(self.__dict__)
+        data["typename"] = self.typename.lower()
+        s = "%(fieldtype)s %(typename)s %(name)s = %(tag)s" % data
+        if self.has_default:
+            s += " [default=%s]" % self.default
+        return s
+
+
+class VarInt(ProtoBufType):
+    """Base class for all variable length numbers, that is wire protocol type
+    0. Includes int32, int64, uint32, uint64, sint32, sint64, bool, enum."""
+    default = 0
+
+    def _serialize_field(self, value):
+        return encode_varint((self.tag << 3) | 0) + encode_varint(value)
+
+    def parse(self, buf, pos):
+        return decode_varint(buf, pos)
+
+
+class Int32(VarInt):
+    typename = "Int32"
+
+class Int64(VarInt):
+    typename = "Int64"
+
+class UInt32(VarInt):
+    typename = "UInt32"
+
+class UInt64(VarInt):
+    typename = "UInt64"
+
+class Bool(VarInt):
+    typename = "Bool"
+
+    def _serialize_field(self, value):
+        return VarInt._serialize_field(int(bool(value)))
+
+    def parse(self, buf, pos):
+        return bool(decode_varint(buf, pos))
+
+
+
+class LengthDelimited(ProtoBufType):
+    """Base class for all length delimited types, that is stuff with wire
+    protocol type id 2"""
+    default = ""
+
+    def _serialize_field(self, value):
+        return encode_varint((self.tag << 3) | 2) + encode_lengthdelim(value)
+
+    def parse(self, buf, pos=0):
+        return decode_lengthdelim(buf, pos)
+
+
+class String(LengthDelimited):
+    typename = "string"
+
+class Bytes(LengthDelimited):
+    typename = "bytes"
+
+
+# Metaclass magic stuff
+
+class ProtoBufBase(type):
+    """Metaclass for protobuf messages"""
+    def __new__(cls, name, bases, attrs):
+        super_new = super(ProtoBufBase, cls).__new__
+        parents = [b for b in bases if isinstance(b, ProtoBufBase)]
+        if not parents:
+            # If this isn't a subclass of ProtoBufMessage, don't do anything special.
+            return super_new(cls, name, bases, attrs)
+
+        #print "Making class", name, attrs, bases
+
+        module = attrs.pop('__module__')
+        new_class = super_new(cls, name, bases, {'__module__': module})
+
+        fields = OrderedDict()
+        items = attrs.items()
+        # make sure we sort by tag. Order matters
+        items.sort(lambda a, b: cmp(a[1].tag, b[1].tag))
+
+        for name, obj in items:
+            if isinstance(obj, ProtoBufType):
+                obj.name = name
+                if obj.tag in fields:
+                    raise InvalidTagError("Tag already used in message: %s" % obj.tag)
+                fields[obj.tag] = obj
+                # register a setter/getter pair here?
+            else:
+                # we just let anything that isn't a protobuf type through
+                setattr(new_class, name, obj)
+
+        new_class._fields = fields
+        return new_class
+
+
+class ProtoBufMessage(object):
+    """Baseclass for all protobuf messages. Developers should have all their
+    message definitions derive from this class"""
+    __metaclass__ = ProtoBufBase
+
+    def __init__(self, **kwargs):
+        """Make sure message objects can be instanciated with kwargs for
+        the fields"""
+        own_fields = [e.name for e in self._fields.values()]
+        for key, value in kwargs.items():
+            if key in own_fields:
+                setattr(self, key, value)
+
+    @classmethod
+    def to_proto_def(cls):
+        fields = [e.to_proto_def() for e in cls._fields.values()]
+        s = "message %s {\n    %s\n}" % (cls.__name__, "\n    ".join(fields))
+        return s
+
+    @classmethod
+    def parse(cls, buf, pos=0):
+        # init with defaults?
+        buflen = len(buf)-1
+        obj = cls()
+        while pos < buflen:
+            fieldmeta, pos = decode_varint(buf, pos)
+            wiretype = fieldmeta & 7
+            tag = fieldmeta >> 3
+            handler = cls._fields[tag]
+            val, pos = handler.parse(buf, pos)
+            if handler.fieldtype == "repeated":
+                current = getattr(obj, handler.name, [])
+                current.append(val)
+                setattr(obj, handler.name, current)
+            else:
+                setattr(obj, handler.name, val)
+
+        return obj
+
+    def __str__(self):
+        strings = []
+        for obj in self._fields.values():
+            s = "%s=%s" % (obj.name, getattr(self, obj.name, obj.default))
+            strings.append(s)
+
+        return "<%s: %s>" % (self.__class__.__name__, ", ".join(strings))
+
+
+    def serialize(self):
+        buf = []
+        for obj in self._fields.values():
+            val = getattr(self, obj.name, obj.default)
+            if obj.fieldtype == "required" or val != obj.default:
+                buf.append(obj.serialize(val))
+        return "".join(buf)
+
+    def __eq__(self, other):
+        for obj in self._fields.values():
+            if getattr(self, obj.name, obj.default) != getattr(other, obj.name, obj.default):
+                return False
+        return True
+
+def get_type_class(typename):
+    for obj in globals().values():
+        if (inspect.isclass(obj) and
+            issubclass(obj, ProtoBufType) and
+            not obj is ProtoBufType and
+            obj.typename == typename):
+            return obj
     service/command pair"""
     while True:
         msg = server.pop_message()
+        print msg
         if msg.service == service and msg.commandID == command:
             return msg
 
     fp.close()
     return path
 
+import sys
+url = sys.argv[1]
+
 tags = count(1)
 
 server = STPServer() # by default, listen on port 7001 on all ports
 server.send("scope", 5, 1, tags.next(), serialize(["exec"]))
 server.send("scope", 5, 1, tags.next(), serialize(["window-manager"]))
 
-# Then send a message that opens a URL in a new tab
-# Open link in new page
-server.send("exec", 1, 1, tags.next(), serialize([[["Open url in new page", "http://bash.org/?random"]]]))
+server.send("exec", 2, 1, tags.next(), serialize([]))
+
+
+
+server.send("exec", 1, 1, tags.next(), serialize([[["New page"]]]))
 
 # wait for an event from the window manager that tells us the id of the
 # newly created window
 windowid = payload[0]
 
 
+
+# Then send a message that opens a URL in a new tab
+# Open link in new page
+server.send("exec", 1, 1, tags.next(), serialize([[["Go", url, windowid]]]))
+
+
+
 # wait for an event telling us that the window has finished loading
+cnt = 1
 while True:
+    
     event = wait_for_event(server, "window-manager", 7)
+    print event
     if deserialize(event.payload)[0] == windowid:
-        break
+        cnt+=1
+        print "cnt", cnt
+        #break
+
 
 
 # build the arguments to pass to the setupScreenWatcher msg
 args = [
-    1000, # ms until img should be taken
+    4000, # ms until img should be taken
     [0, 0, 800, 800], # area to screenshot. x, y, w, h
     [],
     windowid
 payload = deserialize(reply.payload)
 path = save_screenshot(payload[2])
 
-print "Saved screenshot as", path
+print "Saved screenshot as", path
+
+
+server.send("exec", 1, 1, tags.next(), serialize([[["Close page", "", windowid]]]))