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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e40ae80557aa74e152aa616ae813744d8b755586
4
- data.tar.gz: a5f833254c367b6d9bc4b4bdecd8303020388194
3
+ metadata.gz: d1656014a13ed6678e5efba71d74c72e7d910906
4
+ data.tar.gz: c41b56ab703cf2bc2322ed83218aa7f696e2552f
5
5
  SHA512:
6
- metadata.gz: 4af0b717ee2d06d19d858694305c21eef45c2ef0b1e74427acafd18c32c9db0835694c9c84fadc5456d1a55bb51a7b1e5664d58f50f564ba4aed75095a1769de
7
- data.tar.gz: 38fef1144589bacb4c4b7edcae7427f435aeaa95fca85a0b45789f4805f2e9b21deade91c6f396096bf4e201a8cd0603b0207e21dc25787f0a50f10113403208
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 implemennt external workspace bar in `Ruby` language.
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
- To be done (not implemented yet)
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
 
@@ -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
- # Usage example:
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
- connect && @protocol.connect
32
+ open
21
33
  end
22
34
 
23
- def connect
35
+ def open
24
36
  @protocol.connect
25
37
  end
26
38
 
27
- def disconnect
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
@@ -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
- # Usage example:
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 Socket::UNIXSocket.
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
- # Throws:
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
- # Throws:
80
- # * NotConnected if protocol is not connected.
81
- # * WrongMagicString if got message with not expected magic string.
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
- # +type+: expected type of the message.
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
- # +type+: type of the message
106
- # +payload+: patload of the message
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
- # +data+: data to be unpacked.
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]
@@ -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 JSON string.
7
+ # Array, Hash from passed string in json format.
10
8
  #
11
- # For example:
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
- # Returns Reply object with dynamically accessed values.
41
+ # @param [String] response response from i3 in json format.
44
42
  #
45
- # +response+: string, that represents response from i3 in json format.
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
- # Returns false if this reply represents and error
51
- # from i3-ipc protocol. Otherwise returns true, which
52
- # meens that request is successful and reply has some
53
- # data.
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
@@ -1,3 +1,3 @@
1
1
  module I3ipc
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
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.1.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-05-23 00:00:00.000000000 Z
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.6
104
+ rubygems_version: 2.4.8
105
105
  signing_key:
106
106
  specification_version: 4
107
107
  summary: Interprocess communication with i3 wm