mirror of
https://github.com/vidarh/ruby-x11
synced 2024-12-27 09:59:21 +01:00
packet ecoding/decoding now works
This commit is contained in:
parent
336a418a6a
commit
9f62ffbc11
5 changed files with 214 additions and 78 deletions
|
@ -8,5 +8,5 @@ require 'hexdump'
|
|||
require 'X11/protocol'
|
||||
require 'X11/auth'
|
||||
require 'X11/display'
|
||||
require 'X11/encode'
|
||||
require 'X11/type'
|
||||
require 'X11/packet'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
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
79
lib/X11/type.rb
Normal 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
|
Loading…
Reference in a new issue