mirror of
https://github.com/vidarh/ruby-x11
synced 2024-12-26 09:59:02 +01:00
Added significant number of X requests and event types, and a bunch of convenience functions.
This is still "rough", but it works.
This commit is contained in:
parent
10d3b63c06
commit
3fa4211f06
6 changed files with 739 additions and 42 deletions
13
lib/X11.rb
13
lib/X11.rb
|
@ -3,9 +3,10 @@ module X11
|
||||||
end
|
end
|
||||||
|
|
||||||
require 'socket'
|
require 'socket'
|
||||||
require 'X11/protocol'
|
require_relative './X11/protocol'
|
||||||
require 'X11/auth'
|
require_relative './X11/auth'
|
||||||
require 'X11/display'
|
require_relative './X11/display'
|
||||||
require 'X11/screen'
|
require_relative './X11/screen'
|
||||||
require 'X11/type'
|
require_relative './X11/type'
|
||||||
require 'X11/form'
|
require_relative './X11/form'
|
||||||
|
require_relative './X11/keysyms'
|
||||||
|
|
|
@ -27,10 +27,24 @@ module X11
|
||||||
end
|
end
|
||||||
|
|
||||||
authorize(host, family, display_id)
|
authorize(host, family, display_id)
|
||||||
|
|
||||||
@requestseq = 1
|
@requestseq = 1
|
||||||
@queue = []
|
@queue = []
|
||||||
|
|
||||||
|
@extensions = {}
|
||||||
|
|
||||||
|
# Interned atoms
|
||||||
|
@atoms = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def event_handler= block
|
||||||
|
@event_handler= block
|
||||||
|
end
|
||||||
|
|
||||||
|
def display_info
|
||||||
|
@internal
|
||||||
|
end
|
||||||
|
|
||||||
def screens
|
def screens
|
||||||
@internal.screens.map do |s|
|
@internal.screens.map do |s|
|
||||||
Screen.new(self, s)
|
Screen.new(self, s)
|
||||||
|
@ -66,8 +80,18 @@ module X11
|
||||||
|
|
||||||
def read_event type, data, event_class
|
def read_event type, data, event_class
|
||||||
case type
|
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
|
when 12
|
||||||
return Form::Expose.from_packet(StringIO.new(data))
|
return Form::Expose.from_packet(StringIO.new(data))
|
||||||
|
when 14
|
||||||
|
return Form::NoExposure.from_packet(StringIO.new(data))
|
||||||
when 19
|
when 19
|
||||||
return Form::MapNotify.from_packet(StringIO.new(data))
|
return Form::MapNotify.from_packet(StringIO.new(data))
|
||||||
when 22
|
when 22
|
||||||
|
@ -108,9 +132,14 @@ module X11
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_request data
|
def write_request ob
|
||||||
p data
|
#p data
|
||||||
data = data.to_packet if data.respond_to?(:to_packet)
|
#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)
|
@socket.write(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -121,12 +150,17 @@ module X11
|
||||||
reply ? reply.from_packet(StringIO.new(pkt)) : pkt
|
reply ? reply.from_packet(StringIO.new(pkt)) : pkt
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def peek_packet
|
||||||
|
!@queue.empty?
|
||||||
|
end
|
||||||
|
|
||||||
def next_packet
|
def next_packet
|
||||||
@queue.shift || read_packet
|
@queue.shift || read_packet
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_reply
|
def next_reply
|
||||||
while pkt = next_packet
|
# FIXME: This is totally broken
|
||||||
|
while pkt = read_packet
|
||||||
if pkt.is_a?(String)
|
if pkt.is_a?(String)
|
||||||
return pkt
|
return pkt
|
||||||
else
|
else
|
||||||
|
@ -142,14 +176,243 @@ module X11
|
||||||
yield(pkt)
|
yield(pkt)
|
||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
def authorize(host, family, display_id)
|
def authorize(host, family, display_id)
|
||||||
auth = Auth.new
|
auth = Auth.new
|
||||||
auth_info = auth.get_by_hostname(host||"localhost", family, display_id)
|
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(
|
handshake = Form::ClientHandshake.new(
|
||||||
Protocol::BYTE_ORDER,
|
Protocol::BYTE_ORDER,
|
||||||
|
|
460
lib/X11/form.rb
460
lib/X11/form.rb
|
@ -21,7 +21,17 @@ module X11
|
||||||
#
|
#
|
||||||
# Point.from_packet(socket) => #<Point @x=10 @y=20>
|
# Point.from_packet(socket) => #<Point @x=10 @y=20>
|
||||||
#
|
#
|
||||||
class BaseForm
|
class Form
|
||||||
|
def self.structs
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.fields
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class BaseForm < Form
|
||||||
include X11::Type
|
include X11::Type
|
||||||
|
|
||||||
# initialize field accessors
|
# initialize field accessors
|
||||||
|
@ -37,7 +47,7 @@ module X11
|
||||||
|
|
||||||
def to_packet
|
def to_packet
|
||||||
# fetch class level instance variable holding defined fields
|
# fetch class level instance variable holding defined fields
|
||||||
structs = self.class.instance_variable_get("@structs")
|
structs = self.class.structs
|
||||||
|
|
||||||
packet = structs.map do |s|
|
packet = structs.map do |s|
|
||||||
# fetch value of field set in initialization
|
# fetch value of field set in initialization
|
||||||
|
@ -52,22 +62,34 @@ module X11
|
||||||
value = s.value
|
value = s.value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
#p [s,value]
|
||||||
|
|
||||||
if value.is_a?(BaseForm)
|
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
|
else
|
||||||
s.type_klass.pack(value)
|
#p [s,value]
|
||||||
|
v = s.type_klass.pack(value)
|
||||||
end
|
end
|
||||||
|
#p v
|
||||||
|
v
|
||||||
when :unused
|
when :unused
|
||||||
sz = s.size.respond_to?(:call) ? s.size.call(self) : s.size
|
sz = s.size.respond_to?(:call) ? s.size.call(self) : s.size
|
||||||
"\x00" * sz
|
"\x00" * sz
|
||||||
when :length
|
when :length
|
||||||
|
#p [s,value]
|
||||||
|
#p [value.size]
|
||||||
s.type_klass.pack(value.size)
|
s.type_klass.pack(value.size)
|
||||||
when :string
|
when :string
|
||||||
s.type_klass.pack(value)
|
s.type_klass.pack(value)
|
||||||
when :list
|
when :list
|
||||||
value.collect do |obj|
|
value.collect do |obj|
|
||||||
p obj
|
|
||||||
if obj.is_a?(BaseForm)
|
if obj.is_a?(BaseForm)
|
||||||
obj.to_packet
|
obj.to_packet
|
||||||
else
|
else
|
||||||
|
@ -79,6 +101,10 @@ module X11
|
||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
def structs
|
||||||
|
superclass.structs + Array(@structs) #instance_variable_get("@structs"))
|
||||||
|
end
|
||||||
|
|
||||||
# FIXME: Doing small reads from socket is a bad idea, and
|
# FIXME: Doing small reads from socket is a bad idea, and
|
||||||
# the protocol provides length fields that makes it unnecessary.
|
# the protocol provides length fields that makes it unnecessary.
|
||||||
def from_packet(socket)
|
def from_packet(socket)
|
||||||
|
@ -87,7 +113,7 @@ module X11
|
||||||
form = new
|
form = new
|
||||||
lengths = {}
|
lengths = {}
|
||||||
|
|
||||||
@structs.each do |s|
|
structs.each do |s|
|
||||||
case s.type
|
case s.type
|
||||||
when :field
|
when :field
|
||||||
val = if s.type_klass.superclass == BaseForm
|
val = if s.type_klass.superclass == BaseForm
|
||||||
|
@ -106,8 +132,16 @@ module X11
|
||||||
val = s.type_klass.unpack(socket, lengths[s.name])
|
val = s.type_klass.unpack(socket, lengths[s.name])
|
||||||
form.instance_variable_set("@#{s.name}", val)
|
form.instance_variable_set("@#{s.name}", val)
|
||||||
when :list
|
when :list
|
||||||
val = lengths[s.name].times.collect do
|
len = lengths[s.name]
|
||||||
s.type_klass.from_packet(socket)
|
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
|
end
|
||||||
form.instance_variable_set("@#{s.name}", val)
|
form.instance_variable_set("@#{s.name}", val)
|
||||||
end
|
end
|
||||||
|
@ -116,11 +150,19 @@ module X11
|
||||||
return form
|
return form
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Field = Struct.new(:name, :type, :type_klass, :value, :size, keyword_init: true)
|
||||||
|
|
||||||
def field(name, type_klass, type = nil, value: nil)
|
def field(name, type_klass, type = nil, value: nil)
|
||||||
# name, type_klass, type = args
|
# 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.name = name
|
||||||
s.type = (type == nil ? :field : type)
|
s.type = (type == nil ? :field : type)
|
||||||
s.type_klass = type_klass
|
s.type_klass = type_klass
|
||||||
|
@ -131,20 +173,18 @@ module X11
|
||||||
end
|
end
|
||||||
|
|
||||||
def unused(size)
|
def unused(size)
|
||||||
s = OpenStruct.new
|
|
||||||
s.size = size
|
|
||||||
s.type = :unused
|
|
||||||
|
|
||||||
@structs ||= []
|
@structs ||= []
|
||||||
@structs << s
|
@structs << Field.new(size: size, type: :unused)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fields
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
CardinalAtom=6
|
||||||
|
|
||||||
##
|
##
|
||||||
## X11 Packet Defintions
|
## X11 Packet Defintions
|
||||||
##
|
##
|
||||||
|
@ -245,7 +285,47 @@ module X11
|
||||||
unused 21
|
unused 21
|
||||||
end
|
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
|
# Requests
|
||||||
|
|
||||||
CopyFromParent = 0
|
CopyFromParent = 0
|
||||||
|
@ -253,10 +333,13 @@ module X11
|
||||||
InputOnly = 2
|
InputOnly = 2
|
||||||
|
|
||||||
CWBackPixel = 0x0002
|
CWBackPixel = 0x0002
|
||||||
|
CWBorderPixel = 0x0008
|
||||||
CWEventMask = 0x0800
|
CWEventMask = 0x0800
|
||||||
|
CWColorMap = 0x2000
|
||||||
|
|
||||||
KeyPressMask = 0x00001
|
KeyPressMask = 0x00001
|
||||||
ButtonPressMask = 0x00004
|
ButtonPressMask = 0x00004
|
||||||
|
PointerMotionMask = 0x00040
|
||||||
ExposureMask = 0x08000
|
ExposureMask = 0x08000
|
||||||
StructureNotifyMask = 0x20000
|
StructureNotifyMask = 0x20000
|
||||||
SubstructureNotifyMask = 0x80000
|
SubstructureNotifyMask = 0x80000
|
||||||
|
@ -264,7 +347,7 @@ module X11
|
||||||
class CreateWindow < BaseForm
|
class CreateWindow < BaseForm
|
||||||
field :opcode, Uint8, value: 1
|
field :opcode, Uint8, value: 1
|
||||||
field :depth, Uint8
|
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 :wid, Window
|
||||||
field :parent, Window
|
field :parent, Window
|
||||||
field :x, Int16
|
field :x, Int16
|
||||||
|
@ -285,6 +368,53 @@ module X11
|
||||||
field :window, Window
|
field :window, Window
|
||||||
end
|
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
|
class OpenFont < BaseForm
|
||||||
field :opcode, Uint8, value: 45
|
field :opcode, Uint8, value: 45
|
||||||
unused 1
|
unused 1
|
||||||
|
@ -310,6 +440,16 @@ module X11
|
||||||
field :pattern, String8
|
field :pattern, String8
|
||||||
end
|
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
|
class Str < BaseForm
|
||||||
field :name, Uint8, :length, value: ->(str) { str.name.length }
|
field :name, Uint8, :length, value: ->(str) { str.name.length }
|
||||||
field :name, String8Unpadded, :string
|
field :name, String8Unpadded, :string
|
||||||
|
@ -369,6 +509,21 @@ module X11
|
||||||
field :height, Uint16
|
field :height, Uint16
|
||||||
end
|
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
|
class PolyFillRectangle < BaseForm
|
||||||
field :opcode, Uint8, value: 70
|
field :opcode, Uint8, value: 70
|
||||||
unused 1
|
unused 1
|
||||||
|
@ -405,7 +560,7 @@ module X11
|
||||||
class ImageText8 < BaseForm
|
class ImageText8 < BaseForm
|
||||||
field :opcode, Uint8, value: 76
|
field :opcode, Uint8, value: 76
|
||||||
field :n, Uint8, :length
|
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 :drawable, Drawable
|
||||||
field :gc, Gcontext
|
field :gc, Gcontext
|
||||||
field :x, Int16
|
field :x, Int16
|
||||||
|
@ -413,15 +568,118 @@ module X11
|
||||||
field :n, String8, :string
|
field :n, String8, :string
|
||||||
end
|
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)
|
# Events (page ~157)
|
||||||
# FIXME: Events have quite a bit of redundancy, but unfortunately
|
# FIXME: Events have quite a bit of redundancy, but unfortunately
|
||||||
# BaseForm can't handle subclassing well.
|
# 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
|
field :code, Uint8
|
||||||
unused 1
|
end
|
||||||
|
|
||||||
|
class SimpleEvent < Event
|
||||||
|
field :detail, Uint8
|
||||||
field :sequence_number, Uint16
|
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 :x, Uint16
|
||||||
field :y, Uint16
|
field :y, Uint16
|
||||||
field :width, Uint16
|
field :width, Uint16
|
||||||
|
@ -430,17 +688,22 @@ module X11
|
||||||
unused 14
|
unused 14
|
||||||
end
|
end
|
||||||
|
|
||||||
class MapNotify < BaseForm
|
class NoExposure < SimpleEvent # 14
|
||||||
field :code, Uint8
|
field :drawable, Drawable
|
||||||
|
field :minor_opcode, Uint16
|
||||||
|
field :major_opcode, Uint8
|
||||||
|
unused 21
|
||||||
|
end
|
||||||
|
|
||||||
|
class MapNotify < Event
|
||||||
unused 1
|
unused 1
|
||||||
field :sequence_number, Uint16
|
field :sequence_number, Uint16
|
||||||
field :event, Window
|
field :event, Window
|
||||||
field :override_redirect, Bool
|
field :override_redirect, Bool
|
||||||
unused 19
|
unused 19
|
||||||
end
|
end
|
||||||
|
|
||||||
class ConfigureNotify < BaseForm
|
class ConfigureNotify < Event
|
||||||
field :code, Uint8
|
|
||||||
unused 1
|
unused 1
|
||||||
field :sequence_number, Uint16
|
field :sequence_number, Uint16
|
||||||
field :event, Window
|
field :event, Window
|
||||||
|
@ -453,5 +716,150 @@ module X11
|
||||||
field :override_redirect, Bool
|
field :override_redirect, Bool
|
||||||
unused 5
|
unused 5
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,6 +15,10 @@ module X11
|
||||||
@internal.root_depth
|
@internal.root_depth
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def root_visual
|
||||||
|
@internal.root_visual
|
||||||
|
end
|
||||||
|
|
||||||
def width
|
def width
|
||||||
@internal.width_in_pixels
|
@internal.width_in_pixels
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,6 +20,11 @@ module X11
|
||||||
def self.size
|
def self.size
|
||||||
#{bytesize}
|
#{bytesize}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.from_packet(sock)
|
||||||
|
r = sock.read(size)
|
||||||
|
r ? unpack(r) : nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -34,10 +39,12 @@ module X11
|
||||||
|
|
||||||
class String8
|
class String8
|
||||||
def self.pack(x)
|
def self.pack(x)
|
||||||
x + "\x00"*(-x.length & 3)
|
x.force_encoding("ASCII-8BIT") + "\x00"*(-x.length & 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def self.unpack(socket, size)
|
def self.unpack(socket, size)
|
||||||
val = socket.read(size)
|
val = socket.read(size)
|
||||||
unused_padding = (4 - (size % 4)) % 4
|
unused_padding = (4 - (size % 4)) % 4
|
||||||
|
@ -46,6 +53,20 @@ module X11
|
||||||
end
|
end
|
||||||
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
|
class String8Unpadded
|
||||||
def self.pack(x)
|
def self.pack(x)
|
||||||
x
|
x
|
||||||
|
@ -63,7 +84,7 @@ module X11
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.unpack(str)
|
def self.unpack(str)
|
||||||
str[0] == "0x01"
|
str[0] == "\x01"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.size
|
def self.size
|
||||||
|
@ -90,6 +111,6 @@ module X11
|
||||||
VisualID = Uint32
|
VisualID = Uint32
|
||||||
Mask = Uint32
|
Mask = Uint32
|
||||||
Timestamp = Uint32
|
Timestamp = Uint32
|
||||||
|
Keysym = Uint32
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
module X11
|
module X11
|
||||||
VERSION = "0.0.1"
|
VERSION = "0.0.2"
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue