Extensive increase in number of requests and events supported.

This commit is contained in:
Vidar Hokstad 2023-12-16 14:49:10 +00:00
parent 370ac3e367
commit e5b672673c
3 changed files with 413 additions and 122 deletions

View file

@ -68,6 +68,7 @@ module X11
def read_error data
error = Form::Error.from_packet(StringIO.new(data))
STDERR.puts "ERROR: #{error.inspect}"
raise error.inspect
error
end
@ -80,28 +81,47 @@ module X11
end
def read_event type, data, event_class
io = StringIO.new(data)
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 5
return Form::ButtonRelease.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
return Form::ConfigureNotify.from_packet(StringIO.new(data))
# 0 is error, not handled here
# 1 is reply, not handled here
when 2 then return Form::KeyPress.from_packet(io)
when 3 then return Form::KeyRelease.from_packet(io)
when 4 then return Form::ButtonPress.from_packet(io)
when 5 then return Form::ButtonRelease.from_packet(io)
when 6 then return Form::MotionNotify.from_packet(io)
when 7 then return Form::EnterNotify.from_packet(io)
when 8 then return Form::LeaveNotify.from_packet(io)
when 9 then return Form::FocusIn.from_packet(io)
when 10 then return Form::FocusOut.from_packet(io)
# FIXME 11: KeymapNotify
when 12 then return Form::Expose.from_packet(io)
# FIXME 13: GraphicsExposure
when 14 then return Form::NoExposure.from_packet(io)
# FIXME: 15: VisibilityNotify
when 16 then return Form::CreateNotify.from_packet(io)
when 17 then return Form::DestroyNotify.from_packet(io)
when 18 then return Form::UnmapNotify.from_packet(io)
when 19 then return Form::MapNotify.from_packet(io)
when 20 then return Form::MapRequest.from_packet(io)
when 21 then return Form::ReparentNotify.from_packet(io)
when 22 then return Form::ConfigureNotify.from_packet(io)
when 23 then return Form::ConfigureRequest.from_packet(io)
# FIXME: 24: GravityNotify
# FIXME: 25: ResizeRequest
# FIXME: 26: CirculateNotify
# FIXME: 27: CirculateRequest
when 28 then return Form::PropertyNotify.from_packet(io)
# FIXME: 29: SelectionClear
# FIXME: 30: SelectionRequest
# FIXME: 31: SelectionNotify
# FIXME: 32: ColormapNotify
when 33 then return Form::ClientMessage.from_packet(StringIO.new(data))
# FIXME: 34: MappingNotify
else
STDERR.puts "FIXME: Event: #{type}"
STDERR.puts "EVENT: #{data.inspect}"
data
end
end
@ -122,19 +142,28 @@ module X11
data = read_full_packet(32)
return nil if data.nil?
type = data.unpack("C").first
# FIXME: What is bit 8 for? Synthentic?
type = data.unpack("C").first & 0x7f
case type
when 0
read_error(data)
when 1
read_reply(data)
when 2..34
read_event(type, data, nil)
when 0 then read_error(data)
when 1 then read_reply(data)
when 2..34 then read_event(type, data, nil)
else
raise ProtocolError, "Unsupported reply type: #{type}"
raise ProtocolError, "Unsupported reply type: #{type} #{data.inspect}"
end
end
def write_raw_packet(pkt)
@requestseq += 1
@socket.write(pkt)
end
def write_packet(*args)
pkt = args.join
pkt[2..3] = u16(pkt.length/4)
write_raw_packet(pkt)
end
def write_request ob
#p data
#p [:write_request, @requestseq, ob.class]
@ -142,14 +171,16 @@ module X11
#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)
write_raw_packet(data)
end
def write_sync(data, reply=nil)
seq = @requestseq
write_request(data)
pkt = next_reply
pkt = next_reply(seq)
return nil if !pkt
return pkt if pkt.is_a?(X11::Form::Error)
pp reply
reply ? reply.from_packet(StringIO.new(pkt)) : pkt
end
@ -161,17 +192,19 @@ module X11
@queue.shift || read_packet
end
def next_reply
def next_reply(errseq)
# FIXME: This is totally broken
while pkt = read_packet
if pkt.is_a?(String)
return pkt
elsif pkt.is_a?(X11::Form::Error) && pkt.sequence_number == errseq
return pkt
else
@queue.push(pkt)
end
end
end
def run
loop do
pkt = read_packet
@ -193,12 +226,10 @@ module X11
wid = new_id
parent ||= screens.first.root
if visual.nil?
visual = find_visual(0, depth).visual_id
end
values[X11::Form::CWColorMap] ||= create_colormap(0, parent, visual)
values = values.sort_by{_1[0]}
@ -212,6 +243,10 @@ module X11
return wid
end
def get_window_attributes(wid)
write_sync( Form::GetWindowAttributes.new(wid), Form::WindowAttributes )
end
def change_window_attributes(wid,
values: {})
values = values.sort_by{_1[0]}
@ -222,7 +257,10 @@ module X11
)
end
def select_input(w, events) = change_window_attributes(w, values: {Form::CWEventMask => events})
def atom(name)
name = name.to_sym
intern_atom(false, name) if !@atoms[name]
@atoms[name]
end
@ -251,13 +289,13 @@ module X11
end
end
def destroy_window(window)
write_request(X11::Form::DestroyWindow.new(window))
end
def get_geometry(drawable)
write_sync(X11::Form::GetGeometry.new(drawable), X11::Form::Geometry)
def get_atom_name(atom)
reply = write_sync(X11::Form::GetAtomName.new(atom), X11::Form::AtomName)
reply&.name
end
def destroy_window(window) = write_request(X11::Form::DestroyWindow.new(window))
def get_geometry(drawable) = write_sync(X11::Form::GetGeometry.new(drawable), X11::Form::Geometry)
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)
@ -269,8 +307,35 @@ module X11
mid
end
def change_property(*args)
write_request(X11::Form::ChangeProperty.new(*args))
def get_property(window, property, type, offset: 0, length: 4, delete: false)
property = atom(property) if !property.is_a?(Integer)
type = atom_enum(type)
result = write_sync(X11::Form::GetProperty.new(
delete, window, property, type, offset, length
), X11::Form::Property)
if result && result.format != 0
case result.format
when 16
result.value = result.value.unpack("v")
result.value = result.value.first if length == 2
when 32
result.value = result.value.unpack("V")
result.value = result.value.first if length == 4
end
elsif result
result.value = nil
end
result
end
def change_property(mode, window, property, type, format, data)
property = atom(property.to_sym) if property.is_a?(Symbol) || property.is_a?(String)
mode = open_enum(mode, {replace: 0, prepend: 1, append: 2})
type = atom_enum(type)
write_request(X11::Form::ChangeProperty.new(mode, window, property, type, format, data))
end
def list_fonts(*args)
@ -278,25 +343,46 @@ module X11
X11::Form::ListFontsReply)
end
def open_font(*args)
write_request(X11::Form::OpenFont.new(*args))
def open_font(*args) = write_request(X11::Form::OpenFont.new(*args))
def change_gc(*args) = write_request(X11::Form::ChangeGC.new(*args))
def change_save_set(...)= write_request(X11::Form::ChangeSaveSet.new(...))
def reparent_window(window, parent, x, y, save: true)
# You so almost always want this that it should've been a single request
change_save_set(0, window) if save
write_request(X11::Form::ReparentWindow.new(window, parent, x,y))
end
def map_window(*args) = write_request(X11::Form::MapWindow.new(*args))
def unmap_window(*args) = write_request(X11::Form::UnmapWindow.new(*args))
def u8(*args) = args.pack("c*")
def u16(*args) = args.pack("v*")
def u32(*args) = args.pack("V*")
def atom_enum(val) = open_enum(val, {cardinal: Form::CardinalAtom, atom: Form::AtomAtom, window: Form::WindowAtom})
def window(*args)
args.each {|a| raise "Window expected" if a.nil? }
u32(*args)
end
def change_gc(*args)
write_request(X11::Form::ChangeGC.new(*args))
def open_enum(val, map) = (map[val].nil? ? val : map[val])
def set_input_focus(revert_to, focus, time=:now)
# FIXME: This is an experiment.
# Upside: Simpler. Downside: Doesn't work server-side.
#
revert_to = open_enum(revert_to, {none: 0, pointer_root: 1, parent: 2})
focus = open_enum(focus, {none: 0, pointer_root: 1 })
time = open_enum(time, {current_time: 0, now: 0})
write_packet(u8(42,revert_to), u16(3), window(focus), u32(time))
end
def map_window(*args)
write_request(X11::Form::MapWindow.new(*args))
end
def grab_key(owner_events, grab_window, modifiers, keycode, pointer_mode, keyboard_mode)
write_request(X11::Form::GrabKey.new(
owner_events,
grab_window,
modifiers,
keycode,
pointer_mode == :async ? 1 : 0,
pointer_mode == :async ? 1 : 0,
keyboard_mode == :async ? 1 : 0
))
end
@ -354,7 +440,7 @@ module X11
when :below then 1
when :top_if then 2
when :bottom_if then 3
when :opposite then 4
when :opposite then 4
else raise "Unknown stack_mode #{stack_mode.inspect}"
end
end
@ -384,29 +470,12 @@ module X11
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 put_image(*args) = write_request(X11::Form::PutImage.new(*args))
def clear_area(*args) = write_request(X11::Form::ClearArea.new(*args))
def copy_area(*args) = write_request(X11::Form::CopyArea.new(*args))
def image_text8(*args) = write_request(X11::Form::ImageText8.new(*args))
def image_text16(*args)= write_request(X11::Form::ImageText16.new(*args))
def poly_fill_rectangle(*args) = write_request(X11::Form::PolyFillRectangle.new(*args))
def create_pixmap(depth, drawable, w,h)
pid = new_id

View file

@ -81,14 +81,14 @@ module X11
when :unused
sz = s.size.respond_to?(:call) ? s.size.call(self) : s.size
"\x00" * sz
when :length
when :length, :format_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|
Array(value).collect do |obj|
if obj.is_a?(BaseForm)
obj.to_packet
else
@ -127,8 +127,17 @@ module X11
when :length
size = s.type_klass.unpack( socket.read(s.type_klass.size) )
lengths[s.name] = size
when :format_length
size = s.type_klass.unpack( socket.read(s.type_klass.size) )
lengths[s.name] = case form.format
when 8 then size
when 16 then size*2
when 32 then size*4
else 0
end
when :string
val = s.type_klass.unpack(socket, lengths[s.name])
len = lengths[s.name]
val = s.type_klass.unpack(socket, len)
form.instance_variable_set("@#{s.name}", val)
when :list
len = lengths[s.name]
@ -177,12 +186,14 @@ module X11
end
def fields
super+Array(@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 or s.type == :format_length}
end
end
end
AtomAtom=4
CardinalAtom=6
WindowAtom=33
##
## X11 Packet Defintions
@ -247,9 +258,9 @@ module X11
end
class DisplayInfo < BaseForm
field :release_number, Uint32
field :resource_id_base, Uint32
field :resource_id_mask, Uint32
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
@ -258,11 +269,11 @@ module X11
field :image_byte_order, Signifigance
field :bitmap_bit_order, Signifigance
field :bitmap_format_scanline_unit, Uint8
field :bitmap_format_scanline_pad, Uint8
field :bitmap_format_scanline_pad, Uint8
field :min_keycode, KeyCode
field :max_keycode, KeyCode
unused 4
field :vendor, String8, :string
field :vendor, String8, :string
field :formats, FormatInfo, :list
field :screens, ScreenInfo, :list
end
@ -287,13 +298,13 @@ module X11
# XRender structures
class DirectFormat < BaseForm
field :red, Uint16
field :red_mask, Uint16
field :green, Uint16
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 :blue, Uint16
field :blue_mask, Uint16
field :alpha, Uint16
field :alpha_mask, Uint16
end
@ -328,8 +339,8 @@ module X11
# Requests
CopyFromParent = 0
InputOutput = 1
InputOnly = 2
InputOutput = 1
InputOnly = 2
CWBackPixmap = 0x0001
CWBackPixel = 0x0002
@ -337,23 +348,29 @@ module X11
CWBorderPixel = 0x0008
CWBitGravity = 0x0010
CWWinGravity = 0x0020
CWBackingStore= 0x0040
CWSaveUnder = 0x0400
CWEventMask = 0x0800
CWColorMap = 0x2000
CWBackingStore = 0x0040
CWBackingPlanes = 0x0080
CWBackingPixel = 0x0100
CWOverrideRedirect = 0x0200
CWSaveUnder = 0x0400
CWEventMask = 0x0800
CWColorMap = 0x2000
KeyPressMask = 0x00001
KeyReleaseMask = 0x00002
ButtonPressMask = 0x00004
ButtonReleaseMask = 0x00008
EnterWindowMask = 0x00010
LeaveWindowMask = 0x00020
PointerMotionMask = 0x00040
PointerMotionHintMask = 0x00080
Button1MotionMask = 0x00100
ExposureMask = 0x08000
StructureNotifyMask = 0x20000
SubstructureNotifyMask = 0x80000
KeyPressMask = 0x000001
KeyReleaseMask = 0x000002
ButtonPressMask = 0x000004
ButtonReleaseMask = 0x000008
EnterWindowMask = 0x000010
LeaveWindowMask = 0x000020
PointerMotionMask = 0x000040
PointerMotionHintMask = 0x000080
Button1MotionMask = 0x000100
ExposureMask = 0x008000
StructureNotifyMask = 0x020000
SubstructureNotifyMask = 0x080000
SubstructureRedirectMask=0x100000
FocusChangeMask = 0x200000
PropertyChangeMask = 0x400000
class CreateWindow < BaseForm
field :opcode, Uint8, value: 1
@ -394,14 +411,14 @@ module X11
field :sequence_number, Uint16
field :reply_length, Uint32
field :visual, VisualID
field :class, Uint16
field :wclass, Uint16
field :bit_gravity, Uint8
field :win_gravity, Uint8
field :backing_planes, Uint32
field :backing_pixel, Uint32
field :save_under, Bool
field :map_is_installed, Bool
field :map_state, Bool
field :map_state, Uint8
field :override_redirect, Bool
field :colormap, Colormap
field :all_event_masks, Uint32
@ -417,6 +434,23 @@ module X11
field :window, Window
end
class ChangeSaveSet < BaseForm
field :opcode, Uint8, value: 6
field :mode, Uint8
field :request_length, Uint16, value: 2
field :window, Window
end
class ReparentWindow < BaseForm
field :opcode, Uint8, value: 7
unused 1
field :request_length, Uint16, value: 4
field :window, Window
field :parent, Window
field :x, Int16
field :y, Int16
end
class MapWindow < BaseForm
field :opcode, Uint8, value: 8
unused 1
@ -424,6 +458,13 @@ module X11
field :window, Window
end
class UnmapWindow < BaseForm
field :opcode, Uint8, value: 10
unused 1
field :request_length, Uint16, value: 2
field :window, Window
end
class ConfigureWindow < BaseForm
field :opcode, Uint8, value: 12
unused 1
@ -455,6 +496,25 @@ module X11
unused 10
end
class QueryTree < BaseForm
field :opcode, Uint8, value: 15
unused 1
field :request_length, Uint16, value: 2
field :window, Window
end
class QueryTreeReply < BaseForm
field :reply, Uint8, value: 1
unused 1
field :sequence_number, Uint16
field :reply_length, Uint32
field :root, Window
field :parent, Window
field :children, Uint16, :length
unused 14
field :children, Window, :list
end
class InternAtom < BaseForm
field :opcode, Uint8, value: 16
field :only_if_exists, Bool
@ -480,6 +540,22 @@ module X11
unused 20
end
class GetAtomName < BaseForm
field :opcode, Uint8, value: 17
unused 1
field :request_length, Uint16, value: 2
field :atom, Atom
end
class AtomName < Reply
unused 1
field :sequence_number, Uint16
field :reply_length, Uint32
field :name, Uint16, :length
unused 22
field :name, String8, :string
end
Replace = 0
Prepend = 1
Append = 2
@ -496,12 +572,33 @@ module X11
field :type, Atom
field :format, Uint8
unused 3
field :data, Uint32, value: ->(cp) {
cp.data.length / 4
}
field :data, Uint32, value: ->(cp) { cp.data.length / (cp.format/8) }
field :data, Uint8, :list
end
class GetProperty < BaseForm
field :opcode, Uint8, value: 20
field :delete, Bool
field :request_length, Uint16, value: 6
field :window, Window
field :property, Atom
field :type, Atom
field :long_offset, Uint32
field :long_length, Uint32
end
class Property < BaseForm
field :reply, Uint8, value: 1
field :format, Uint8
field :sequence_number, Uint16
field :reply_length, Uint32
field :type, Atom
field :bytes_after, Uint32
field :value, Uint32, :format_length
unused 12
field :value, String8, :string
end
class GrabButton < BaseForm
field :opcode, Uint8, value: 28
field :owner_events, Bool
@ -528,6 +625,18 @@ module X11
field :keyboard_mode, Uint8
unused 3
end
class OpenFont < BaseForm
field :opcode, Uint8, value: 45
unused 1
field :request_length, Uint16, value: ->(of) {
3+(of.name.length+3)/4
}
field :fid, Font
field :name, Uint16, :length
unused 2
field :name, String8, :string
end
class ListFonts < BaseForm
field :opcode, Uint8, value: 49
@ -618,11 +727,11 @@ module X11
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 :src_x, Uint16
field :src_y, Uint16
field :dst_x, Uint16
field :dst_y, Uint16
field :width, Uint16
field :height, Uint16
end
@ -754,7 +863,7 @@ module X11
field :sequence_number, Uint16
end
class PressEvent < SimpleEvent
class InputEvent < SimpleEvent
field :time, Uint32
field :root, Window
field :event, Window
@ -764,6 +873,32 @@ module X11
field :event_x, Int16
field :event_y, Int16
field :state, Uint16
end
class EnterLeaveNotify < InputEvent
field :mode, Uint8
field :same_screen_or_focus, Uint8
def same_screen = same_screen_or_focus.anybit?(0x02)
def focus = same_screen_or_focus.anybit?(0x01)
end
class EnterNotify < EnterLeaveNotify
end
class LeaveNotify < EnterLeaveNotify
end
class FocusIn < SimpleEvent
field :event, Window
field :mode, Uint8
unused 23
end
class FocusOut < FocusIn
end
class PressEvent < InputEvent
field :same_screen, Bool
unused 1
end
@ -782,6 +917,31 @@ module X11
class ButtonRelease < PressEvent
end
class ReparentNotify < SimpleEvent
field :event, Window
field :window, Window
field :parent, Window
field :x, Int16
field :y, Int16
field :override_redirect, Bool
unused 11
end
class ConfigureRequest < Event # 23
field :stack_mode, Uint8
field :sequence_number, Uint16
field :parent, Window
field :window, Window
field :sibling, Window
field :x, Int16
field :y, Int16
field :width, Uint16
field :height, Uint16
field :border_width, Uint16
field :value_mask, Uint16
unused 4
end
class Expose < SimpleEvent
field :window, Window
@ -800,14 +960,51 @@ module X11
unused 21
end
class CreateNotify < SimpleEvent # 16
field :parent, Window
field :window, Window
field :x, Int16
field :y, Int16
field :width, Uint16
field :height, Uint16
field :border_width, Uint16
field :override_redirect, Bool
end
class DestroyNotify < Event
unused 1
field :sequence_number, Uint16
field :event, Window
field :window, Window
unused 20
end
class UnmapNotify < Event
unused 1
field :sequence_number, Uint16
field :event, Window
field :window, Window
field :from_configure, Bool
unused 19
end
class MapNotify < Event
unused 1
field :sequence_number, Uint16
field :event, Window
field :window, Window
field :override_redirect, Bool
unused 19
end
class MapRequest < Event
unused 1
field :sequence_number, Uint16
field :parent, Window
field :window, Window
unused 20
end
class ConfigureNotify < Event
unused 1
field :sequence_number, Uint16
@ -822,6 +1019,24 @@ module X11
unused 5
end
class ClientMessage < Event
field :format, Uint8
field :sequence_number, Uint16
field :window, Window
field :type, Atom
field :data, X11::Type::Message
end
class PropertyNotify < Event # 28
unused 1
field :sequence_number, Uint16
field :window, Window
field :atom, Atom
field :time, Uint32
field :state, Uint8
unused 15
end
# XRender extension
# From https://cgit.freedesktop.org/xorg/proto/renderproto/tree/renderproto.h

View file

@ -36,16 +36,23 @@ module X11
define "Uint8", "C", 1
define "Uint16", "S", 2
define "Uint32", "L", 4
define "Message", "c*", 20
class Message
def self.pack(x) = x.b
def self.unpack(x) = x.b
def self.size = 20
def self.from_packet(sock) = sock.read(2).b
end
class String8
def self.pack(x)
x.force_encoding("ASCII-8BIT") + "\x00"*(-x.length & 3)
end
def self.unpack(socket, size)
raise "Expected size for String8" if size.nil?
val = socket.read(size)
unused_padding = (4 - (size % 4)) % 4
socket.read(unused_padding)