Class: I3::IPC

Inherits:
Object
  • Object
show all
Defined in:
lib/i3-ipc.rb

Defined Under Namespace

Classes: WrongMagicCode, WrongType

Constant Summary collapse

MAGIC_STRING =
"i3-ipc"
SOCKET_PATH =
File.expand_path("~/.i3/ipc.sock")
MESSAGE_TYPE_COMMAND =
0
MESSAGE_TYPE_GET_WORKSPACES =
1
MESSAGE_TYPE_SUBSCRIBE =
2
MESSAGE_TYPE_GET_OUTPUTS =
3
MESSAGE_REPLY_COMMAND =
0
MESSAGE_REPLY_GET_WORKSPACES =
1
MESSAGE_REPLY_SUBSCRIBE =
2
MESSAGE_REPLY_GET_OUTPUTS =
3
EVENT_MASK =
(1 << 31)
EVENT_WORKSPACE =
(EVENT_MASK | 0)

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(socket_path = SOCKET_PATH, force_connect = false) ⇒ IPC

connects to the given i3 ipc interface

Parameters:

  • socket_path (defaults to: SOCKET_PATH)

    String the path to i3’s socket

  • force_connect (defaults to: false)

    Boolean connects to the socket if true



32
33
34
35
# File 'lib/i3-ipc.rb', line 32

def initialize(socket_path=SOCKET_PATH, force_connect=false)
  @socket_path = socket_path
  connect if connect
end

Class Method Details

.format(type, payload = nil) ⇒ Object

format the message a typical message looks like

"i3-ipc" <message length> <message type> <payload>


97
98
99
100
101
102
# File 'lib/i3-ipc.rb', line 97

def self.format(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

.parse_response(response) ⇒ Object

parse a full ipc response similar to handle_response, but parses full reply as received by EventMachine

returns an Array containing the reply type and the parsed data



114
115
116
117
118
119
120
121
122
123
# File 'lib/i3-ipc.rb', line 114

def self.parse_response(response)
  if response[0, (MAGIC_STRING.length)] != MAGIC_STRING
    raise WrongMagicCode
  end

  len, recv_type = response[6, 8].unpack("LL")

  answer = response[14, len]
  [recv_type, ::JSON.parse(answer)]
end

.subscribe(list, socket_path = SOCKET_PATH, &blk) ⇒ Object

shortcut



38
39
40
# File 'lib/i3-ipc.rb', line 38

def self.subscribe(list, socket_path=SOCKET_PATH, &blk)
  Subscription.subscribe(list, socket_path, &blk)
end

Instance Method Details

#closeObject

closes the socket connection



146
147
148
# File 'lib/i3-ipc.rb', line 146

def close
  @socket.close
end

#closed?Boolean

alias for @socket.closed? for easy access

Returns:

  • (Boolean)


151
152
153
# File 'lib/i3-ipc.rb', line 151

def closed?
  @socket.closed?
end

#command(payload) ⇒ Object

send a command to i3

the payload is a command for i3 (like the commands you can bind to keys in the configuration file) and will be executed directly after receiving it.

returns { “success” => true } for now. i3 does send this reply without checks



50
51
52
53
# File 'lib/i3-ipc.rb', line 50

def command(payload)
  write format(MESSAGE_TYPE_COMMAND, payload)
  handle_response MESSAGE_TYPE_COMMAND
end

#connectObject

connects to the given socket



141
142
143
# File 'lib/i3-ipc.rb', line 141

def connect
  @socket = UNIXSocket.new(@socket_path)
end

#format(type, payload = nil) ⇒ Object



104
105
106
# File 'lib/i3-ipc.rb', line 104

def format(type, payload=nil)
  self.class.format(type, payload)
end

#get_outputsObject

Gets the current outputs. The reply will be a JSON-encoded list of outputs (see the reply section of i3 docu).



67
68
69
70
# File 'lib/i3-ipc.rb', line 67

def get_outputs
  write format(MESSAGE_TYPE_GET_OUTPUTS)
  handle_response MESSAGE_TYPE_GET_OUTPUTS
end

#get_workspacesObject

gets the current workspaces. the reply will be the list of workspaces (see the reply section of i3 docu)



58
59
60
61
# File 'lib/i3-ipc.rb', line 58

def get_workspaces
  write format(MESSAGE_TYPE_GET_WORKSPACES)
  handle_response MESSAGE_TYPE_GET_WORKSPACES
end

#handle_response(type) ⇒ Object

reads the reply from the socket and parse the returned json into a ruby object

throws WrongMagicCode when magic word is wrong throws WrongType if returned type does not match expected

this is a bit duplicated code but I don’t know a way to read the full send reply without knowing its length

Raises:



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/i3-ipc.rb', line 81

def handle_response(type)
  # reads 14 bytes
  # length of "i3-ipc" + 4 bytes length + 4 bytes type
  buffer = read 14
  raise WrongMagicCode unless buffer[0, (MAGIC_STRING.length)] == MAGIC_STRING

  len, recv_type = buffer[6..-1].unpack("LL")
  raise WrongType unless recv_type == type

  answer = read len
  ::JSON.parse(answer)
end

#parse_response(response) ⇒ Object



125
126
127
# File 'lib/i3-ipc.rb', line 125

def parse_response(response)
  self.class.parse_response(response)
end

#read(len) ⇒ Object



136
137
138
# File 'lib/i3-ipc.rb', line 136

def read(len)
  @socket.read(len)
end

#write(msg) ⇒ Object

writes message to the socket if socket is not connected, it connects first



131
132
133
134
# File 'lib/i3-ipc.rb', line 131

def write(msg)
  connect if @socket.nil? || closed?
  @last_write_length = @socket.write msg
end