consular-gnome-terminal 0.1.0

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