i3ipc 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +20 -2
- data/lib/i3ipc/connection.rb +50 -5
- data/lib/i3ipc/protocol.rb +43 -17
- data/lib/i3ipc/reply.rb +9 -10
- data/lib/i3ipc/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1656014a13ed6678e5efba71d74c72e7d910906
|
4
|
+
data.tar.gz: c41b56ab703cf2bc2322ed83218aa7f696e2552f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38f1d719f5c32f6ebd335d376481e76cb6be67ecba380af9753792654b05cf6adeecdb54847089fbf91372c3994291861d1cd49d22e9e660d3c62a9f1a3b5d69
|
7
|
+
data.tar.gz: 136c448a42f39671740959c11c5beca1cd5f16115f2e983b590e6d60dbfd9ee480decde1f66b2c3fa4376ac708b5abe771fe055fd10f0ee2a676ba1adc2f045f
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@ An improved Ruby library to control [i3wm](http://i3wm.org/).
|
|
17
17
|
|
18
18
|
i3's interprocess communication (or [ipc](http://i3wm.org/docs/ipc.html)) is the interface i3 wm uses to receive commands from the clients. It also features a publish/subscribe mechanism for notifying interested parties of window manager events.
|
19
19
|
|
20
|
-
This gem will be useful for example for controlling i3 windows manager or to get various information like the current workspaces or to
|
20
|
+
This gem will be useful for example for controlling i3 windows manager or to get various information like the current workspaces or to implement external workspace bar in `Ruby` language.
|
21
21
|
|
22
22
|
Inspired by [i3ipc-python](https://github.com/acrisci/i3ipc-python), [i3ipc-gjs](https://github.com/acrisci/i3ipc-gjs), [i3ipc-lua](https://github.com/acrisci/i3ipc-lua) and reworked mainly from [i3-ipc](https://github.com/badboy/i3-ipc) (thanks to [@badboy](https://github.com/badboy) for this gem).
|
23
23
|
|
@@ -98,7 +98,25 @@ Reply consists of a list of workspaces. Each workspace has some properties:
|
|
98
98
|
|
99
99
|
### Subscribe
|
100
100
|
|
101
|
-
|
101
|
+
Takes an [event](http://i3wm.org/docs/ipc.html#_available_events) and
|
102
|
+
a Proc object. The Proc object will be called with i3's response
|
103
|
+
whenever i3 generates the specified event. `subscribe` returns a
|
104
|
+
Thread; the block will execute in this thread until the thread is
|
105
|
+
killed.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
block = Proc.new do |reply|
|
109
|
+
if reply.change == 'title'
|
110
|
+
puts "title changed for window #{reply.container.name}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
pid = i3.subscribe('window', block)
|
115
|
+
pid.join
|
116
|
+
```
|
117
|
+
|
118
|
+
It is recommended to use separate `Connection`s for each subscription,
|
119
|
+
since replies to subscription events may be sent by i3 at any time.
|
102
120
|
|
103
121
|
### Outputs
|
104
122
|
|
data/lib/i3ipc/connection.rb
CHANGED
@@ -2,11 +2,23 @@ require 'i3ipc/protocol'
|
|
2
2
|
require 'i3ipc/reply'
|
3
3
|
|
4
4
|
module I3Ipc
|
5
|
+
# Throws when subscribing to an invalid event. Valid events are
|
6
|
+
# listed in function event_number().
|
7
|
+
class WrongEvent < RuntimeError
|
8
|
+
def initialize(event_name)
|
9
|
+
@event_name = event_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def message
|
13
|
+
%Q{Tried to subscribe to invalid event type '#{@event_name}'}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
5
17
|
# Entry point for communication with i3-ipc.
|
6
18
|
# Able to send/receive messages and convert
|
7
19
|
# responses.
|
8
20
|
#
|
9
|
-
#
|
21
|
+
# @example
|
10
22
|
# con = Connection.new
|
11
23
|
# p con.version.human_readable # => 4.10.2 (2015-0...
|
12
24
|
# p con.command('focus left').success? # => true
|
@@ -17,14 +29,14 @@ module I3Ipc
|
|
17
29
|
|
18
30
|
def initialize(connect = true)
|
19
31
|
@protocol = Protocol.new
|
20
|
-
|
32
|
+
open
|
21
33
|
end
|
22
34
|
|
23
|
-
def
|
35
|
+
def open
|
24
36
|
@protocol.connect
|
25
37
|
end
|
26
38
|
|
27
|
-
def
|
39
|
+
def close
|
28
40
|
@protocol.disconnect
|
29
41
|
end
|
30
42
|
|
@@ -36,6 +48,25 @@ module I3Ipc
|
|
36
48
|
reply_for(1)
|
37
49
|
end
|
38
50
|
|
51
|
+
def subscribe(event, block)
|
52
|
+
event_number = event_number(event)
|
53
|
+
|
54
|
+
# Send subscription request
|
55
|
+
@protocol.send(2, [event])
|
56
|
+
|
57
|
+
reply = Reply.parse(@protocol.receive 2)
|
58
|
+
raise WrongEvent.new(event) unless reply.success?
|
59
|
+
|
60
|
+
pid = Thread.new do
|
61
|
+
while true
|
62
|
+
reply = Reply.parse(@protocol.receive_event event_number)
|
63
|
+
block.call(reply)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
pid
|
68
|
+
end
|
69
|
+
|
39
70
|
def outputs
|
40
71
|
reply_for(3)
|
41
72
|
end
|
@@ -58,9 +89,23 @@ module I3Ipc
|
|
58
89
|
|
59
90
|
private
|
60
91
|
|
61
|
-
def reply_for(type, message = nil)
|
92
|
+
def reply_for(type, message = nil, events=nil)
|
62
93
|
@protocol.send(type, message)
|
94
|
+
|
63
95
|
Reply.parse(@protocol.receive type)
|
64
96
|
end
|
97
|
+
|
98
|
+
def event_number(event)
|
99
|
+
case event
|
100
|
+
when 'workspace' then 0
|
101
|
+
when 'output' then 1
|
102
|
+
when 'mode' then 2
|
103
|
+
when 'window' then 3
|
104
|
+
when 'barconfig_update' then 4
|
105
|
+
when 'binding' then 5
|
106
|
+
else raise WrongEvent.new(event)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
65
110
|
end
|
66
111
|
end
|
data/lib/i3ipc/protocol.rb
CHANGED
@@ -2,16 +2,16 @@ require 'socket'
|
|
2
2
|
|
3
3
|
module I3Ipc
|
4
4
|
# Communication interface with i3-ipc.
|
5
|
-
#
|
6
5
|
# Can connect to i3-ipc socket, disconnect, send and receive messages.
|
7
6
|
#
|
8
|
-
#
|
7
|
+
# For i3-ipc interface details refer to https://i3wm.org/docs/ipc.html.
|
8
|
+
#
|
9
|
+
# @example
|
9
10
|
# protocol = Protocol.new
|
10
11
|
# protocol.send(7)
|
11
12
|
# puts protocol.receive
|
12
13
|
# protocol.disconnect
|
13
14
|
#
|
14
|
-
# For i3-ipc interface details refer to https://i3wm.org/docs/ipc.html.
|
15
15
|
class Protocol
|
16
16
|
# Magic string for i3-ipc protocol to ensure the integrity of messages.
|
17
17
|
MAGIC_STRING = 'i3-ipc'
|
@@ -49,7 +49,7 @@ module I3Ipc
|
|
49
49
|
@socketpath = socketpath ? socketpath : get_socketpath
|
50
50
|
end
|
51
51
|
|
52
|
-
# Connects to i3-ipc server socket using
|
52
|
+
# Connects to i3-ipc server socket using {::UNIXSocket}.
|
53
53
|
# Does nothing if already connected.
|
54
54
|
def connect
|
55
55
|
@socket = UNIXSocket.new(@socketpath) unless @socket
|
@@ -63,12 +63,10 @@ module I3Ipc
|
|
63
63
|
end
|
64
64
|
|
65
65
|
# Sends packed message to i3-ipc server socket.
|
66
|
+
# @param [Integer] type type of the message.
|
67
|
+
# @param [String] payload message payload.
|
66
68
|
#
|
67
|
-
#
|
68
|
-
# * NotConnected if protocol is not connected.
|
69
|
-
#
|
70
|
-
# +type+: type of the message.
|
71
|
-
# +payload+: payload of the message
|
69
|
+
# @raise [NotConnected] if protocol is not connected.
|
72
70
|
def send(type, payload = nil)
|
73
71
|
check_connected
|
74
72
|
@socket.write(pack(type, payload))
|
@@ -76,12 +74,13 @@ module I3Ipc
|
|
76
74
|
|
77
75
|
# Receives message from i3-ipc server socket.
|
78
76
|
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
# * WrongType if got message with not expected magic type.
|
77
|
+
# @param [Integer] type expected type of the message.
|
78
|
+
#
|
79
|
+
# @return [String] unpacked response from i3 server.
|
83
80
|
#
|
84
|
-
#
|
81
|
+
# @raise [NotConnected] if protocol is not connected.
|
82
|
+
# @raise [WrongMagicString] if got message with wrong magic string.
|
83
|
+
# @raise [WrongType] if got message with not expected type.
|
85
84
|
def receive(type = nil)
|
86
85
|
check_connected
|
87
86
|
# length of "i3-ipc" + 4 bytes length + 4 bytes type
|
@@ -94,16 +93,42 @@ module I3Ipc
|
|
94
93
|
@socket.read(len)
|
95
94
|
end
|
96
95
|
|
96
|
+
# Receives event from i3-ipc server socket.
|
97
|
+
#
|
98
|
+
# @param [Integer] type expected type of the message.
|
99
|
+
#
|
100
|
+
# @return [String] unpacked response from i3 server.
|
101
|
+
#
|
102
|
+
# @raise [NotConnected] if protocol is not connected.
|
103
|
+
# @raise [WrongMagicString] if got message with wrong magic string.
|
104
|
+
# @raise [WrongType] if got message with not expected type.
|
105
|
+
def receive_event(type = nil)
|
106
|
+
check_connected
|
107
|
+
# length of "i3-ipc" + 4 bytes length + 4 bytes type
|
108
|
+
data = @socket.read 14
|
109
|
+
magic, len, recv_type = unpack_header(data)
|
110
|
+
|
111
|
+
# Strip highest bit
|
112
|
+
recv_type = recv_type & 2147483647
|
113
|
+
|
114
|
+
raise WrongMagicString.new(magic) unless MAGIC_STRING.eql? magic
|
115
|
+
type && (raise WrongType.new(type, recv_type) unless type == recv_type)
|
116
|
+
|
117
|
+
@socket.read(len)
|
118
|
+
end
|
119
|
+
|
97
120
|
private
|
98
121
|
|
99
122
|
# Packs the message.
|
100
123
|
# A typical message looks like:
|
124
|
+
# @example
|
101
125
|
# <header><payload>
|
102
126
|
# where a header is:
|
127
|
+
# @example
|
103
128
|
# <magic string><message length><message type>
|
104
129
|
#
|
105
|
-
#
|
106
|
-
#
|
130
|
+
# @param [Integer] type type of the message.
|
131
|
+
# @param [String] payload payload of the message.
|
107
132
|
def pack(type, payload=nil)
|
108
133
|
size = payload ? payload.to_s.bytes.count : 0
|
109
134
|
msg = MAGIC_STRING + [size, type].pack("LL")
|
@@ -113,9 +138,10 @@ module I3Ipc
|
|
113
138
|
|
114
139
|
# Unpacks the header.
|
115
140
|
# A typical header looks like:
|
141
|
+
# @example
|
116
142
|
# <magic_string><message length><message type>
|
117
143
|
#
|
118
|
-
#
|
144
|
+
# @param [String] data: data to be unpacked.
|
119
145
|
def unpack_header(data)
|
120
146
|
struct_header_len = MAGIC_STRING.size
|
121
147
|
magic_message = data[0, struct_header_len]
|
data/lib/i3ipc/reply.rb
CHANGED
@@ -3,12 +3,10 @@ require 'json'
|
|
3
3
|
module I3Ipc
|
4
4
|
# Wrapper for reply from i3-ipc.
|
5
5
|
#
|
6
|
-
# Represents response from i3 as object tree.
|
7
|
-
#
|
8
6
|
# Able to parse Numeric, String, TrueClass, FalseClass,
|
9
|
-
# Array, Hash from passed
|
7
|
+
# Array, Hash from passed string in json format.
|
10
8
|
#
|
11
|
-
#
|
9
|
+
# @example
|
12
10
|
# response = Reply.parse(
|
13
11
|
# %Q{
|
14
12
|
# {
|
@@ -40,17 +38,18 @@ module I3Ipc
|
|
40
38
|
|
41
39
|
# Parses response from I3-ipc protocol.
|
42
40
|
#
|
43
|
-
#
|
41
|
+
# @param [String] response response from i3 in json format.
|
44
42
|
#
|
45
|
-
#
|
43
|
+
# @return [Reply] object with dynamically accessed values.
|
46
44
|
def self.parse(response)
|
47
45
|
parse_data JSON.parse(response)
|
48
46
|
end
|
49
47
|
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
48
|
+
# Indicates whether this reply success or not.
|
49
|
+
#
|
50
|
+
# @return false if this reply represents and error from i3-ipc protocol.
|
51
|
+
# Otherwise returns true, which means that request is successful and
|
52
|
+
# reply has some data.
|
54
53
|
def success?
|
55
54
|
not self.respond_to? :error
|
56
55
|
end
|
data/lib/i3ipc/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: i3ipc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vitalii Elengaupt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -101,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
101
|
version: '0'
|
102
102
|
requirements: []
|
103
103
|
rubyforge_project:
|
104
|
-
rubygems_version: 2.4.
|
104
|
+
rubygems_version: 2.4.8
|
105
105
|
signing_key:
|
106
106
|
specification_version: 4
|
107
107
|
summary: Interprocess communication with i3 wm
|