class I3Ipc::Protocol
Communication interface with i3-ipc. Can connect to i3-ipc socket, disconnect, send and receive messages.
For i3-ipc interface details refer to i3wm.org/docs/ipc.html.
@example
protocol = Protocol.new protocol.send(7) puts protocol.receive protocol.disconnect
Constants
- MAGIC_STRING
Magic string for i3-ipc protocol to ensure the integrity of messages.
Public Class Methods
# File lib/i3ipc/protocol.rb, line 48 def initialize(socketpath = nil) @socketpath = socketpath ? socketpath : get_socketpath end
Public Instance Methods
Connects to i3-ipc server socket using {::UNIXSocket}. Does nothing if already connected.
# File lib/i3ipc/protocol.rb, line 54 def connect @socket = UNIXSocket.new(@socketpath) unless @socket end
Disconnects from i3-ipc server socket. Does nothing if not connected.
# File lib/i3ipc/protocol.rb, line 60 def disconnect @socket && @socket.close @socket = nil end
Receives message from i3-ipc server socket.
@param [Integer] type expected type of the message.
@return [String] unpacked response from i3 server.
@raise [NotConnected] if protocol is not connected. @raise [WrongMagicString] if got message with wrong magic string. @raise [WrongType] if got message with not expected type.
# File lib/i3ipc/protocol.rb, line 84 def receive(type = nil) check_connected # length of "i3-ipc" + 4 bytes length + 4 bytes type data = @socket.read 14 magic, len, recv_type = unpack_header(data) raise WrongMagicString.new(magic) unless MAGIC_STRING.eql? magic type && (raise WrongType.new(type, recv_type) unless type == recv_type) @socket.read(len) end
Receives event from i3-ipc server socket.
@param [Integer] type expected type of the message.
@return [String] unpacked response from i3 server.
@raise [NotConnected] if protocol is not connected. @raise [WrongMagicString] if got message with wrong magic string. @raise [WrongType] if got message with not expected type.
# File lib/i3ipc/protocol.rb, line 105 def receive_event(type = nil) check_connected # length of "i3-ipc" + 4 bytes length + 4 bytes type data = @socket.read 14 magic, len, recv_type = unpack_header(data) # Strip highest bit recv_type = recv_type & 2147483647 raise WrongMagicString.new(magic) unless MAGIC_STRING.eql? magic type && (raise WrongType.new(type, recv_type) unless type == recv_type) @socket.read(len) end
Sends packed message to i3-ipc server socket. @param [Integer] type type of the message. @param [String] payload message payload.
@raise [NotConnected] if protocol is not connected.
# File lib/i3ipc/protocol.rb, line 70 def send(type, payload = nil) check_connected @socket.write(pack(type, payload)) end
Private Instance Methods
# File lib/i3ipc/protocol.rb, line 170 def check_connected raise NotConnected unless @socket end
# File lib/i3ipc/protocol.rb, line 152 def get_socketpath if !ENV['I3SOCK'].nil? ENV['I3SOCK'] else cmd = if system('i3 --version') 'i3' elsif system('sway --version') 'sway' else raise 'Unable to find i3 compatible window manager' end path = `#{cmd} --get-socketpath`.chomp! raise 'Unable to get i3 compatible socketpath' unless path path end end
Packs the message. A typical message looks like: @example
<header><payload>
where a header is: @example
<magic string><message length><message type>
@param [Integer] type type of the message. @param [String] payload payload of the message.
# File lib/i3ipc/protocol.rb, line 132 def pack(type, payload=nil) size = payload ? payload.to_s.bytes.count : 0 msg = MAGIC_STRING + [size, type].pack("LL") msg << payload.to_s if payload msg end
Unpacks the header. A typical header looks like: @example
<magic_string><message length><message type>
@param [String] data: data to be unpacked.
# File lib/i3ipc/protocol.rb, line 145 def unpack_header(data) struct_header_len = MAGIC_STRING.size magic_message = data[0, struct_header_len] len, type = data[struct_header_len..-1].unpack("LL") [magic_message, len, type] end