diff --git a/lib/X11.rb b/lib/X11.rb index 410b13a..950596b 100644 --- a/lib/X11.rb +++ b/lib/X11.rb @@ -3,9 +3,10 @@ module X11 end require 'socket' -require 'X11/protocol' -require 'X11/auth' -require 'X11/display' -require 'X11/screen' -require 'X11/type' -require 'X11/form' +require_relative './X11/protocol' +require_relative './X11/auth' +require_relative './X11/display' +require_relative './X11/screen' +require_relative './X11/type' +require_relative './X11/form' +require_relative './X11/keysyms' diff --git a/lib/X11/display.rb b/lib/X11/display.rb index 82e6e3d..787ba05 100644 --- a/lib/X11/display.rb +++ b/lib/X11/display.rb @@ -27,10 +27,24 @@ module X11 end authorize(host, family, display_id) + @requestseq = 1 @queue = [] + + @extensions = {} + + # Interned atoms + @atoms = {} end + def event_handler= block + @event_handler= block + end + + def display_info + @internal + end + def screens @internal.screens.map do |s| Screen.new(self, s) @@ -66,8 +80,18 @@ module X11 def read_event type, data, event_class case type + when 2 + return Form::KeyPress.from_packet(StringIO.new(data)) + when 3 + return Form::KeyRelease.from_packet(StringIO.new(data)) + when 4 + return Form::ButtonPress.from_packet(StringIO.new(data)) + when 6 + return Form::MotionNotify.from_packet(StringIO.new(data)) when 12 return Form::Expose.from_packet(StringIO.new(data)) + when 14 + return Form::NoExposure.from_packet(StringIO.new(data)) when 19 return Form::MapNotify.from_packet(StringIO.new(data)) when 22 @@ -108,9 +132,14 @@ module X11 end end - def write_request data - p data - data = data.to_packet if data.respond_to?(:to_packet) + def write_request ob + #p data + #p [:write_request, @requestseq, ob.class] + data = ob.to_packet if ob.respond_to?(:to_packet) + #p [:AddGlyph,data] if ob.is_a?(X11::Form::XRenderAddGlyphs) + #p [ob.request_length.to_i*4, data.size] + raise "BAD LENGTH for #{ob.inspect} (#{ob.request_length.to_i*4} ! #{data.size} " if ob.request_length && ob.request_length.to_i*4 != data.size + @requestseq += 1 @socket.write(data) end @@ -121,12 +150,17 @@ module X11 reply ? reply.from_packet(StringIO.new(pkt)) : pkt end + def peek_packet + !@queue.empty? + end + def next_packet @queue.shift || read_packet end def next_reply - while pkt = next_packet + # FIXME: This is totally broken + while pkt = read_packet if pkt.is_a?(String) return pkt else @@ -142,14 +176,243 @@ module X11 yield(pkt) end end - + + # Requests + def create_window(*args) + write_request(X11::Form::CreateWindow.new(*args)) + end + + def atom(name) + intern_atom(false, name) if !@atoms[name] + @atoms[name] + end + + def query_extension(name) + r = write_sync(X11::Form::QueryExtension.new(name), X11::Form::QueryExtensionReply) + @extensions[name] = { + major: r.major_opcode + } + r + end + + def major_opcode(name) + if !@extensions[name] + query_extension(name) + end + raise "No such extension '#{name}'" if !@extensions[name] + @extensions[name][:major] + end + + def intern_atom(flag, name) + reply = write_sync(X11::Form::InternAtom.new(flag, name.to_s), + X11::Form::InternAtomReply) + if reply + @atoms[name.to_sym] = reply.atom + end + end + + def get_keyboard_mapping(min_keycode=display_info.min_keycode, count= display_info.max_keycode - min_keycode) + write_sync(X11::Form::GetKeyboardMapping.new(min_keycode, count), X11::Form::GetKeyboardMappingReply) + end + + def create_colormap(alloc, window, visual) + mid = new_id + write_request(X11::Form::CreateColormap.new(alloc, mid, window, visual)) + mid + end + + def change_property(*args) + write_request(X11::Form::ChangeProperty.new(*args)) + end + + def list_fonts(*args) + write_sync(X11::Form::ListFonts.new(*args), + X11::Form::ListFontsReply) + end + + def open_font(*args) + write_request(X11::Form::OpenFont.new(*args)) + end + + def change_gc(*args) + write_request(X11::Form::ChangeGC.new(*args)) + end + + def map_window(*args) + write_request(X11::Form::MapWindow.new(*args)) + end + + + def create_gc(window, foreground: nil, background: nil) + mask = 0 + args = [] + + # FIXME: + # The rest can be found here: + # https://tronche.com/gui/x/xlib/GC/manipulating.html#XGCValues + if foreground + mask |= 0x04 + args << foreground + end + if background + mask |= 0x08 + args << background + end + + + gc = new_id + write_request(X11::Form::CreateGC.new(gc, window, mask, args)) + gc + end + + def put_image(*args) + write_request(X11::Form::PutImage.new(*args)) + end + + def clear_area(*args) + write_request(X11::Form::ClearArea.new(*args)) + end + + def copy_area(*args) + write_request(X11::Form::CopyArea.new(*args)) + end + + def image_text8(*args) + write_request(X11::Form::ImageText8.new(*args)) + end + + def image_text16(*args) + write_request(X11::Form::ImageText16.new(*args)) + end + + def poly_fill_rectangle(*args) + write_request(X11::Form::PolyFillRectangle.new(*args)) + end + + def create_pixmap(depth, drawable, w,h) + pid = new_id + write_request(X11::Form::CreatePixmap.new(depth, pid, drawable, w,h)) + pid + end + + # XRender + + def render_opcode + return @render_opcode if @render_opcode + @render_opcode = major_opcode("RENDER") + if @render_opcode + @render_version = write_sync(X11::Form::XRenderQueryVersion.new( + @render_opcode,0,11), + X11::Form::XRenderQueryVersionReply + ) + end + @render_opcode + end + + def render_create_picture(drawable, format, vmask=0, vlist=[]) + pid = new_id + write_request(X11::Form::XRenderCreatePicture.new( + render_opcode, pid, drawable, format, vmask, vlist)) + pid + end + + def render_query_pict_formats + @render_formats ||= write_sync( + X11::Form::XRenderQueryPictFormats.new(render_opcode), + X11::Form::XRenderQueryPictFormatsReply + ) + end + + def render_find_visual_format(visual) + # FIXME. + render_query_pict_formats.screens.map do |s| + s.depths.map do |d| + d.visuals.map {|v| v.visual == visual ? v : nil } + end + end.flatten.compact.first.format + end + + def render_find_standard_format(sym) + # A pox be on the people who made this necessary + + formats = render_query_pict_formats + + case sym + when :a8 + @a8 ||= formats.formats.find do |f| + f.type == 1 && + f.depth == 8 && + f.direct.alpha_mask == 255 + end + when :rgb24 + @rgb24 ||= formats.formats.find do |f| + f.type == 1 && + f.depth == 24 && + f.direct.red == 16 && + f.direct.green == 8 && + f.direct.blue == 0 + end + when :argb24 + @argb24 ||= formats.formats.find do |f| + f.type == 1 && + f.depth == 32 && + f.direct.alpha == 24 && + f.direct.red == 16 && + f.direct.green == 8 && + f.direct.blue == 0 + end + else + raise "Unsupported format (a4/a1 by omission)" + end + end + + def render_create_glyph_set(format) + glyphset = new_id + write_request(X11::Form::XRenderCreateGlyphSet.new( + major_opcode("RENDER"),glyphset, format)) + glyphset + end + + def render_add_glyphs(glyphset, glyphids, glyphinfos, data) + write_request(X11::Form::XRenderAddGlyphs.new(render_opcode, + glyphset, Array(glyphids), Array(glyphinfos), data)) + end + + def render_fill_rectangles(op, dst, color, rects) + color = Form::XRenderColor.new(*color) if color.is_a?(Array) + rects = rects.map{|r| r.is_a?(Array) ? Form::Rectangle.new(*r) : r} + write_request(Form::XRenderFillRectangles.new(render_opcode, op, dst, color, rects)) + end + + def render_composite_glyphs32(op, src, dst, fmt, glyphset, srcx,srcy, *elts) + write_request(X11::Form::XRenderCompositeGlyphs32.new( + render_opcode, + op, src, dst, fmt, + glyphset, + srcx, srcy, + elts.map {|e| e.is_a?(Array) ? Form::GlyphElt32.new(*e) : e } + )) + end + + def render_create_solid_fill(*color) + if color.length == 1 && color.is_a?(Form::XRenderColor) + color = color[0] + else + color = Form::XRenderColor.new(*color) + end + fill = new_id + write_request(Form::XRenderCreateSolidFill.new(render_opcode,fill,color)) + fill + end + private def authorize(host, family, display_id) auth = Auth.new auth_info = auth.get_by_hostname(host||"localhost", family, display_id) - auth_name, auth_data = auth_info.address, auth_info.auth_data + auth_name, auth_data = auth_info.auth_name, auth_info.auth_data + p [auth_name, auth_data] handshake = Form::ClientHandshake.new( Protocol::BYTE_ORDER, diff --git a/lib/X11/form.rb b/lib/X11/form.rb index f121be9..b8a753f 100644 --- a/lib/X11/form.rb +++ b/lib/X11/form.rb @@ -21,7 +21,17 @@ module X11 # # Point.from_packet(socket) => # # - class BaseForm + class Form + def self.structs + [] + end + + def self.fields + [] + end + end + + class BaseForm < Form include X11::Type # initialize field accessors @@ -37,7 +47,7 @@ module X11 def to_packet # fetch class level instance variable holding defined fields - structs = self.class.instance_variable_get("@structs") + structs = self.class.structs packet = structs.map do |s| # fetch value of field set in initialization @@ -52,22 +62,34 @@ module X11 value = s.value end end + #p [s,value] if value.is_a?(BaseForm) - value.to_packet + v = value.to_packet + elsif value.is_a?(Symbol) + #if !@atoms[value] + # reply = write_sync(X11::Forms::InternAtom.new(false, value.to_s), X11::Forms::InternAtomReply) + # @ + #end + #value = @atoms[value] + raise "FIXME" else - s.type_klass.pack(value) + #p [s,value] + v = s.type_klass.pack(value) end + #p v + v when :unused sz = s.size.respond_to?(:call) ? s.size.call(self) : s.size "\x00" * sz when :length + #p [s,value] + #p [value.size] s.type_klass.pack(value.size) when :string s.type_klass.pack(value) when :list value.collect do |obj| - p obj if obj.is_a?(BaseForm) obj.to_packet else @@ -79,6 +101,10 @@ module X11 end class << self + def structs + superclass.structs + Array(@structs) #instance_variable_get("@structs")) + end + # FIXME: Doing small reads from socket is a bad idea, and # the protocol provides length fields that makes it unnecessary. def from_packet(socket) @@ -87,7 +113,7 @@ module X11 form = new lengths = {} - @structs.each do |s| + structs.each do |s| case s.type when :field val = if s.type_klass.superclass == BaseForm @@ -106,8 +132,16 @@ module X11 val = s.type_klass.unpack(socket, lengths[s.name]) form.instance_variable_set("@#{s.name}", val) when :list - val = lengths[s.name].times.collect do - s.type_klass.from_packet(socket) + len = lengths[s.name] + if len + val = len.times.collect do + s.type_klass.from_packet(socket) + end + else + val = [] + while ob = s.type_klass.from_packet(socket) + val << ob + end end form.instance_variable_set("@#{s.name}", val) end @@ -116,11 +150,19 @@ module X11 return form end + Field = Struct.new(:name, :type, :type_klass, :value, :size, keyword_init: true) + def field(name, type_klass, type = nil, value: nil) # name, type_klass, type = args - class_eval { attr_accessor name } + class_eval do + if value && value.respond_to?(:call) + define_method(name.to_sym) { value.call(self) } + else + attr_accessor name + end + end - s = OpenStruct.new + s = Field.new s.name = name s.type = (type == nil ? :field : type) s.type_klass = type_klass @@ -131,20 +173,18 @@ module X11 end def unused(size) - s = OpenStruct.new - s.size = size - s.type = :unused - @structs ||= [] - @structs << s + @structs << Field.new(size: size, type: :unused) end def fields - @structs.dup.delete_if{|s| s.type == :unused or s.type == :length} + super+Array(@structs).dup.delete_if{|s| s.type == :unused or s.type == :length } end end end + CardinalAtom=6 + ## ## X11 Packet Defintions ## @@ -245,7 +285,47 @@ module X11 unused 21 end + # XRender structures + class DirectFormat < BaseForm + field :red, Uint16 + field :red_mask, Uint16 + field :green, Uint16 + field :green_mask, Uint16 + field :blue, Uint16 + field :blue_mask, Uint16 + field :alpha, Uint16 + field :alpha_mask, Uint16 + end + + class PictVisual < BaseForm + field :visual, Uint32 + field :format, Uint32 + end + + class PictDepth < BaseForm + field :depth, Uint8 + unused 1 + field :visuals, Uint16, :length + unused 4 + field :visuals, PictVisual, :list + end + + class PictScreen < BaseForm + field :depths, Uint32, :length + field :fallback, Uint32 + field :depths, PictDepth, :list + end + + class PictFormInfo < BaseForm + field :id, Uint32 + field :type, Uint8 + field :depth, Uint8 + unused 2 + field :direct, DirectFormat + field :colormap, Colormap + end + # Requests CopyFromParent = 0 @@ -253,10 +333,13 @@ module X11 InputOnly = 2 CWBackPixel = 0x0002 + CWBorderPixel = 0x0008 CWEventMask = 0x0800 + CWColorMap = 0x2000 KeyPressMask = 0x00001 ButtonPressMask = 0x00004 + PointerMotionMask = 0x00040 ExposureMask = 0x08000 StructureNotifyMask = 0x20000 SubstructureNotifyMask = 0x80000 @@ -264,7 +347,7 @@ module X11 class CreateWindow < BaseForm field :opcode, Uint8, value: 1 field :depth, Uint8 - field :request_length, Uint16, value: ->(cw) { len = 8 + cw.value_list.length; p len; len } + field :request_length, Uint16, value: ->(cw) { len = 8 + cw.value_list.length } field :wid, Window field :parent, Window field :x, Int16 @@ -285,6 +368,53 @@ module X11 field :window, Window end + class InternAtom < BaseForm + field :opcode, Uint8, value: 16 + field :only_if_exists, Bool + field :request_length, Uint16, value: ->(ia) { + 2+(ia.name.length+3)/4 + } + field :name, Uint16, value: ->(ia) { + ia.name.length + } + unused 2 + field :name, String8, :string + end + + class Reply < BaseForm + field :reply, Uint8 + end + + class InternAtomReply < Reply + unused 1 + field :sequence_number, Uint16 + field :reply_length, Uint32 + field :atom, Atom + unused 20 + end + + Replace = 0 + Prepend = 1 + Append = 2 + + class ChangeProperty < BaseForm + field :opcode, Uint8, value: 18 + field :mode, Uint8 + field :request_length, Uint16, value: ->(cp) { + #p [:data, cp.data, :len, cp.data.length, :total, 6+(cp.data.length+3)/4] + 6+(cp.data.length+3)/4 + } + field :window, Window + field :property, Atom + field :type, Atom + field :format, Uint8 + unused 3 + field :data, Uint32, value: ->(cp) { + cp.data.length / 4 + } + field :data, Uint8, :list + end + class OpenFont < BaseForm field :opcode, Uint8, value: 45 unused 1 @@ -310,6 +440,16 @@ module X11 field :pattern, String8 end + class CreatePixmap < BaseForm + field :opcode, Uint8, value: 53 + field :depth, Uint8 + field :request_length, Uint16, value: 4 + field :pid, Pixmap + field :drawable, Uint32 + field :width, Uint16 + field :height, Uint16 + end + class Str < BaseForm field :name, Uint8, :length, value: ->(str) { str.name.length } field :name, String8Unpadded, :string @@ -369,6 +509,21 @@ module X11 field :height, Uint16 end + class CopyArea < BaseForm + field :opcode, Uint8, value: 62 + unused 1 + field :request_length, Uint16, value: 7 + field :src_drawable, Drawable + field :dst_drawable, Drawable + field :gc, Gcontext + field :src_x, Uint16 + field :src_y, Uint16 + field :dst_x, Uint16 + field :dst_y, Uint16 + field :width, Uint16 + field :height, Uint16 + end + class PolyFillRectangle < BaseForm field :opcode, Uint8, value: 70 unused 1 @@ -405,7 +560,7 @@ module X11 class ImageText8 < BaseForm field :opcode, Uint8, value: 76 field :n, Uint8, :length - field :request_length, Uint16, value: ->(it) { 4+(it.n.length+4)/4 } + field :request_length, Uint16, value: ->(it) { 4+(it.n.length+3)/4 } field :drawable, Drawable field :gc, Gcontext field :x, Int16 @@ -413,15 +568,118 @@ module X11 field :n, String8, :string end + class ImageText16 < BaseForm + field :opcode, Uint8, value: 77 + field :n, Uint8, :length + field :request_length, Uint16, value: ->(it) { 4+(it.n.length*2+3)/4 } + field :drawable, Drawable + field :gc, Gcontext + field :x, Int16 + field :y, Int16 + field :n, String16, :string + end + + class CreateColormap < BaseForm + field :opcode, Uint8, value: 78 + field :alloc, Uint8 + field :request_length, Uint16, value: 4 + field :mid, Colormap + field :window, Window + field :visual, Uint32 + end + + class QueryExtension < BaseForm + field :opcode, Uint8, value: 98 + unused 1 + field :request_length, Uint16, value: ->(qe) { 2+(qe.name.length+3)/4 } + field :name, Uint16, :length + unused 2 + field :name, String8 + end + + class QueryExtensionReply < Reply + unused 1 + field :sequence_number, Uint16 + field :reply_length, Uint32 + field :present, Bool + field :major_opcode, Uint8 + field :first_event, Uint8 + field :first_error, Uint8 + unused 20 + end + + class GetKeyboardMapping < BaseForm + field :opcode, Uint8, value: 101 + unused 1 + field :request_length, Uint16, value: 2 + field :first_keycode, Uint8 + field :count, Uint8 + unused 2 + end + + class GetKeyboardMappingReply < Reply + field :keysyms_per_keycode, Uint8 + field :sequence_number, Uint16 + field :reply_length, Uint32 + unused 24 + field :keysyms, Keysym, :list + end + # Events (page ~157) # FIXME: Events have quite a bit of redundancy, but unfortunately # BaseForm can't handle subclassing well. - class Expose < BaseForm + Shift = 0x001 + Lock = 0x002 + Control = 0x004 + Mod1 = 0x008 + Mod2 = 0x010 + Mod3 = 0x0020 + Mod4 = 0x0040 + Mod5 = 0x0080 + Button1 = 0x100 + Button2 = 0x200 + Button3 = 0x400 + Button4 = 0x800 + Button5 = 0x1000 + + class Event < BaseForm field :code, Uint8 - unused 1 + end + + class SimpleEvent < Event + field :detail, Uint8 field :sequence_number, Uint16 - field :widow, Window + end + + class PressEvent < SimpleEvent + field :time, Uint32 + field :root, Window + field :event, Window + field :child, Window + field :root_x, Int16 + field :root_y, Int16 + field :event_x, Int16 + field :event_y, Int16 + field :state, Uint16 + field :same_screen, Bool + unused 1 + end + + class ButtonPress < PressEvent + end + + class KeyPress < PressEvent + end + + class KeyRelease < PressEvent + end + + class MotionNotify < PressEvent + end + + class Expose < SimpleEvent + field :window, Window field :x, Uint16 field :y, Uint16 field :width, Uint16 @@ -430,17 +688,22 @@ module X11 unused 14 end - class MapNotify < BaseForm - field :code, Uint8 + class NoExposure < SimpleEvent # 14 + field :drawable, Drawable + field :minor_opcode, Uint16 + field :major_opcode, Uint8 + unused 21 + end + + class MapNotify < Event unused 1 field :sequence_number, Uint16 field :event, Window field :override_redirect, Bool unused 19 end - - class ConfigureNotify < BaseForm - field :code, Uint8 + + class ConfigureNotify < Event unused 1 field :sequence_number, Uint16 field :event, Window @@ -453,5 +716,150 @@ module X11 field :override_redirect, Bool unused 5 end + + + # XRender extension + # From https://cgit.freedesktop.org/xorg/proto/renderproto/tree/renderproto.h + class XRenderQueryVersion < BaseForm + field :req_type, Uint8 + field :render_req_type, Uint8, value: 0 + field :request_length, Uint16, value: 3 + field :major_version, Uint32 + field :minor_version, Uint32 + end + + class XRenderQueryVersionReply < Reply + unused 1 + field :sequence_number, Uint16 + field :request_length, Uint32 + field :major_version, Uint32 + field :minor_version, Uint32 + #unused 16 + end + + class XRenderQueryPictFormats < BaseForm + field :req_type, Uint8 + field :render_req_type, Uint8, value: 1 + field :request_length, Uint16, value: 1 + end + + class XRenderQueryPictFormatsReply < Reply + unused 1 + field :sequence_number, Uint16 + field :length, Uint32 + field :formats, Uint32, :length + field :screens, Uint32, :length + field :depths, Uint32, :length + field :visuals, Uint32, :length + field :subpixel, Uint32, :length + unused 4 + field :formats, PictFormInfo, :list + field :screens, PictScreen, :list + field :subpixels, Uint32, :list + end + + class XRenderCreatePicture < BaseForm + field :req_type, Uint8 + field :render_req_type, Uint8, value: 4 + field :request_length, Uint16, value: ->(cp) { + 5 + Array(cp.value_list).length + } + field :pid, Uint32 + field :drawable, Uint32 + field :format, Uint32 + field :value_mask, Uint32 + field :value_list, Uint32, :list + end + + class XRenderCreateGlyphSet < BaseForm + field :req_type, Uint8 + field :render_req_type, Uint8, value: 17 + field :request_length, Uint16, value: 3 + field :gsid, Uint32 + field :format, Uint32 + end + + class GlyphInfo < BaseForm + field :width, Uint16 + field :height, Uint16 + field :x, Int16 + field :y, Int16 + field :x_off, Int16 + field :y_off, Int16 + end + + class XRenderAddGlyphs < BaseForm + field :req_type, Uint8 + field :render_req_type, Uint8, value: 20 + field :request_length, Uint16, value: ->(ag) { + if ag.glyphs.length != ag.glyphids.length + raise "Mismatch: Expected XRenderAddGlyphs glyphs and glyphids to be same length" + end + + # GlyphInfo length == 3 + Glyphid length == 1 + 3 + ag.glyphs.length * 4 + (ag.data.length+3)/4 + } + field :glyphset, Uint32 + field :glyphs, Uint32, :length + field :glyphids, Uint32, :list + field :glyphs, GlyphInfo, :list + field :data, String8 + end + + class XRenderColor < BaseForm + field :red, Uint16 + field :green, Uint16 + field :blue, Uint16 + field :alpha, Uint16 + end + + class GlyphElt32 < BaseForm + field :glyphs, Uint8, :length + unused 3 + field :delta_x, Uint16 + field :delta_y, Uint16 + field :glyphs, Uint32, :list + end + + # This is *also* the same as XRenderCOmpositeGlyphs16 and 8 w/other render_req_type, + # but do we care? + class XRenderCompositeGlyphs32 < BaseForm + field :req_type, Uint8 + field :render_req_type, Uint8, value: 25 + field :request_length, Uint16, value: ->(ch) { + 7 + (ch.glyphcmds[0].glyphs.length + 2) # per glyphcmd + } + field :op, Uint8 + unused 3 + field :src, Uint32 + field :dst, Uint32 + field :mask_format, Uint32 + field :glyphset, Uint32 + field :xsrc, Uint16 + field :ysrc, Uint16 + # FIXME: + # We say this is a list, because technically it is + # But currently it'll break with more than one item. + field :glyphcmds, GlyphElt32, :list + end + + class XRenderFillRectangles < BaseForm + field :req_type, Uint8 + field :render_req_type, Uint8, value: 26 + field :request_length, Uint16, value: ->(fr) { 5 + fr.rects.length * 2 } + field :op, Uint8 + unused 3 + field :dst, Uint32 + field :color, Uint32 + field :rects, Rectangle, :list + end + + class XRenderCreateSolidFill < BaseForm + field :req_type, Uint8 + field :render_req_type, Uint8, value: 33 + field :request_length, Uint16, value: 4 + field :fill, Uint32 + field :color, XRenderColor + end end end diff --git a/lib/X11/screen.rb b/lib/X11/screen.rb index c13bbc7..324b738 100644 --- a/lib/X11/screen.rb +++ b/lib/X11/screen.rb @@ -15,6 +15,10 @@ module X11 @internal.root_depth end + def root_visual + @internal.root_visual + end + def width @internal.width_in_pixels end diff --git a/lib/X11/type.rb b/lib/X11/type.rb index b49b66d..d76eb36 100644 --- a/lib/X11/type.rb +++ b/lib/X11/type.rb @@ -20,6 +20,11 @@ module X11 def self.size #{bytesize} end + + def self.from_packet(sock) + r = sock.read(size) + r ? unpack(r) : nil + end end } end @@ -34,10 +39,12 @@ module X11 class String8 def self.pack(x) - x + "\x00"*(-x.length & 3) + x.force_encoding("ASCII-8BIT") + "\x00"*(-x.length & 3) end + + def self.unpack(socket, size) val = socket.read(size) unused_padding = (4 - (size % 4)) % 4 @@ -46,6 +53,20 @@ module X11 end end + class String16 + def self.pack(x) + x.encode("UTF-16BE").force_encoding("ASCII-8BIT") + "\x00\x00"*(-x.length & 1) + end + + def self.unpack(socket, size) + val = socket.read(size) + unused_padding = (4 - (size % 4)) % 4 + socket.read(unused_padding) + val.force_encoding("UTF-16BE") + end + end + + class String8Unpadded def self.pack(x) x @@ -63,7 +84,7 @@ module X11 end def self.unpack(str) - str[0] == "0x01" + str[0] == "\x01" end def self.size @@ -90,6 +111,6 @@ module X11 VisualID = Uint32 Mask = Uint32 Timestamp = Uint32 - + Keysym = Uint32 end end diff --git a/lib/X11/version.rb b/lib/X11/version.rb index a9c4afe..6a57d9d 100644 --- a/lib/X11/version.rb +++ b/lib/X11/version.rb @@ -1,3 +1,3 @@ module X11 - VERSION = "0.0.1" + VERSION = "0.0.2" end