lib/i3ipc/protocol.rb in i3ipc-0.1.0 vs lib/i3ipc/protocol.rb in i3ipc-0.2.0
- old
+ new
@@ -1,19 +1,19 @@
require 'socket'
module I3Ipc
# Communication interface with i3-ipc.
- #
# Can connect to i3-ipc socket, disconnect, send and receive messages.
#
- # Usage example:
+ # For i3-ipc interface details refer to https://i3wm.org/docs/ipc.html.
+ #
+ # @example
# protocol = Protocol.new
# protocol.send(7)
# puts protocol.receive
# protocol.disconnect
#
- # For i3-ipc interface details refer to https://i3wm.org/docs/ipc.html.
class Protocol
# Magic string for i3-ipc protocol to ensure the integrity of messages.
MAGIC_STRING = 'i3-ipc'
# Throws when received data with not expected magic string.
@@ -47,11 +47,11 @@
def initialize(socketpath = nil)
@socketpath = socketpath ? socketpath : get_socketpath
end
- # Connects to i3-ipc server socket using Socket::UNIXSocket.
+ # Connects to i3-ipc server socket using {::UNIXSocket}.
# Does nothing if already connected.
def connect
@socket = UNIXSocket.new(@socketpath) unless @socket
end
@@ -61,29 +61,28 @@
@socket && @socket.close
@socket = nil
end
# Sends packed message to i3-ipc server socket.
+ # @param [Integer] type type of the message.
+ # @param [String] payload message payload.
#
- # Throws:
- # * NotConnected if protocol is not connected.
- #
- # +type+: type of the message.
- # +payload+: payload of the message
+ # @raise [NotConnected] if protocol is not connected.
def send(type, payload = nil)
check_connected
@socket.write(pack(type, payload))
end
# Receives message from i3-ipc server socket.
#
- # Throws:
- # * NotConnected if protocol is not connected.
- # * WrongMagicString if got message with not expected magic string.
- # * WrongType if got message with not expected magic type.
+ # @param [Integer] type expected type of the message.
#
- # +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.
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)
@@ -92,31 +91,58 @@
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.
+ 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
+
private
# Packs the message.
# A typical message looks like:
+ # @example
# <header><payload>
# where a header is:
+ # @example
# <magic string><message length><message type>
#
- # +type+: type of the message
- # +payload+: patload of the message
+ # @param [Integer] type type of the message.
+ # @param [String] payload payload of the message.
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>
#
- # +data+: data to be unpacked.
+ # @param [String] data: data to be unpacked.
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]