i3-ipc 0.0.2
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/LICENSE +20 -0
- data/README.markdown +45 -0
- data/Rakefile +50 -0
- data/bin/i3-ipc +5 -0
- data/lib/i3-ipc.rb +93 -0
- data/lib/i3-ipc/manpage.rb +90 -0
- data/lib/i3-ipc/runner.rb +76 -0
- data/lib/i3-ipc/standalone.rb +53 -0
- data/lib/i3-ipc/version.rb +3 -0
- metadata +70 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jan-Erik Rediger
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
i3-ipc
|
2
|
+
======
|
3
|
+
|
4
|
+
inter-process communication with [i3][], the improved tiling window manager.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
RubyGem:
|
10
|
+
|
11
|
+
gem install i3-ipc
|
12
|
+
|
13
|
+
Old school:
|
14
|
+
|
15
|
+
curl -s http://github.com/badboy/i3-ipc/raw/master/i3-ipc > i3-ipc &&
|
16
|
+
chmod 755 i3-ipc &&
|
17
|
+
mv i3-ipc /usr/local/bin/i3-ipc
|
18
|
+
|
19
|
+
Use
|
20
|
+
---
|
21
|
+
|
22
|
+
i3-ipc -t 1
|
23
|
+
i3-ipc -t 1 -p
|
24
|
+
i3-ipc -t 1 -j
|
25
|
+
i3-ipc "exec xterm"
|
26
|
+
|
27
|
+
Contributing
|
28
|
+
------------
|
29
|
+
|
30
|
+
Once you've made your great commits:
|
31
|
+
|
32
|
+
1. [Fork][0] the project.
|
33
|
+
2. Create a topic branch - `git checkout -b my_branch`
|
34
|
+
3. Push to your branch - `git push origin my_branch`
|
35
|
+
4. Create an [Issue][1] with a link to your branch
|
36
|
+
5. That's it!
|
37
|
+
|
38
|
+
Copyright
|
39
|
+
---------
|
40
|
+
|
41
|
+
Copyright (c) 2010 Jan-Erik Rediger. See LICENSE for details.
|
42
|
+
|
43
|
+
[i3]: http://i3.zekjur.net/
|
44
|
+
[0]: http://help.github.com/forking/
|
45
|
+
[1]: http://github.com/badboy/i3-ipc/issues
|
data/Rakefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
begin
|
2
|
+
require 'mg'
|
3
|
+
MG.new("i3-ipc.gemspec")
|
4
|
+
rescue LoadError
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Build standalone script"
|
9
|
+
task :build => [ :standalone, :build_man ]
|
10
|
+
|
11
|
+
desc "Build standalone script"
|
12
|
+
task :standalone => :load_i3_ipc do
|
13
|
+
require 'i3-ipc/standalone'
|
14
|
+
I3::Standalone.save('i3-ipc')
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Build i3-ipc manual"
|
18
|
+
task :build_man do
|
19
|
+
sh "ronn -br5 --organization=badboy --manual='i3-ipc Manual' man/*.ronn"
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Show i3-ipc manual"
|
23
|
+
task :man => :build_man do
|
24
|
+
exec "man man/i3-ipc.1"
|
25
|
+
end
|
26
|
+
|
27
|
+
task :load_i3_ipc do
|
28
|
+
$LOAD_PATH.unshift 'lib'
|
29
|
+
require 'i3-ipc'
|
30
|
+
end
|
31
|
+
|
32
|
+
Rake::TaskManager.class_eval do
|
33
|
+
def remove_task(task_name)
|
34
|
+
@tasks.delete(task_name.to_s)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Remove mg's install task
|
39
|
+
Rake.application.remove_task(:install)
|
40
|
+
|
41
|
+
desc "Install standalone script and man pages"
|
42
|
+
task :install => :standalone do
|
43
|
+
prefix = ENV['PREFIX'] || ENV['prefix'] || '/usr/local'
|
44
|
+
|
45
|
+
FileUtils.mkdir_p "#{prefix}/bin"
|
46
|
+
FileUtils.cp "i3-ipc", "#{prefix}/bin"
|
47
|
+
|
48
|
+
FileUtils.mkdir_p "#{prefix}/share/man/man1"
|
49
|
+
FileUtils.cp "man/i3-ipc.1", "#{prefix}/share/man/man1"
|
50
|
+
end
|
data/bin/i3-ipc
ADDED
data/lib/i3-ipc.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'json'
|
3
|
+
require 'i3-ipc/runner'
|
4
|
+
require 'i3-ipc/manpage'
|
5
|
+
require 'i3-ipc/version'
|
6
|
+
|
7
|
+
module I3
|
8
|
+
class IPC
|
9
|
+
MAGIC_STRING = "i3-ipc"
|
10
|
+
|
11
|
+
class WrongAnswer < RuntimeError; end # :nodoc:
|
12
|
+
class WrongType < RuntimeError; end # :nodoc:
|
13
|
+
|
14
|
+
# connects to the given i3 ipc interface
|
15
|
+
# @param socket_path String the path to i3's socket
|
16
|
+
# @param force_connect Boolean connects to the socket if true
|
17
|
+
def initialize(socket_path="/tmp/i3-ipc.sock", force_connect=false)
|
18
|
+
@socket_path = socket_path
|
19
|
+
connect if connect
|
20
|
+
end
|
21
|
+
|
22
|
+
# send a message to i3
|
23
|
+
#
|
24
|
+
# the message is a command for i3
|
25
|
+
# (like the commands you can bind to keys in the configuration file)
|
26
|
+
# and will be executed directly after receiving it.
|
27
|
+
#
|
28
|
+
# returns { "success" => true }
|
29
|
+
def message(payload)
|
30
|
+
write format(0, payload)
|
31
|
+
handle_response 0
|
32
|
+
end
|
33
|
+
|
34
|
+
# gets the current workspaces.
|
35
|
+
# the reply will be the JSON-encoded list of workspaces
|
36
|
+
# (see the reply section of i3 docu)
|
37
|
+
def get_workspace
|
38
|
+
write format(1)
|
39
|
+
handle_response 1
|
40
|
+
end
|
41
|
+
|
42
|
+
# reads the reply from the socket
|
43
|
+
# and parse the returned json into a ruby object
|
44
|
+
#
|
45
|
+
# throws WrongAnswer when magic word is wrong
|
46
|
+
# throws WrongType if returned type does not match expected
|
47
|
+
def handle_response(type)
|
48
|
+
# reads 14 bytes
|
49
|
+
# length of "i3-ipc" + 4 bytes length + 4 bytes type
|
50
|
+
buffer = @socket.read 14
|
51
|
+
raise WrongAnswer unless buffer[0, ("i3-ipc".length)]
|
52
|
+
|
53
|
+
len, recv_type = buffer[6..-1].unpack("LL")
|
54
|
+
raise WrongType unless recv_type == type
|
55
|
+
|
56
|
+
answer = @socket.read(len)
|
57
|
+
::JSON.parse(answer)
|
58
|
+
end
|
59
|
+
|
60
|
+
# format the message
|
61
|
+
# a typical message looks like
|
62
|
+
# "i3-ipc" <message length> <message type> <payload>
|
63
|
+
def format(type, payload=nil)
|
64
|
+
size = payload ? payload.to_s.bytes.count : 0
|
65
|
+
msg = "i3-ipc%s" % [size, type].pack("LL")
|
66
|
+
msg << payload.to_s if payload
|
67
|
+
msg
|
68
|
+
end
|
69
|
+
|
70
|
+
# writes message to the socket
|
71
|
+
# if socket is not connected, it calls conenct
|
72
|
+
def write(msg)
|
73
|
+
connect if @socket.nil? || closed?
|
74
|
+
@last_write_length = @socket.write msg
|
75
|
+
end
|
76
|
+
|
77
|
+
# connects to the given socket
|
78
|
+
def connect
|
79
|
+
@socket = UNIXSocket.new(@socket_path)
|
80
|
+
end
|
81
|
+
|
82
|
+
# closes the socket connection
|
83
|
+
def close
|
84
|
+
@socket.close
|
85
|
+
end
|
86
|
+
|
87
|
+
# alias for @socket.closed? for easy access
|
88
|
+
def closed?
|
89
|
+
@socket.closed?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module I3
|
2
|
+
module Manpage
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# Prints a manpage, all pretty and paged.
|
6
|
+
def display(name)
|
7
|
+
puts manpage(name)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns the terminal-formatted manpage, ready to be printed to
|
11
|
+
# the screen.
|
12
|
+
def manpage(name)
|
13
|
+
return "** Can't find groff(1)" unless groff?
|
14
|
+
|
15
|
+
require 'open3'
|
16
|
+
out = nil
|
17
|
+
Open3.popen3(groff_command) do |stdin, stdout, _|
|
18
|
+
stdin.puts raw_manpage(name)
|
19
|
+
stdin.close
|
20
|
+
out = stdout.read.strip
|
21
|
+
end
|
22
|
+
out
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the raw manpage. If we're not running in standalone
|
26
|
+
# mode, it's a file sitting at the root under the `man`
|
27
|
+
# directory.
|
28
|
+
#
|
29
|
+
# If we are running in standalone mode the manpage will be
|
30
|
+
# included after the __END__ of the file so we can grab it using
|
31
|
+
# DATA.
|
32
|
+
def raw_manpage(name)
|
33
|
+
if File.exists? file = File.dirname(__FILE__) + "/../../man/#{name}.1"
|
34
|
+
File.read(file)
|
35
|
+
else
|
36
|
+
DATA.read
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns true if groff is installed and in our path, false if
|
41
|
+
# not.
|
42
|
+
def groff?
|
43
|
+
system("which groff > /dev/null")
|
44
|
+
end
|
45
|
+
|
46
|
+
# The groff command complete with crazy arguments we need to run
|
47
|
+
# in order to turn our raw roff (manpage markup) into something
|
48
|
+
# readable on the terminal.
|
49
|
+
def groff_command
|
50
|
+
"groff -Wall -mtty-char -mandoc -Tascii"
|
51
|
+
end
|
52
|
+
|
53
|
+
# All calls to `puts` are paged, git-style.
|
54
|
+
def puts(*args)
|
55
|
+
page_stdout
|
56
|
+
super
|
57
|
+
end
|
58
|
+
|
59
|
+
# http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby
|
60
|
+
def page_stdout
|
61
|
+
return unless $stdout.tty?
|
62
|
+
|
63
|
+
read, write = IO.pipe
|
64
|
+
|
65
|
+
if Kernel.fork
|
66
|
+
# Parent process, become pager
|
67
|
+
$stdin.reopen(read)
|
68
|
+
read.close
|
69
|
+
write.close
|
70
|
+
|
71
|
+
# Don't page if the input is short enough
|
72
|
+
ENV['LESS'] = 'FSRX'
|
73
|
+
|
74
|
+
# Wait until we have input before we start the pager
|
75
|
+
Kernel.select [STDIN]
|
76
|
+
|
77
|
+
pager = ENV['PAGER'] || 'less -isr'
|
78
|
+
pager = 'cat' if pager.empty?
|
79
|
+
|
80
|
+
exec pager rescue exec "/bin/sh", "-c", pager
|
81
|
+
else
|
82
|
+
# Child process
|
83
|
+
$stdout.reopen(write)
|
84
|
+
$stderr.reopen(write) if $stderr.tty?
|
85
|
+
read.close
|
86
|
+
write.close
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module I3
|
4
|
+
module Runner
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def execute(*args)
|
8
|
+
socket_file = '/tmp/i3-ipc.sock'
|
9
|
+
type = 0
|
10
|
+
quiet = false
|
11
|
+
output = :default
|
12
|
+
|
13
|
+
opts = OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: i3-ipc [options] [message]"
|
15
|
+
|
16
|
+
s_desc = 'Set socket file, defaults to /tmp/i3-ipc.sock'
|
17
|
+
opts.on('-s', '--socket', s_desc) do |s|
|
18
|
+
socket_file = s
|
19
|
+
end
|
20
|
+
|
21
|
+
t_desc = 'Set type, 0 = command, 1 = workspace list, defaults to 0'
|
22
|
+
opts.on('-tTYPE', '--type TYPE', Integer, t_desc) do |t|
|
23
|
+
type = t
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on('-p', '--pretty-print', 'Pretty print reply') do |p|
|
27
|
+
output = :pretty_print
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-j', '--json', 'Output raw json') do |p|
|
31
|
+
output = :json
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on('-q', '--quiet', %(Don't show reply)) do |q|
|
35
|
+
quiet = q
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on('-m', '--man', 'Print manual') do
|
39
|
+
I3::Manpage.display("i3-ipc")
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('-h', '--help', 'Display this screen') do
|
43
|
+
puts opts
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.parse!(args)
|
49
|
+
|
50
|
+
s = I3::IPC.new(socket_file)
|
51
|
+
|
52
|
+
if type == 0
|
53
|
+
if args.empty?
|
54
|
+
abort "error: message type needs a message"
|
55
|
+
end
|
56
|
+
|
57
|
+
payload = args.shift
|
58
|
+
|
59
|
+
puts s.message(payload) unless quiet
|
60
|
+
elsif type == 1
|
61
|
+
workspaces = s.get_workspace
|
62
|
+
if output == :pretty_print
|
63
|
+
require 'pp'
|
64
|
+
pp workspaces
|
65
|
+
elsif output == :json
|
66
|
+
require 'json'
|
67
|
+
puts workspaces.to_json
|
68
|
+
else
|
69
|
+
p workspaces
|
70
|
+
end
|
71
|
+
else
|
72
|
+
abort "error: type #{type} not yet implemented"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module I3
|
2
|
+
module Standalone
|
3
|
+
extend self
|
4
|
+
|
5
|
+
PREAMBLE = <<-preamble
|
6
|
+
#!/usr/bin/env ruby
|
7
|
+
#
|
8
|
+
# This file, gist, is generated code.
|
9
|
+
# Please DO NOT EDIT or send patches for it.
|
10
|
+
#
|
11
|
+
# Please take a look at the source from
|
12
|
+
# http://github.com/badboy/i3-ipc
|
13
|
+
# and submit patches against the individual files
|
14
|
+
# that build gist.
|
15
|
+
#
|
16
|
+
|
17
|
+
preamble
|
18
|
+
|
19
|
+
POSTAMBLE = "I3::Runner.execute(*ARGV)\n"
|
20
|
+
__DIR__ = File.dirname(__FILE__)
|
21
|
+
MANPAGE = "__END__\n#{File.read(__DIR__ + '/../../man/i3-ipc.1')}"
|
22
|
+
|
23
|
+
def save(filename, path = '.')
|
24
|
+
target = File.join(File.expand_path(path), filename)
|
25
|
+
File.open(target, 'w') do |f|
|
26
|
+
f.puts build
|
27
|
+
f.chmod 0755
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def build
|
32
|
+
root = File.dirname(__FILE__)
|
33
|
+
|
34
|
+
standalone = ''
|
35
|
+
standalone << PREAMBLE
|
36
|
+
|
37
|
+
Dir["#{root}/../**/*.rb"].each do |file|
|
38
|
+
# skip standalone.rb
|
39
|
+
next if File.expand_path(file) == File.expand_path(__FILE__)
|
40
|
+
|
41
|
+
File.readlines(file).each do |line|
|
42
|
+
next if line =~ /^\s*#/
|
43
|
+
next if line =~ /^require 'i3-ipc/
|
44
|
+
standalone << line
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
standalone << POSTAMBLE
|
49
|
+
standalone << MANPAGE
|
50
|
+
standalone
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: i3-ipc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jan-Erik Rediger
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-12 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
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
|
+
email: badboy@archlinux.us
|
23
|
+
executables:
|
24
|
+
- i3-ipc
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- README.markdown
|
31
|
+
- Rakefile
|
32
|
+
- LICENSE
|
33
|
+
- lib/i3-ipc/manpage.rb
|
34
|
+
- lib/i3-ipc/standalone.rb
|
35
|
+
- lib/i3-ipc/version.rb
|
36
|
+
- lib/i3-ipc/runner.rb
|
37
|
+
- lib/i3-ipc.rb
|
38
|
+
- bin/i3-ipc
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/badboy/i3-ipc
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.3.6
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: inter-process communication with i3, the improved tiling window manager.
|
69
|
+
test_files: []
|
70
|
+
|