i3-ipc 0.0.2 → 0.1.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.
- data/README.markdown +68 -5
- data/Rakefile +55 -14
- data/lib/i3-ipc.rb +64 -16
- data/lib/i3-ipc/standalone.rb +9 -3
- data/lib/i3-ipc/subscription.rb +36 -0
- data/lib/i3-ipc/version.rb +1 -1
- metadata +19 -5
data/README.markdown
CHANGED
@@ -10,7 +10,7 @@ RubyGem:
|
|
10
10
|
|
11
11
|
gem install i3-ipc
|
12
12
|
|
13
|
-
Old school:
|
13
|
+
Old school (for the cli script only):
|
14
14
|
|
15
15
|
curl -s http://github.com/badboy/i3-ipc/raw/master/i3-ipc > i3-ipc &&
|
16
16
|
chmod 755 i3-ipc &&
|
@@ -24,15 +24,75 @@ Use
|
|
24
24
|
i3-ipc -t 1 -j
|
25
25
|
i3-ipc "exec xterm"
|
26
26
|
|
27
|
+
Read the [man-page]() for more information.
|
28
|
+
|
29
|
+
Subscribing
|
30
|
+
-----------
|
31
|
+
|
32
|
+
As of commit [3db4890]() i3 added events.
|
33
|
+
For now there's only one event: `workspace`.
|
34
|
+
|
35
|
+
According to the documentation:
|
36
|
+
> This event is sent when the user switches to a different workspace, when a new workspace is initialized or when a workspace is removed (because the last client vanished).
|
37
|
+
|
38
|
+
i3-ipc uses [EventMachine][em] to receive and handle these events.
|
39
|
+
|
40
|
+
With `i3-ipc`'s interface and EventMachine as its backend it's rather easy to subscribe to this event notifying:
|
41
|
+
|
42
|
+
I3::IPC.subscribe [:workspace] do |em, type, data|
|
43
|
+
# ...
|
44
|
+
end
|
45
|
+
|
46
|
+
There are 3 arguments passed to the block:
|
47
|
+
|
48
|
+
* `em` is the instance of the EM::Connection class.
|
49
|
+
To send data to the socket, you need to use `em.send_data`.
|
50
|
+
* `type` is the received message type.
|
51
|
+
This could be one of
|
52
|
+
* MESSAGE_REPLY_COMMAND
|
53
|
+
* MESSAGE_REPLY_COMMAND
|
54
|
+
* MESSAGE_REPLY_COMMAND
|
55
|
+
* EVENT_WORKSPACE #_
|
56
|
+
* `data` is the received data, already parsed.
|
57
|
+
|
58
|
+
For example you can use the following code to get the actual focused screen:
|
59
|
+
|
60
|
+
I3::IPC.subscribe [:workspace] do |em, type, data|
|
61
|
+
case type
|
62
|
+
when I3::IPC::MESSAGE_REPLY_GET_WORKSPACES
|
63
|
+
data.each do |e|
|
64
|
+
if e["focused"]
|
65
|
+
puts "focused: %s" % e["name"]
|
66
|
+
else
|
67
|
+
puts "unfocused: %s" % e["name"]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
when I3::IPC::EVENT_WORKSPACE
|
71
|
+
em.send_data I3::IPC.format(I3::IPC::MESSAGE_TYPE_GET_WORKSPACES)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
A full example of how this can be used for the workspace bar can be found in the `examples` directory.
|
76
|
+
|
77
|
+
You can use `EM.stop` to stop the connection.
|
78
|
+
|
79
|
+
|
80
|
+
What needs to be done?
|
81
|
+
----------------------
|
82
|
+
|
83
|
+
* cleanup the subscribtion frontend
|
84
|
+
* write tests
|
85
|
+
* …
|
86
|
+
|
27
87
|
Contributing
|
28
88
|
------------
|
29
89
|
|
30
90
|
Once you've made your great commits:
|
31
91
|
|
32
|
-
1. [Fork]
|
92
|
+
1. [Fork]() the project.
|
33
93
|
2. Create a topic branch - `git checkout -b my_branch`
|
34
94
|
3. Push to your branch - `git push origin my_branch`
|
35
|
-
4. Create an [Issue]
|
95
|
+
4. Create an [Issue]() with a link to your branch
|
36
96
|
5. That's it!
|
37
97
|
|
38
98
|
Copyright
|
@@ -41,5 +101,8 @@ Copyright
|
|
41
101
|
Copyright (c) 2010 Jan-Erik Rediger. See LICENSE for details.
|
42
102
|
|
43
103
|
[i3]: http://i3.zekjur.net/
|
44
|
-
[
|
45
|
-
[
|
104
|
+
[manpage]: http://badboy.github.com/i3-ipc/
|
105
|
+
[3db4890]: http://code.stapelberg.de/git/i3/commit/?h=next&id=3db4890683e87
|
106
|
+
[em]: http://github.com/eventmachine/eventmachine
|
107
|
+
[fork]: http://help.github.com/forking/
|
108
|
+
[issue]: http://github.com/badboy/i3-ipc/issues
|
data/Rakefile
CHANGED
@@ -1,27 +1,43 @@
|
|
1
1
|
begin
|
2
2
|
require 'mg'
|
3
3
|
MG.new("i3-ipc.gemspec")
|
4
|
+
|
5
|
+
desc "Build a gem."
|
6
|
+
task :gem => :package
|
7
|
+
|
8
|
+
desc "Push a new version to Gemcutter and publish docs."
|
9
|
+
task :publish => :gemcutter do
|
10
|
+
require File.dirname(__FILE__) + '/lib/i3-ipc/version'
|
11
|
+
|
12
|
+
system "git tag v#{I3::Version}"
|
13
|
+
sh "git push origin master --tags"
|
14
|
+
sh "git clean -fd"
|
15
|
+
exec "rake pages"
|
16
|
+
end
|
4
17
|
rescue LoadError
|
5
|
-
|
18
|
+
warn "mg not available."
|
19
|
+
warn "Install it with: gem i mg"
|
6
20
|
end
|
7
21
|
|
8
22
|
desc "Build standalone script"
|
9
|
-
task :build => [ :standalone, :
|
23
|
+
task :build => [ "build:standalone", "build:man" ]
|
10
24
|
|
11
|
-
desc "
|
12
|
-
task :
|
13
|
-
|
14
|
-
I3::Standalone.save('i3-ipc')
|
25
|
+
desc "Show i3-ipc manual"
|
26
|
+
task :man => "build:man" do
|
27
|
+
exec "man man/i3-ipc.1"
|
15
28
|
end
|
16
29
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
30
|
+
namespace :build do
|
31
|
+
desc "Build i3-ipc manual"
|
32
|
+
task :man do
|
33
|
+
sh "ronn -br5 --organization=badboy --manual='i3-ipc Manual' man/*.ronn"
|
34
|
+
end
|
21
35
|
|
22
|
-
desc "
|
23
|
-
task :
|
24
|
-
|
36
|
+
desc "Build standalone script"
|
37
|
+
task :standalone => :load_i3_ipc do
|
38
|
+
require 'i3-ipc/standalone'
|
39
|
+
I3::Standalone.save('i3-ipc')
|
40
|
+
end
|
25
41
|
end
|
26
42
|
|
27
43
|
task :load_i3_ipc do
|
@@ -39,7 +55,7 @@ end
|
|
39
55
|
Rake.application.remove_task(:install)
|
40
56
|
|
41
57
|
desc "Install standalone script and man pages"
|
42
|
-
task :install => :standalone do
|
58
|
+
task :install => "build:standalone" do
|
43
59
|
prefix = ENV['PREFIX'] || ENV['prefix'] || '/usr/local'
|
44
60
|
|
45
61
|
FileUtils.mkdir_p "#{prefix}/bin"
|
@@ -48,3 +64,28 @@ task :install => :standalone do
|
|
48
64
|
FileUtils.mkdir_p "#{prefix}/share/man/man1"
|
49
65
|
FileUtils.cp "man/i3-ipc.1", "#{prefix}/share/man/man1"
|
50
66
|
end
|
67
|
+
|
68
|
+
desc "Publish to GitHub Pages"
|
69
|
+
task :pages => [ "build:man" ] do
|
70
|
+
Dir['man/*.html'].each do |f|
|
71
|
+
cp f, File.basename(f).sub('.html', '.newhtml')
|
72
|
+
end
|
73
|
+
|
74
|
+
`git commit -am 'generated manual'`
|
75
|
+
`git checkout gh-pages`
|
76
|
+
|
77
|
+
Dir['*.newhtml'].each do |f|
|
78
|
+
mv f, "index.html"
|
79
|
+
end
|
80
|
+
|
81
|
+
`git add .`
|
82
|
+
`git commit -m updated`
|
83
|
+
`git push origin gh-pages`
|
84
|
+
`git checkout master`
|
85
|
+
puts :done
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "fire up IRB console"
|
89
|
+
task :console do
|
90
|
+
exec "irb -Ilib -ri3-ipc"
|
91
|
+
end
|
data/lib/i3-ipc.rb
CHANGED
@@ -1,54 +1,77 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'json'
|
3
3
|
require 'i3-ipc/runner'
|
4
|
+
require 'i3-ipc/subscription'
|
4
5
|
require 'i3-ipc/manpage'
|
5
6
|
require 'i3-ipc/version'
|
6
7
|
|
7
8
|
module I3
|
8
9
|
class IPC
|
9
10
|
MAGIC_STRING = "i3-ipc"
|
11
|
+
SOCKET_PATH = "/tmp/i3-ipc.sock"
|
10
12
|
|
11
|
-
|
13
|
+
MESSAGE_TYPE_COMMAND = 0
|
14
|
+
MESSAGE_TYPE_GET_WORKSPACES = 1
|
15
|
+
MESSAGE_TYPE_SUBSCRIBE = 2
|
16
|
+
|
17
|
+
MESSAGE_REPLY_COMMAND = 0
|
18
|
+
MESSAGE_REPLY_GET_WORKSPACES = 1
|
19
|
+
MESSAGE_REPLY_SUBSCRIBE = 2
|
20
|
+
|
21
|
+
EVENT_MASK = (1 << 31)
|
22
|
+
EVENT_WORKSPACE = (EVENT_MASK | 0)
|
23
|
+
|
24
|
+
class WrongMagicCode < RuntimeError; end # :nodoc:
|
12
25
|
class WrongType < RuntimeError; end # :nodoc:
|
13
26
|
|
14
27
|
# connects to the given i3 ipc interface
|
15
28
|
# @param socket_path String the path to i3's socket
|
16
29
|
# @param force_connect Boolean connects to the socket if true
|
17
|
-
def initialize(socket_path=
|
30
|
+
def initialize(socket_path=SOCKET_PATH, force_connect=false)
|
18
31
|
@socket_path = socket_path
|
19
32
|
connect if connect
|
20
33
|
end
|
21
34
|
|
22
|
-
#
|
35
|
+
# shortcut
|
36
|
+
def self.subscribe(list, socket_path=SOCKET_PATH, &blk)
|
37
|
+
Subscription.subscribe(list, socket_path, &blk)
|
38
|
+
end
|
39
|
+
|
40
|
+
# send a command to i3
|
23
41
|
#
|
24
|
-
# the
|
42
|
+
# the payload is a command for i3
|
25
43
|
# (like the commands you can bind to keys in the configuration file)
|
26
44
|
# and will be executed directly after receiving it.
|
27
45
|
#
|
28
|
-
# returns { "success" => true }
|
29
|
-
|
30
|
-
|
31
|
-
|
46
|
+
# returns { "success" => true } for now.
|
47
|
+
# i3 does send this reply without checks
|
48
|
+
def command(payload)
|
49
|
+
write format(MESSAGE_TYPE_COMMAND, payload)
|
50
|
+
handle_response MESSAGE_TYPE_COMMAND
|
32
51
|
end
|
33
52
|
|
34
53
|
# gets the current workspaces.
|
35
|
-
# the reply will be the
|
54
|
+
# the reply will be the list of workspaces
|
36
55
|
# (see the reply section of i3 docu)
|
37
56
|
def get_workspace
|
38
|
-
write format(
|
39
|
-
handle_response
|
57
|
+
write format(MESSAGE_TYPE_GET_WORKSPACES)
|
58
|
+
handle_response MESSAGE_TYPE_GET_WORKSPACES
|
40
59
|
end
|
41
60
|
|
42
61
|
# reads the reply from the socket
|
43
62
|
# and parse the returned json into a ruby object
|
44
63
|
#
|
45
|
-
# throws
|
64
|
+
# throws WrongMagicCode when magic word is wrong
|
46
65
|
# throws WrongType if returned type does not match expected
|
66
|
+
#
|
67
|
+
# this is a bit duplicated code
|
68
|
+
# but I don't know a way to read the full send reply
|
69
|
+
# without knowing its length
|
47
70
|
def handle_response(type)
|
48
71
|
# reads 14 bytes
|
49
72
|
# length of "i3-ipc" + 4 bytes length + 4 bytes type
|
50
73
|
buffer = @socket.read 14
|
51
|
-
raise
|
74
|
+
raise WrongMagicCode unless buffer[0, (MAGIC_STRING.length)] == MAGIC_STRING
|
52
75
|
|
53
76
|
len, recv_type = buffer[6..-1].unpack("LL")
|
54
77
|
raise WrongType unless recv_type == type
|
@@ -60,15 +83,40 @@ module I3
|
|
60
83
|
# format the message
|
61
84
|
# a typical message looks like
|
62
85
|
# "i3-ipc" <message length> <message type> <payload>
|
63
|
-
def format(type, payload=nil)
|
86
|
+
def self.format(type, payload=nil)
|
64
87
|
size = payload ? payload.to_s.bytes.count : 0
|
65
|
-
msg =
|
88
|
+
msg = MAGIC_STRING + [size, type].pack("LL")
|
66
89
|
msg << payload.to_s if payload
|
67
90
|
msg
|
68
91
|
end
|
69
92
|
|
93
|
+
def format(type, payload=nil)
|
94
|
+
self.class.format(type, payload)
|
95
|
+
end
|
96
|
+
|
97
|
+
# parse a full ipc response
|
98
|
+
# similar to handle_response,
|
99
|
+
# but parses full reply as received by EventMachine
|
100
|
+
#
|
101
|
+
# returns an Array containing the
|
102
|
+
# reply type and the parsed data
|
103
|
+
def self.parse_response(response)
|
104
|
+
if response[0, (MAGIC_STRING.length)] != MAGIC_STRING
|
105
|
+
raise WrongMagicCode
|
106
|
+
end
|
107
|
+
|
108
|
+
len, recv_type = response[6, 8].unpack("LL")
|
109
|
+
|
110
|
+
answer = response[14, len]
|
111
|
+
[recv_type, ::JSON.parse(answer)]
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_response(response)
|
115
|
+
self.class.parse_response(response)
|
116
|
+
end
|
117
|
+
|
70
118
|
# writes message to the socket
|
71
|
-
# if socket is not connected, it
|
119
|
+
# if socket is not connected, it connects first
|
72
120
|
def write(msg)
|
73
121
|
connect if @socket.nil? || closed?
|
74
122
|
@last_write_length = @socket.write msg
|
data/lib/i3-ipc/standalone.rb
CHANGED
@@ -5,13 +5,13 @@ module I3
|
|
5
5
|
PREAMBLE = <<-preamble
|
6
6
|
#!/usr/bin/env ruby
|
7
7
|
#
|
8
|
-
# This file,
|
8
|
+
# This file, i3-ipc, is generated code.
|
9
9
|
# Please DO NOT EDIT or send patches for it.
|
10
10
|
#
|
11
11
|
# Please take a look at the source from
|
12
12
|
# http://github.com/badboy/i3-ipc
|
13
13
|
# and submit patches against the individual files
|
14
|
-
# that build
|
14
|
+
# that build i3-ipc.
|
15
15
|
#
|
16
16
|
|
17
17
|
preamble
|
@@ -33,10 +33,16 @@ preamble
|
|
33
33
|
|
34
34
|
standalone = ''
|
35
35
|
standalone << PREAMBLE
|
36
|
+
file_dir = File.expand_path(File.dirname(__FILE__))
|
37
|
+
exclude_files = %w(subscription.rb)
|
38
|
+
|
39
|
+
exclude_file_list = exclude_files.map { |file|
|
40
|
+
File.join(file_dir, file)
|
41
|
+
} + [File.expand_path(__FILE__)]
|
36
42
|
|
37
43
|
Dir["#{root}/../**/*.rb"].each do |file|
|
38
44
|
# skip standalone.rb
|
39
|
-
next if
|
45
|
+
next if exclude_file_list.include?(File.expand_path(file))
|
40
46
|
|
41
47
|
File.readlines(file).each do |line|
|
42
48
|
next if line =~ /^\s*#/
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module I3
|
4
|
+
module Subscription
|
5
|
+
extend self
|
6
|
+
|
7
|
+
class SubscriptionConnection < EM::Connection
|
8
|
+
def self.connect(subscription_list, socket_path=I3::IPC::SOCKET_PATH, &blk)
|
9
|
+
new_klass = Class.new(self)
|
10
|
+
new_klass.send(:define_method, :initialize) do
|
11
|
+
@subscription_list = subscription_list
|
12
|
+
@handler = blk
|
13
|
+
end
|
14
|
+
EM.connect socket_path, new_klass
|
15
|
+
end
|
16
|
+
|
17
|
+
# send subscription to i3
|
18
|
+
def post_init
|
19
|
+
send_data I3::IPC.format(I3::IPC::MESSAGE_TYPE_SUBSCRIBE,
|
20
|
+
@subscription_list.to_json)
|
21
|
+
end
|
22
|
+
|
23
|
+
# receive data, parse it and pass on to the user-defined handler
|
24
|
+
def receive_data(data)
|
25
|
+
@handler.call(self, *I3::IPC.parse_response(data)) if @handler
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def subscribe(subscription_list, socket_path=I3::IPC::SOCKET_PATH, &blk)
|
30
|
+
EM.run do
|
31
|
+
SubscriptionConnection.connect(subscription_list,
|
32
|
+
socket_path, &blk)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/i3-ipc/version.rb
CHANGED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.2
|
9
|
+
version: 0.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Jan-Erik Rediger
|
@@ -14,10 +14,23 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-03-
|
17
|
+
date: 2010-03-14 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: eventmachine
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 12
|
30
|
+
- 10
|
31
|
+
version: 0.12.10
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
21
34
|
description: " uses the ipc socket of i3 to send commands or get information directly from the window manager. Useful for scripting the window manager.'\n"
|
22
35
|
email: badboy@archlinux.us
|
23
36
|
executables:
|
@@ -30,6 +43,7 @@ files:
|
|
30
43
|
- README.markdown
|
31
44
|
- Rakefile
|
32
45
|
- LICENSE
|
46
|
+
- lib/i3-ipc/subscription.rb
|
33
47
|
- lib/i3-ipc/manpage.rb
|
34
48
|
- lib/i3-ipc/standalone.rb
|
35
49
|
- lib/i3-ipc/version.rb
|