i3ipc 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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