packet ecoding/decoding now works

This commit is contained in:
Richard Ramsden 2012-05-12 16:46:48 -07:00
parent 336a418a6a
commit 9f62ffbc11
5 changed files with 214 additions and 78 deletions

View file

@ -8,5 +8,5 @@ require 'hexdump'
require 'X11/protocol'
require 'X11/auth'
require 'X11/display'
require 'X11/encode'
require 'X11/type'
require 'X11/packet'

View file

@ -54,6 +54,9 @@ module X11
raise AuthorizationError, "Received unknown opcode #{type}"
end
@socket.read(7)
puts Packet::DisplayInfo.read(@socket).inspect
end
end
end

View file

@ -1,51 +0,0 @@
# This module is used for encoding Ruby Objects to binary
# data. The types Int8, Int16, etc. are data-types defined
# in the X11 protocol. We wrap each data-type in a lambda expression
# which gets evaluated when a packet is created.
#
# EXAMPLE:
# Int8.call(255) => "\xFF"
# String8.call("hello") => "hello"
module X11
module Encode
# List.of(Foo)
# In this document the List.of notation strictly means some number of
# repetitions of the FOO encoding; the actual length of the list is encoded
# elsewhere
class List
class << self
def of(type)
lambda do |data|
# X11 has other List.of(Foo) for different data types right now
# will just throw an error until we've implemented them all.
throw "dont know how to handle this yet"
end
end
end
end
# Takes an object and uses Array#pack to
# convert it into binary data
def self.pack(a)
lambda {|value| [value].pack(a)}
end
# X11 Protocol requires us to pad strings to a multiple of 4 bytes
# For instance, C<pack(padded('Hello'), 'Hello')> gives C<"Hello\0\0\0">.
def self.pad(x); x + "\0"*(-x.length & 3); end
# Primitive Types
Int8 = pack("c")
Int16 = pack("s")
Int32 = pack("l")
Uint8 = pack("C")
Uint16 = pack("S")
Uint32 = pack("L")
# LISTofFOO
String8 = lambda{|x| self.pad(x)} # equivalent to List.of(Uint8) /w padding
end
end

View file

@ -1,40 +1,80 @@
module X11
module Packet
class BasePacket
include X11::Encode
@@struct = []
class BasePacket
include X11::Type
class << self
def create(*values)
lengths = lengths_for(values)
packet = @@struct.map do |tuple|
type, name, encode_fun = tuple
case type
packet = @structs.map do |s|
case s.type
when :field
encode_fun.call(values.shift)
when :length
encode_fun.call(lengths[name])
s.encode.pack(values.shift)
when :unused
name
"\x00" * s.size
when :length
s.encode.pack(lengths[s.name])
when :data
if s.encode == X11::Type::String8
X11::Type::String8.pack(values.shift)
else
vals = s.encode.create(values.shift)
end
end
end
end
packet.join
end
def field(name, encode_fun)
@@struct.push([:field, name, encode_fun])
def read(socket)
lengths = {}
values = {}
@structs.each do |s|
case s.type
when :field
values[s.name] = s.encode.unpack( socket.read(s.encode.size) )
when :unused
socket.read(s.size)
when :length
size = s.encode.unpack( socket.read(s.encode.size) )
lengths[s.name] = size
when :data
if s.encode == X11::Type::String8
values[s.name] = X11::Type::String8.unpack(socket, s.size)
else
puts lengths[s.name]
values[s.name] = lengths[s.name].times.collect do
s.encode.read(socket)
end
end
end
end
values
end
def field(*args)
name, encode, type = args
puts name
s = Struct.new(:name, :encode, :type).new
s.name = name
s.type = (type == nil ? :field : type)
s.encode = encode
@structs ||= []
@structs << s
end
def unused(size)
@@struct.push([:unused, "\x00" * size])
end
s = Struct.new(:size, :type).new
s.size = size
s.type = :unused
def length(name, encode_fun)
@@struct.push([:length, name, encode_fun])
@structs ||= []
@structs << s
end
private
@ -43,17 +83,17 @@ module X11
args = args.dup
lengths = {}
fields.each do |type, name, klass|
fields.each do |s|
value = args.shift
lengths[name] = value.size if value.is_a?(String)
lengths[s.name] = value.size if s.type == :data
end
return lengths
end
def fields
@@struct.dup.delete_if do |type,name,klass|
type == :unused or type == :length
@structs.dup.delete_if do |s|
s.type == :unused or s.type == :length
end
end
@ -81,11 +121,76 @@ module X11
unused 1
field :protocol_major_version, Uint16
field :protocol_minor_version, Uint16
length :auth_proto_name, Uint16
length :auth_proto_data, Uint16
field :auth_proto_name, Uint16, :length
field :auth_proto_data, Uint16, :length
unused 2
field :auth_proto_name, String8
field :auth_proto_data, String8
field :auth_proto_name, String8, :data
field :auth_proto_data, String8, :data
end
class FormatInfo < BasePacket
field :depth, Uint8
field :bits_per_pixel, Uint8
field :scanline_pad, Uint8
unused 5
end
class VisualInfo < BasePacket
field :visual_id, VisualID
field :qlass, Uint8
field :bits_per_rgb_value, Uint8
field :colormap_entries, Uint16
field :red_mask, Uint32
field :green_mask, Uint32
field :blue_mask, Uint32
unused 4
end
class DepthInfo < BasePacket
field :depth, Uint8
unused 1
field :visuals, Uint16, :length
unused 4
field :visuals, VisualInfo, :data
end
class ScreenInfo < BasePacket
field :root, Window
field :default_colormap, Colormap
field :white_pixel, Colornum
field :black_pixel, Colornum
field :current_input_masks, EventMask
field :size_in_pixels, Uint16
field :size_in_millimeters, Uint16
field :min_installed_maps, Uint16
field :max_installed_maps, Uint16
field :root_visual, VisualID
field :backing_stores, Uint8
field :save_unders, Bool
field :root_depth, Uint8
field :depths, Uint8,:length
field :depths, DepthInfo, :data
end
class DisplayInfo < BasePacket
field :release_number, Uint32
field :resource_id_base, Uint32
field :resource_id_mask, Uint32
field :motion_buffer_size, Uint32
field :vendor, Uint16, :length
field :maximum_request_length, Uint16
field :screens, Uint8, :length
field :formats, Uint8, :length
field :image_byte_order, Signifigance
field :bitmap_bit_order, Signifigance
field :bitmap_format_scanline_unit, Uint8
field :bitmap_format_scanline_pad, Uint8
field :min_keycode, KeyCode
field :max_keycode, KeyCode
field :vendor, String8, :data
field :formats, FormatInfo, :data
field :screens, ScreenInfo, :data
end
end
end

79
lib/X11/type.rb Normal file
View file

@ -0,0 +1,79 @@
# This module is used for encoding Ruby Objects to binary
# data. The types Int8, Int16, etc. are data-types defined
# in the X11 protocol. We wrap each data-type in a lambda expression
# which gets evaluated when a packet is created.
module X11
module Type
def self.define(type, directive, bytesize)
eval %{
class X11::Type::#{type}
def self.pack(x)
[x].pack(\"#{directive}\")
end
def self.unpack(x)
x.unpack(\"#{directive}\").first
end
def self.size
#{bytesize}
end
end
}
end
# Primitive Types
define "Int8", "c", 1
define "Int16", "s", 2
define "Int32", "l", 4
define "Uint8", "C", 1
define "Uint16", "S", 2
define "Uint32", "L", 4
KeyCode = Uint8
Signifigance = Uint8
Bool = Uint8
Bitmask = Uint32
Window = Uint32
Pixmap = Uint32
Cursor = Uint32
Colornum = Uint32
Font = Uint32
Gcontext = Uint32
Colormap = Uint32
Drawable = Uint32
Fontable = Uint32
Atom = Uint32
VisualID = Uint32
EventMask = Uint32
# Strings are "Special" in X11 they are a list
# data type but their padded
class String8
def self.pack(x)
x + "\x00"*(-x.length & 3)
end
def self.unpack(socket, size)
val = socket.read(size)
unused_padding = (4 - (size % 4)) % 4
socket.read(unused_padding)
val
end
end
# List.of(Foo)
# In this document the List.of notation strictly means some number of
# repetitions of the FOO encoding; the actual length of the list is encoded
# elsewhere
class List
def self.of(type)
end
end
end
end