consular-gnome-terminal 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.
@@ -0,0 +1,13 @@
1
+ = Terminator support for Consular
2
+
3
+ This gem is an add-on to Consular that implements support for the Terminator
4
+ terminal emulator on Linux.
5
+
6
+ Since Terminator does not have an API for controlling it, a commandline tool
7
+ called `xdotool` is used to send keyboard shortcuts to a running Terminator
8
+ in order to control it.
9
+
10
+ == Copyright
11
+
12
+ Copyright (c) 2011 Ilkka Laukkanen. See LICENSE.txt for further details.
13
+
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'rspec/core'
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.pattern = 'spec/**/*_spec.rb'
9
+ end
10
+
11
+ RSpec::Core::RakeTask.new(:rcov) do |t|
12
+ t.pattern = 'spec/**/*_spec.rb'
13
+ t.rcov = true
14
+ end
15
+
16
+ require 'cucumber/rake/task'
17
+ Cucumber::Rake::Task.new(:features)
18
+
19
+ require 'yard'
20
+ YARD::Rake::YardocTask.new
21
+
22
+ task :default => [:features, :spec]
23
+
@@ -0,0 +1,50 @@
1
+ # Gnome Terminal support for Consular
2
+ # Copyright (C) 2011 Jesse Cooke
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ $:.push File.expand_path("../lib", __FILE__)
18
+ require 'consular/gnome-terminal/version'
19
+
20
+ Gem::Specification.new do |s|
21
+ s.name = 'consular-gnome-terminal'
22
+ s.version = Consular::GnomeTerminal::VERSION
23
+ s.authors = ['Jesse Cooke']
24
+ s.email = [%q{jesse@jc00ke.com}]
25
+ s.homepage = %q{http://github.com/jc00ke/consular-gnome-terminal}
26
+ s.summary = %q{Gnome Terminal support for Consular}
27
+ s.licenses = ["GPLv3"]
28
+ s.description =
29
+ %q{Add support for automating the Gnome Terminal with Consular.}
30
+
31
+ s.files = `git ls-files`.split("\n")
32
+ s.test_files = `git ls-files -- test/* spec/* features/*`.split("\n")
33
+
34
+ s.extra_rdoc_files = [
35
+ 'LICENSE.txt',
36
+ 'README.rdoc'
37
+ ]
38
+ s.require_paths = ["lib"]
39
+
40
+ s.add_runtime_dependency('consular')
41
+
42
+ s.add_development_dependency('rake')
43
+ s.add_development_dependency('rspec')
44
+ s.add_development_dependency('yard')
45
+ s.add_development_dependency('cucumber')
46
+ s.add_development_dependency('spork')
47
+ s.add_development_dependency('watchr')
48
+ s.add_development_dependency('bundler')
49
+ s.add_development_dependency('mocha')
50
+ end
@@ -0,0 +1 @@
1
+ default: --color --drb --port 8990
@@ -0,0 +1,18 @@
1
+ require 'spork'
2
+
3
+ Spork.prefork do
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+
13
+ require 'rspec/expectations'
14
+ end
15
+
16
+ Spork.each_run do
17
+
18
+ end
@@ -0,0 +1,260 @@
1
+ require 'consular'
2
+ require 'rbconfig'
3
+
4
+ module Consular
5
+ # Consular core for interacting with GnomeTerminal.
6
+ #
7
+ # Since we don't have any real API to work with and just send
8
+ # keypresses via XTEST, we don't have tab references either.
9
+ # Instead we just count tabs and return tab indexes.
10
+ #
11
+ # Adapted from http://github.com/ikka/consular-terminator
12
+ class GnomeTerminal < Core
13
+
14
+ Consular.add_core self
15
+
16
+ class << self
17
+ # Check to see if current system is valid for this core
18
+ #
19
+ # @api public
20
+ def valid_system?
21
+ if (RbConfig::CONFIG['host_os'] =~ /linux/) != nil
22
+ if !(xdotool = `which xdotool`.chomp).empty?
23
+ begin
24
+ return File::Stat.new(xdotool).executable?
25
+ rescue
26
+ return false
27
+ end
28
+ end
29
+ end
30
+ return false
31
+ end
32
+ end
33
+
34
+ # Initialize
35
+ #
36
+ # @param [String] path
37
+ # path to Termfile.
38
+ #
39
+ # @api public
40
+ def initialize(path)
41
+ super
42
+ @tabidx = nil
43
+ end
44
+
45
+ # Method called by runner to Execute Termfile setup.
46
+ #
47
+ # @api public
48
+ def setup!
49
+ @termfile[:setup].each { |cmd| execute_command(cmd, :in => active_window) }
50
+ end
51
+
52
+ # Method called by runner to execute Termfile.
53
+ #
54
+ # @api public
55
+ def process!
56
+ windows = @termfile[:windows]
57
+ default = windows.delete('default')
58
+ execute_window(default, :default => true) unless default[:tabs].empty?
59
+ windows.each_pair { |_, cont| execute_window(cont) }
60
+ end
61
+
62
+ # Executes the commands for each designated window.
63
+ # .run_windows will iterate through each of the tabs in
64
+ # sorted order to execute the tabs in the order they were set.
65
+ # The logic follows this:
66
+ #
67
+ # If the content is for the 'default' window,
68
+ # then use the current active window and generate the commands.
69
+ #
70
+ # If the content is for a new window,
71
+ # then generate a new window and activate the windows.
72
+ #
73
+ # Otherwise, open a new tab and execute the commands.
74
+ #
75
+ # @param [Hash] content
76
+ # The hash contents of the window from the Termfile.
77
+ # @param [Hash] options
78
+ # Addional options to pass. You can use:
79
+ # :default - Whether this is being run as the default window.
80
+ #
81
+ # @example
82
+ # @core.execute_window contents, :default => true
83
+ # @core.execute_window contents, :default => true
84
+ #
85
+ # @api public
86
+ def execute_window(content, options = {})
87
+ window_options = content[:options]
88
+ _contents = content[:tabs]
89
+ _first_run = true
90
+
91
+ _contents.keys.sort.each do |key|
92
+ _content = _contents[key]
93
+ _options = content[:options]
94
+ _name = options[:name]
95
+
96
+ _tab =
97
+ if _first_run && !options[:default]
98
+ open_window options.merge(window_options)
99
+ else
100
+ key == 'default' ? nil : open_tab(_options)
101
+ end
102
+
103
+ _first_run = false
104
+ commands = prepend_befores _content[:commands], _contents[:befores]
105
+ commands = set_title _name, commands
106
+ commands.each { |cmd| execute_command cmd, :in => _tab }
107
+ end
108
+
109
+ end
110
+
111
+ # Prepend a title setting command prior to the other commands.
112
+ #
113
+ # @param [String] title
114
+ # The title to set for the context of the commands.
115
+ # @param [Array<String>] commands
116
+ # The context of commands to preprend to.
117
+ #
118
+ # @api public
119
+ def set_title(title, commands)
120
+ cmd = "PS1=\"$PS1\\[\\e]2;#{title}\\a\\]\""
121
+ title ? commands.insert(0, cmd) : commands
122
+ end
123
+
124
+ # Prepends the :before commands to the current context's
125
+ # commands if it exists.
126
+ #
127
+ # @param [Array<String>] commands
128
+ # The current tab commands
129
+ # @param [Array<String>] befores
130
+ # The current window's :befores
131
+ #
132
+ # @return [Array<String>]
133
+ # The current context commands with the :before commands prepended
134
+ #
135
+ # @api public
136
+ def prepend_befores(commands, befores = nil)
137
+ unless befores.nil? || befores.empty?
138
+ commands.insert(0, befores).flatten!
139
+ else
140
+ commands
141
+ end
142
+ end
143
+
144
+ # Execute the given command in the context of the
145
+ # active window.
146
+ #
147
+ # @param [String] cmd
148
+ # The command to execute.
149
+ # @param [Hash] options
150
+ # Additional options to pass into appscript for the context.
151
+ #
152
+ # @example
153
+ # @osx.execute_command 'ps aux', :in => @tab_object
154
+ #
155
+ # @api public
156
+ def execute_command(cmd, options = {})
157
+ run_in_active_gnome_terminal cmd, options
158
+ end
159
+
160
+ # Opens a new tab and return the last instantiated tab(itself).
161
+ #
162
+ # @param [Hash] options
163
+ # Options to further customize the window. You can use:
164
+ # :settings -
165
+ # :selected -
166
+ #
167
+ # @return
168
+ # Returns a refernce to the last instantiated tab of the
169
+ # window.
170
+ #
171
+ # @api public
172
+ def open_tab(options = nil)
173
+ send_keypress(active_gnome_terminal_window, "ctrl+shift+t")
174
+ # FIXME: horribly hacky but can't come up with any way
175
+ # to truly make sure tab has opened.
176
+ sleep 1
177
+ end
178
+
179
+ # Opens a new window and returns its
180
+ # last instantiated tab.(The first 'tab' in the window).
181
+ #
182
+ # @param [Hash] options
183
+ # Options to further customize the window. You can use:
184
+ # :bound - Set the bounds of the windows
185
+ # :visible - Set whether or not the current window is visible
186
+ # :miniaturized - Set whether or not the window is minimized
187
+ #
188
+ # @return
189
+ # Returns a refernce to the last instantiated tab of the
190
+ # window.
191
+ #
192
+ # @api public
193
+ def open_window(options = nil)
194
+ windowid_before = active_gnome_terminal_window
195
+ send_keypress(active_gnome_terminal_window, "ctrl+shift+i")
196
+ # wait for the active window to change -> new window opened
197
+ while active_gnome_terminal_window == windowid_before
198
+ sleep 1
199
+ end
200
+ return (@tabidx = 0)
201
+ end
202
+
203
+ private
204
+
205
+ # Send keypresses to window winid
206
+ #
207
+ # @api private
208
+ def send_keypress(winid, keys)
209
+ xdotool("windowfocus #{winid}")
210
+ xdotool("key #{keys}")
211
+ end
212
+
213
+ # Run command with active gnome-terminal
214
+ #
215
+ # @api private
216
+ def run_in_active_gnome_terminal(cmd, options = {})
217
+ type_in_window(active_gnome_terminal_window, "#{cmd}\n")
218
+ end
219
+
220
+ # Get active gnome-terminal window winid.
221
+ # Gets the active window and returns it if it is
222
+ # a GnomeTerminal window. If it is not a gnome-terminal window,
223
+ # the method gets all gnome-terminal windows and returns the
224
+ # first one. If no gnome-terminal windows exist, aborts.
225
+ #
226
+ # @api private
227
+ def active_gnome_terminal_window
228
+ active = xdotool("getactivewindow").chomp
229
+ if not all_gnome_terminal_windows.include? active
230
+ active = all_gnome_terminal_windows.first
231
+ end
232
+ if not active
233
+ abort("No Gnome Terminal windows found")
234
+ end
235
+ return active
236
+ end
237
+
238
+ # Get window IDs of all gnome-terminal windows.
239
+ #
240
+ # @api private
241
+ def all_gnome_terminal_windows
242
+ xdotool("search --onlyvisible --class gnome-terminal").split("\n")
243
+ end
244
+
245
+ # Type text in window winid.
246
+ #
247
+ # @api private
248
+ def type_in_window(winid, text)
249
+ xdotool("windowfocus #{winid}")
250
+ xdotool("type \"#{text}\"")
251
+ end
252
+
253
+ # Execute xdotool with the given args and return output
254
+ #
255
+ # @api private
256
+ def xdotool(cmd)
257
+ IO.popen("xdotool #{cmd}").read
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,16 @@
1
+ require 'consular'
2
+
3
+ module Consular
4
+ class GnomeTerminal < Core
5
+ module Version
6
+ MAJOR = 0
7
+ MINOR = 1
8
+ PATCH = 0
9
+ BUILD = nil
10
+ end
11
+
12
+ VERSION = [
13
+ Version::MAJOR, Version::MINOR, Version::PATCH, Version::BUILD
14
+ ].compact.join('.')
15
+ end
16
+ end
@@ -0,0 +1,56 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'rbconfig'
3
+
4
+ describe Consular::GnomeTerminal do
5
+ it 'should be added as a core to Consular' do
6
+ Consular.cores.should include Consular::GnomeTerminal
7
+ end
8
+
9
+ it 'should treat linux as a valid system' do
10
+ RbConfig::CONFIG.expects(:[]).with('host_os').returns('x86-linux')
11
+ Consular::GnomeTerminal.expects(:`).with('which xdotool').returns('/usr/bin/xdotool')
12
+ stat = mock()
13
+ stat.expects(:executable?).returns(true)
14
+ File::Stat.expects(:new).returns(stat)
15
+ Consular::GnomeTerminal.valid_system?.should == true
16
+ end
17
+
18
+ it 'should treat non-linux as an invalid system' do
19
+ RbConfig::CONFIG.expects(:[]).with('host_os').returns('darwin')
20
+ Consular::GnomeTerminal.valid_system?.should == false
21
+ end
22
+
23
+ it 'should treat systems without "which" as invalid' do
24
+ RbConfig::CONFIG.expects(:[]).with('host_os').returns('x86-linux')
25
+ Consular::GnomeTerminal.expects(:`).with('which xdotool').returns('which: command not found')
26
+ File::Stat.expects(:new).throws('no such file')
27
+ Consular::GnomeTerminal.valid_system?.should == false
28
+ end
29
+
30
+ it 'should treat systems without "xdotool" as invalid' do
31
+ RbConfig::CONFIG.expects(:[]).with('host_os').returns('x86-linux')
32
+ Consular::GnomeTerminal.expects(:`).with('which xdotool').returns('')
33
+ File::Stat.expects(:new).throws('no such file')
34
+ Consular::GnomeTerminal.valid_system?.should == false
35
+ end
36
+
37
+ it 'should send ctrl+shift+i when creating new window' do
38
+ f = EmptyTermfile.new
39
+ core = Consular::GnomeTerminal.new f.path
40
+ core.expects(:active_gnome_terminal_window).once.returns(1)
41
+ core.expects(:active_gnome_terminal_window).once.returns(1)
42
+ core.expects(:xdotool).with("windowfocus 1")
43
+ core.expects(:xdotool).with("key ctrl+shift+i")
44
+ core.expects(:active_gnome_terminal_window).once.returns(2)
45
+ core.open_window
46
+ end
47
+
48
+ it 'should send ctrl+shift+t when creating new tab' do
49
+ f = EmptyTermfile.new
50
+ core = Consular::GnomeTerminal.new f.path
51
+ core.expects(:active_gnome_terminal_window).returns(1)
52
+ core.expects(:xdotool).with("windowfocus 1")
53
+ core.expects(:xdotool).with("key ctrl+shift+t")
54
+ core.open_tab
55
+ end
56
+ end
@@ -0,0 +1,30 @@
1
+ require 'spork'
2
+
3
+ Spork.prefork do
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'rspec'
7
+ require 'mocha'
8
+
9
+ # Requires supporting files with custom matchers and macros, etc,
10
+ # in ./support/ and its subdirectories.
11
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
12
+
13
+ RSpec.configure do |config|
14
+ end
15
+
16
+ require 'tempfile'
17
+
18
+ class EmptyTermfile < Tempfile
19
+ def initialize
20
+ super('consular')
21
+ write(%q(setup "echo 'setup'"))
22
+ rewind
23
+ end
24
+ end
25
+ end
26
+
27
+ Spork.each_run do
28
+ require 'consular/gnome-terminal'
29
+ end
30
+