ruby-terminfo 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +3 -0
- data/README +80 -0
- data/Rakefile +143 -0
- data/extconf.rb +53 -0
- data/lib/terminfo.rb +140 -0
- data/terminfo.c +336 -0
- metadata +72 -0
data/ChangeLog
ADDED
data/README
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
= ruby-terminfo - terminfo binding for Ruby
|
2
|
+
|
3
|
+
== Author
|
4
|
+
|
5
|
+
Tanaka Akira <akr@fsij.org>
|
6
|
+
|
7
|
+
== Home Page
|
8
|
+
|
9
|
+
http://www.a-k-r.org/ruby-terminfo/
|
10
|
+
|
11
|
+
== Feature
|
12
|
+
|
13
|
+
* easy to use method, control, for combination of tigetstr/tparm/tputs.
|
14
|
+
* low-level terminfo binding (setupterm, tigetflag, tigetnum, tigetstr, tparm, tputs)
|
15
|
+
* TIOCGWINSZ/TIOCSWINSZ ioctl for screen size
|
16
|
+
|
17
|
+
== Usage
|
18
|
+
|
19
|
+
=== easy to use methods
|
20
|
+
|
21
|
+
require 'terminfo'
|
22
|
+
TermInfo.control("cuf", 7) # cursor forward 7 columns
|
23
|
+
p TermInfo.screen_size # use TIOCGWINSZ, LINES/COLUMNS env. or terminfo lines#/cols#
|
24
|
+
|
25
|
+
=== low level methods
|
26
|
+
|
27
|
+
require 'terminfo'
|
28
|
+
t = TermInfo.new(ENV["TERM"], STDOUT)
|
29
|
+
print t.tputs(t.tparm(t.tigetstr("cuf"), 7), 1) # cursor forward 7 columns
|
30
|
+
p TermInfo.tiocgwinsz(STDOUT) # use TIOCGWINSZ
|
31
|
+
|
32
|
+
== Requirements
|
33
|
+
|
34
|
+
* ruby : http://www.ruby-lang.org/
|
35
|
+
|
36
|
+
== Download
|
37
|
+
|
38
|
+
* latest release: http://www.a-k-r.org/ruby-terminfo/ruby-terminfo-0.1.tar.gz
|
39
|
+
|
40
|
+
* development version in Subversion repository:
|
41
|
+
|
42
|
+
% svn co svn://svn@svn.a-k-r.org/akr/ruby-terminfo/trunk ruby-terminfo
|
43
|
+
|
44
|
+
== Install
|
45
|
+
|
46
|
+
% ruby extconf.rb
|
47
|
+
% make
|
48
|
+
% make install
|
49
|
+
|
50
|
+
== Reference Manual
|
51
|
+
|
52
|
+
See rdoc/classes/TermInfo.html or
|
53
|
+
http://www.a-k-r.org/ruby-terminfo/rdoc/classes/TermInfo.html
|
54
|
+
|
55
|
+
== License
|
56
|
+
|
57
|
+
The modified BSD licence
|
58
|
+
|
59
|
+
Redistribution and use in source and binary forms, with or without
|
60
|
+
modification, are permitted provided that the following conditions are met:
|
61
|
+
|
62
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
63
|
+
list of conditions and the following disclaimer.
|
64
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
65
|
+
this list of conditions and the following disclaimer in the documentation
|
66
|
+
and/or other materials provided with the distribution.
|
67
|
+
3. The name of the author may not be used to endorse or promote products
|
68
|
+
derived from this software without specific prior written permission.
|
69
|
+
|
70
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
71
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
72
|
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
73
|
+
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
74
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
75
|
+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
76
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
77
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
78
|
+
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
79
|
+
OF SUCH DAMAGE.
|
80
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'rake/contrib/sshpublisher'
|
10
|
+
require 'fileutils'
|
11
|
+
include FileUtils
|
12
|
+
|
13
|
+
NAME = "ruby-terminfo"
|
14
|
+
AUTHOR = "Tanaka Akira"
|
15
|
+
EMAIL = "akr@fsij.org"
|
16
|
+
DESCRIPTION = "terminfo binding for Ruby"
|
17
|
+
RUBYFORGE_PROJECT = "ruby-terminfo"
|
18
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
19
|
+
BIN_FILES = %w( )
|
20
|
+
|
21
|
+
VERS = "0.1.1"
|
22
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
23
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
24
|
+
RDOC_OPTS = [
|
25
|
+
'--title', "#{NAME} documentation",
|
26
|
+
"--charset", "utf-8",
|
27
|
+
"--opname", "index.html",
|
28
|
+
"--line-numbers",
|
29
|
+
"--main", "README",
|
30
|
+
"--inline-source",
|
31
|
+
]
|
32
|
+
|
33
|
+
task :default => [:test]
|
34
|
+
task :package => [:clean]
|
35
|
+
|
36
|
+
Rake::TestTask.new("test") do |t|
|
37
|
+
t.libs << "test"
|
38
|
+
t.pattern = "test/**/*_test.rb"
|
39
|
+
t.verbose = true
|
40
|
+
end
|
41
|
+
|
42
|
+
spec = Gem::Specification.new do |s|
|
43
|
+
s.name = NAME
|
44
|
+
s.version = VERS
|
45
|
+
s.platform = Gem::Platform::RUBY
|
46
|
+
s.has_rdoc = true
|
47
|
+
s.extra_rdoc_files = ["README", "ChangeLog"]
|
48
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
49
|
+
s.summary = DESCRIPTION
|
50
|
+
s.description = DESCRIPTION
|
51
|
+
s.author = AUTHOR
|
52
|
+
s.email = EMAIL
|
53
|
+
s.homepage = HOMEPATH
|
54
|
+
s.executables = BIN_FILES
|
55
|
+
s.rubyforge_project = RUBYFORGE_PROJECT
|
56
|
+
s.bindir = "bin"
|
57
|
+
s.require_path = "lib"
|
58
|
+
#s.autorequire = ""
|
59
|
+
s.test_files = Dir["test/*_test.rb"]
|
60
|
+
|
61
|
+
#s.add_dependency('activesupport', '>=1.3.1')
|
62
|
+
#s.required_ruby_version = '>= 1.8.2'
|
63
|
+
|
64
|
+
s.files = %w(README ChangeLog Rakefile) +
|
65
|
+
Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
|
66
|
+
Dir.glob("*.{c,rb}") +
|
67
|
+
Dir.glob("examples/**/*.rb") +
|
68
|
+
Dir.glob("tools/*.rb") +
|
69
|
+
Dir.glob("rails/*.rb")
|
70
|
+
|
71
|
+
s.extensions = ["extconf.rb"]
|
72
|
+
end
|
73
|
+
|
74
|
+
Rake::GemPackageTask.new(spec) do |p|
|
75
|
+
p.need_tar = true
|
76
|
+
p.gem_spec = spec
|
77
|
+
end
|
78
|
+
|
79
|
+
task :install do
|
80
|
+
name = "#{NAME}-#{VERS}.gem"
|
81
|
+
sh %{rake package}
|
82
|
+
sh %{sudo gem install pkg/#{name}}
|
83
|
+
end
|
84
|
+
|
85
|
+
task :uninstall => [:clean] do
|
86
|
+
sh %{sudo gem uninstall #{NAME}}
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
Rake::RDocTask.new do |rdoc|
|
91
|
+
rdoc.rdoc_dir = 'html'
|
92
|
+
rdoc.options += RDOC_OPTS
|
93
|
+
rdoc.template = "resh"
|
94
|
+
#rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
95
|
+
if ENV['DOC_FILES']
|
96
|
+
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
97
|
+
else
|
98
|
+
rdoc.rdoc_files.include('README', 'ChangeLog')
|
99
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
100
|
+
rdoc.rdoc_files.include('**/*.c')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
desc "Publish to RubyForge"
|
105
|
+
task :rubyforge => [:rdoc, :package] do
|
106
|
+
require 'rubyforge'
|
107
|
+
Rake::RubyForgePublisher.new(RUBYFORGE_PROJECT, 'takiuchi').upload
|
108
|
+
end
|
109
|
+
|
110
|
+
desc 'Package and upload the release to rubyforge.'
|
111
|
+
task :release => [:clean, :package] do |t|
|
112
|
+
v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
|
113
|
+
abort "Versions don't match #{v} vs #{VERS}" unless v == VERS
|
114
|
+
pkg = "pkg/#{NAME}-#{VERS}"
|
115
|
+
|
116
|
+
require 'rubyforge'
|
117
|
+
rf = RubyForge.new.configure
|
118
|
+
puts "Logging in"
|
119
|
+
rf.login
|
120
|
+
|
121
|
+
c = rf.userconfig
|
122
|
+
# c["release_notes"] = description if description
|
123
|
+
# c["release_changes"] = changes if changes
|
124
|
+
c["preformatted"] = true
|
125
|
+
|
126
|
+
files = [
|
127
|
+
"#{pkg}.tgz",
|
128
|
+
"#{pkg}.gem"
|
129
|
+
].compact
|
130
|
+
|
131
|
+
puts "Releasing #{NAME} v. #{VERS}"
|
132
|
+
rf.add_release RUBYFORGE_PROJECT, NAME, VERS, *files
|
133
|
+
end
|
134
|
+
|
135
|
+
desc 'Show information about the gem.'
|
136
|
+
task :debug_gem do
|
137
|
+
puts spec.to_ruby
|
138
|
+
end
|
139
|
+
|
140
|
+
desc 'Update gem spec'
|
141
|
+
task :gemspec do
|
142
|
+
open("#{NAME}.gemspec", 'w').write spec.to_ruby
|
143
|
+
end
|
data/extconf.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# extconf.rb - a part of a Ruby binding for terminfo library.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2007 Tanaka Akira. All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions
|
7
|
+
# are met:
|
8
|
+
#
|
9
|
+
# 1. Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions and the following disclaimer.
|
11
|
+
# 2. Redistributions in binary form must reproduce the above
|
12
|
+
# copyright notice, this list of conditions and the following
|
13
|
+
# disclaimer in the documentation and/or other materials provided
|
14
|
+
# with the distribution.
|
15
|
+
# 3. The name of the author may not be used to endorse or promote
|
16
|
+
# products derived from this software without specific prior
|
17
|
+
# written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
20
|
+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
23
|
+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
25
|
+
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
27
|
+
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
require 'mkmf'
|
32
|
+
|
33
|
+
# GNU/Linux -lncurses
|
34
|
+
# FreeBSD -lncurses
|
35
|
+
# HP-UX -lcurses
|
36
|
+
|
37
|
+
have_library("ncurses", "setupterm") or
|
38
|
+
have_library("curses", "setupterm")
|
39
|
+
|
40
|
+
have_type("rb_io_t", ["ruby.h", "rubyio.h"])
|
41
|
+
have_struct_member("rb_io_t", "fd", ["ruby.h", "rubyio.h"])
|
42
|
+
have_struct_member("OpenFile", "fd", ["ruby.h", "rubyio.h"])
|
43
|
+
|
44
|
+
create_header
|
45
|
+
create_makefile('terminfo')
|
46
|
+
|
47
|
+
open("Makefile", "a") {|mfile|
|
48
|
+
mfile.puts <<'End'
|
49
|
+
rdoc:
|
50
|
+
rdoc --op rdoc terminfo.c lib/terminfo.rb
|
51
|
+
End
|
52
|
+
}
|
53
|
+
|
data/lib/terminfo.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# terminfo.rb - a part of a Ruby binding for terminfo library.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2007 Tanaka Akira. All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions
|
7
|
+
# are met:
|
8
|
+
#
|
9
|
+
# 1. Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions and the following disclaimer.
|
11
|
+
# 2. Redistributions in binary form must reproduce the above
|
12
|
+
# copyright notice, this list of conditions and the following
|
13
|
+
# disclaimer in the documentation and/or other materials provided
|
14
|
+
# with the distribution.
|
15
|
+
# 3. The name of the author may not be used to endorse or promote
|
16
|
+
# products derived from this software without specific prior
|
17
|
+
# written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
20
|
+
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
23
|
+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
25
|
+
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
27
|
+
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
require 'terminfo.so'
|
32
|
+
|
33
|
+
class TermInfo
|
34
|
+
def TermInfo.default_object
|
35
|
+
unless defined? @default_terminfo
|
36
|
+
io = open("/dev/tty", "r+")
|
37
|
+
io.sync = true
|
38
|
+
@default_terminfo = TermInfo.new(ENV['TERM'], io)
|
39
|
+
end
|
40
|
+
@default_terminfo
|
41
|
+
end
|
42
|
+
|
43
|
+
def TermInfo.control_string(*args) default_object.control_string(*args) end
|
44
|
+
def TermInfo.control(*args) default_object.control(*args) end
|
45
|
+
def TermInfo.write(str) default_object.write(str) end
|
46
|
+
def TermInfo.flush(&block) default_object.flush(&block) end
|
47
|
+
def TermInfo.screen_size() default_object.screen_size() end
|
48
|
+
def TermInfo.screen_lines() default_object.screen_lines() end
|
49
|
+
def TermInfo.screen_height() default_object.screen_height() end
|
50
|
+
def TermInfo.screen_columns() default_object.screen_columns() end
|
51
|
+
def TermInfo.screen_width() default_object.screen_width() end
|
52
|
+
def TermInfo.io() default_object.io() end
|
53
|
+
|
54
|
+
def initialize(term=ENV['TERM'], io=STDERR)
|
55
|
+
setupterm(term, io.fileno)
|
56
|
+
@term = term
|
57
|
+
@io = io
|
58
|
+
end
|
59
|
+
attr_reader :io
|
60
|
+
|
61
|
+
def inspect
|
62
|
+
"\#<#{self.class}:#{@term}>"
|
63
|
+
end
|
64
|
+
|
65
|
+
# TermInfo#control_string return a string to control terminal.
|
66
|
+
#
|
67
|
+
# TermInfo#control_string([afflines,] capname, p1, p2, ...)
|
68
|
+
#
|
69
|
+
# capname is a terminfo string capability such as "cuu", "el".
|
70
|
+
#
|
71
|
+
# p1, p2, ... are parameters for the capability.
|
72
|
+
#
|
73
|
+
# afflines is a number of lines affected. (used for determining padding length)
|
74
|
+
def control_string(*args)
|
75
|
+
afflines = 1
|
76
|
+
raise ArgumentError, "capname requried" if args.empty?
|
77
|
+
afflines = args.shift.to_i if args.first.respond_to?(:to_int)
|
78
|
+
raise ArgumentError, "capname not given" if !args.first.respond_to?(:to_str)
|
79
|
+
capname = args.shift.to_str
|
80
|
+
self.tputs(self.tparm(self.tigetstr(capname), *args), afflines)
|
81
|
+
end
|
82
|
+
|
83
|
+
# TermInfo#control controls a terminal.
|
84
|
+
#
|
85
|
+
# It prints the result of control_string to io specified at initialization.
|
86
|
+
def control(*args)
|
87
|
+
@io.write(self.control_string(*args))
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def write(str)
|
92
|
+
@io.write(str)
|
93
|
+
end
|
94
|
+
|
95
|
+
def flush
|
96
|
+
oldlevel = nil
|
97
|
+
if block_given?
|
98
|
+
oldlevel = Thread.current[:TermInfo_Flush_level]
|
99
|
+
oldsync = @io.sync
|
100
|
+
begin
|
101
|
+
Thread.current[:TermInfo_Flush_level] = (oldlevel || 0) + 1
|
102
|
+
@io.sync = false
|
103
|
+
yield
|
104
|
+
ensure
|
105
|
+
Thread.current[:TermInfo_Flush_level] = oldlevel
|
106
|
+
@io.sync = oldsync
|
107
|
+
end
|
108
|
+
end
|
109
|
+
@io.flush if oldlevel == nil
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
|
113
|
+
# returns terminal screen size in a two element array: [lines, columns].
|
114
|
+
def screen_size
|
115
|
+
begin
|
116
|
+
size = TermInfo.tiocgwinsz(@io)
|
117
|
+
rescue NotImplementedError
|
118
|
+
size = [0,0]
|
119
|
+
end
|
120
|
+
if size[0] == 0
|
121
|
+
size[0] = ENV.include?('LINES') ? ENV['LINES'].to_i : self.tigetnum("lines")
|
122
|
+
end
|
123
|
+
if size[1] == 0
|
124
|
+
size[1] = ENV.include?('COLUMNS') ? ENV['COLUMNS'].to_i : self.tigetnum("cols")
|
125
|
+
end
|
126
|
+
size
|
127
|
+
end
|
128
|
+
|
129
|
+
# returns terminal screen height.
|
130
|
+
def screen_lines
|
131
|
+
self.screen_size[0]
|
132
|
+
end
|
133
|
+
alias screen_height screen_lines
|
134
|
+
|
135
|
+
# returns terminal screen width.
|
136
|
+
def screen_columns
|
137
|
+
self.screen_size[1]
|
138
|
+
end
|
139
|
+
alias screen_width screen_columns
|
140
|
+
end
|
data/terminfo.c
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
/* terminfo.c - Ruby binding for terminfo library.
|
2
|
+
|
3
|
+
Copyright (C) 2007 Tanaka Akira. All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions
|
7
|
+
are met:
|
8
|
+
|
9
|
+
1. Redistributions of source code must retain the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer.
|
11
|
+
2. Redistributions in binary form must reproduce the above
|
12
|
+
copyright notice, this list of conditions and the following
|
13
|
+
disclaimer in the documentation and/or other materials provided
|
14
|
+
with the distribution.
|
15
|
+
3. The name of the author may not be used to endorse or promote
|
16
|
+
products derived from this software without specific prior
|
17
|
+
written permission.
|
18
|
+
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
20
|
+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
23
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
25
|
+
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
27
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
*/
|
31
|
+
|
32
|
+
#include "ruby.h"
|
33
|
+
#include "rubyio.h"
|
34
|
+
#include "extconf.h"
|
35
|
+
|
36
|
+
#include <curses.h>
|
37
|
+
#include <term.h>
|
38
|
+
#include <termios.h>
|
39
|
+
#include <sys/ioctl.h>
|
40
|
+
|
41
|
+
static VALUE cTermInfo;
|
42
|
+
static VALUE eTermInfoError;
|
43
|
+
|
44
|
+
#ifndef HAVE_TYPE_RB_IO_T
|
45
|
+
typedef OpenFile rb_io_t;
|
46
|
+
#endif
|
47
|
+
|
48
|
+
static void
|
49
|
+
rt_free(void *ptr)
|
50
|
+
{
|
51
|
+
if(ptr != NULL)
|
52
|
+
del_curterm(ptr);
|
53
|
+
}
|
54
|
+
|
55
|
+
static VALUE
|
56
|
+
rt_alloc(VALUE klass)
|
57
|
+
{
|
58
|
+
return Data_Wrap_Struct(klass, NULL, rt_free, 0);
|
59
|
+
}
|
60
|
+
|
61
|
+
static TERMINAL *
|
62
|
+
check_rt(VALUE self)
|
63
|
+
{
|
64
|
+
Check_Type(self, T_DATA);
|
65
|
+
if (RDATA(self)->dfree != rt_free) {
|
66
|
+
rb_raise(rb_eTypeError, "wrong argument type %s (expected TermInfo)",
|
67
|
+
rb_class2name(CLASS_OF(self)));
|
68
|
+
}
|
69
|
+
return DATA_PTR(self);
|
70
|
+
}
|
71
|
+
|
72
|
+
static void
|
73
|
+
setup(VALUE self)
|
74
|
+
{
|
75
|
+
TERMINAL *old;
|
76
|
+
TERMINAL *term = check_rt(self);
|
77
|
+
if (term == NULL) { rb_raise(eTermInfoError, "terminfo object not initialized"); }
|
78
|
+
if (cur_term == term)
|
79
|
+
return;
|
80
|
+
old = set_curterm(term);
|
81
|
+
}
|
82
|
+
|
83
|
+
/*
|
84
|
+
* TermInfo#setupterm(term, fd) => int
|
85
|
+
*
|
86
|
+
* TermInfo#setupterm initializes TermInfo object.
|
87
|
+
*
|
88
|
+
* term is a string of nil.
|
89
|
+
* If nil is given, the environment variable $TERM is used.
|
90
|
+
*
|
91
|
+
* fd is a file descriptor for target terminal.
|
92
|
+
*/
|
93
|
+
static VALUE
|
94
|
+
rt_setupterm(VALUE self, VALUE v_term, VALUE v_fd)
|
95
|
+
{
|
96
|
+
char *term;
|
97
|
+
int fd;
|
98
|
+
int err;
|
99
|
+
int ret;
|
100
|
+
if (check_rt(self) != NULL) { rb_raise(eTermInfoError, "terminfo object already initialized"); }
|
101
|
+
|
102
|
+
if (v_term == Qnil)
|
103
|
+
term = NULL;
|
104
|
+
else
|
105
|
+
term = StringValueCStr(v_term);
|
106
|
+
fd = NUM2INT(v_fd);
|
107
|
+
|
108
|
+
ret = setupterm(term, fd, &err);
|
109
|
+
if (ret == ERR) {
|
110
|
+
if (err == 1) rb_raise(eTermInfoError, "hardcopy terminal");
|
111
|
+
else if (err == 0) rb_raise(eTermInfoError, "terminal could not be found");
|
112
|
+
else if (err == -1) rb_raise(eTermInfoError, "terminfo database could not be found");
|
113
|
+
else rb_raise(eTermInfoError, "unexpected setupterm error");
|
114
|
+
}
|
115
|
+
|
116
|
+
DATA_PTR(self) = cur_term;
|
117
|
+
|
118
|
+
return INT2NUM(err);
|
119
|
+
}
|
120
|
+
|
121
|
+
/*
|
122
|
+
* TermInfo#tigetflag(capname) => int
|
123
|
+
*
|
124
|
+
* TermInfo#tigetflag returns a boolean capability specified by capname.
|
125
|
+
*/
|
126
|
+
static VALUE
|
127
|
+
rt_tigetflag(VALUE self, VALUE v_capname)
|
128
|
+
{
|
129
|
+
int ret;
|
130
|
+
setup(self);
|
131
|
+
ret = tigetflag(StringValueCStr(v_capname));
|
132
|
+
if (ret == -1) { rb_raise(eTermInfoError, "not a boolean capability"); }
|
133
|
+
return RTEST(ret) ? Qtrue : Qfalse;
|
134
|
+
}
|
135
|
+
|
136
|
+
/*
|
137
|
+
* TermInfo#tigetnum(capname) => int
|
138
|
+
*
|
139
|
+
* TermInfo#tigetnum returns a numeric capability specified by capname.
|
140
|
+
*/
|
141
|
+
static VALUE
|
142
|
+
rt_tigetnum(VALUE self, VALUE v_capname)
|
143
|
+
{
|
144
|
+
int ret;
|
145
|
+
setup(self);
|
146
|
+
ret = tigetnum(StringValueCStr(v_capname));
|
147
|
+
if (ret == -2) { rb_raise(eTermInfoError, "not a numeric capability"); }
|
148
|
+
if (ret == -1) { rb_raise(eTermInfoError, "canceled or absent numeric capability"); }
|
149
|
+
return INT2NUM(ret);
|
150
|
+
}
|
151
|
+
|
152
|
+
/*
|
153
|
+
* TermInfo#tigetstr(capname) => str
|
154
|
+
*
|
155
|
+
* TermInfo#tigetstr returns a string capability specified by capname.
|
156
|
+
*
|
157
|
+
* The return value should be printed after tputs is applied.
|
158
|
+
* Also tparm should be applied if it has parameters.
|
159
|
+
*
|
160
|
+
* io.print ti.tputs(ti.tparm(ti.tigetstr("cuf"), 2))
|
161
|
+
*
|
162
|
+
* Note that "cuf" means "cursor forward".
|
163
|
+
*/
|
164
|
+
static VALUE
|
165
|
+
rt_tigetstr(VALUE self, VALUE v_capname)
|
166
|
+
{
|
167
|
+
char *ret;
|
168
|
+
setup(self);
|
169
|
+
ret = tigetstr(StringValueCStr(v_capname));
|
170
|
+
if (ret == (char*)-1) {
|
171
|
+
rb_raise(eTermInfoError, "not a string capability");
|
172
|
+
}
|
173
|
+
if (ret == 0) {
|
174
|
+
rb_raise(eTermInfoError, "canceled or absent string capability");
|
175
|
+
}
|
176
|
+
return rb_str_new2(ret);
|
177
|
+
}
|
178
|
+
|
179
|
+
/*
|
180
|
+
* TermInfo#tparm(str, ...) => str
|
181
|
+
*
|
182
|
+
* TermInfo#tparm expands parameters in str returned by tigetstr.
|
183
|
+
*/
|
184
|
+
static VALUE
|
185
|
+
rt_tparm(int argc, VALUE *argv, VALUE self)
|
186
|
+
{
|
187
|
+
char *capname, *ret;
|
188
|
+
setup(self);
|
189
|
+
VALUE v_capname, v1, v2, v3, v4, v5, v6, v7, v8, v9;
|
190
|
+
long p1, p2, p3, p4, p5, p6, p7, p8, p9;
|
191
|
+
setup(self);
|
192
|
+
|
193
|
+
if (rb_scan_args(argc, argv, "19", &v_capname, &v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8, &v9) == 0) {
|
194
|
+
rb_raise(rb_eArgError, "capname required");
|
195
|
+
}
|
196
|
+
|
197
|
+
capname = StringValueCStr(v_capname);
|
198
|
+
#define conv(p, v) do { if (v == Qnil) p = 0; else p = NUM2LONG(v); } while(0)
|
199
|
+
conv(p1, v1);
|
200
|
+
conv(p2, v2);
|
201
|
+
conv(p3, v3);
|
202
|
+
conv(p4, v4);
|
203
|
+
conv(p5, v5);
|
204
|
+
conv(p6, v6);
|
205
|
+
conv(p7, v7);
|
206
|
+
conv(p8, v8);
|
207
|
+
conv(p9, v9);
|
208
|
+
|
209
|
+
ret = tparm(capname, p1, p2, p3, p4, p5, p6, p7, p8, p9);
|
210
|
+
|
211
|
+
if (ret == NULL) { rb_raise(eTermInfoError, "tparm failed"); }
|
212
|
+
|
213
|
+
return rb_str_new2(ret);
|
214
|
+
}
|
215
|
+
|
216
|
+
static VALUE putfunc_output; /* xxx: not thread safe */
|
217
|
+
|
218
|
+
static int
|
219
|
+
putfunc(int arg)
|
220
|
+
{
|
221
|
+
char ch = arg;
|
222
|
+
rb_str_cat(putfunc_output, &ch, 1);
|
223
|
+
return arg;
|
224
|
+
}
|
225
|
+
|
226
|
+
/*
|
227
|
+
* TermInfo#tputs(str, affcnt) => str
|
228
|
+
*
|
229
|
+
* TermInfo#tputs expands padding informaiton using padding characters.
|
230
|
+
* affcnt is a number of lines affected by the str.
|
231
|
+
*/
|
232
|
+
static VALUE
|
233
|
+
rt_tputs(VALUE self, VALUE v_str, VALUE v_affcnt)
|
234
|
+
{
|
235
|
+
int ret;
|
236
|
+
char *str;
|
237
|
+
int affcnt;
|
238
|
+
VALUE output;
|
239
|
+
|
240
|
+
setup(self);
|
241
|
+
str = StringValueCStr(v_str);
|
242
|
+
affcnt = NUM2INT(v_affcnt);
|
243
|
+
|
244
|
+
putfunc_output = output = rb_str_new2("");
|
245
|
+
ret = tputs(str, affcnt, putfunc);
|
246
|
+
putfunc_output = Qnil;
|
247
|
+
|
248
|
+
if (ret == ERR) { rb_raise(eTermInfoError, "tputs failed"); }
|
249
|
+
|
250
|
+
return output;
|
251
|
+
}
|
252
|
+
|
253
|
+
#if defined(HAVE_ST_FD)
|
254
|
+
# define FILENO(fptr) (fptr->fd)
|
255
|
+
#elif defined(HAVE_RB_IO_T_FD)
|
256
|
+
# define FILENO(fptr) fileno(fptr->fd)
|
257
|
+
#else
|
258
|
+
# define FILENO(fptr) fileno(fptr->f)
|
259
|
+
#endif
|
260
|
+
|
261
|
+
/*
|
262
|
+
* TermInfo.tiocgwinsz(io) => [row, col]
|
263
|
+
*
|
264
|
+
* TermInfo.tiocgwinsz returns the screen size of the terminal refered by io,
|
265
|
+
* using TIOCGWINSZ ioctl.
|
266
|
+
*/
|
267
|
+
static VALUE
|
268
|
+
rt_tiocgwinsz(VALUE self, VALUE io)
|
269
|
+
{
|
270
|
+
#ifdef TIOCGWINSZ
|
271
|
+
rb_io_t *fptr;
|
272
|
+
struct winsize sz;
|
273
|
+
int ret;
|
274
|
+
|
275
|
+
GetOpenFile(io, fptr);
|
276
|
+
|
277
|
+
ret = ioctl(FILENO(fptr), TIOCGWINSZ, &sz);
|
278
|
+
if (ret == -1) rb_raise(rb_eIOError, "TIOCGWINSZ failed");
|
279
|
+
|
280
|
+
return rb_ary_new3(2, INT2NUM(sz.ws_row), INT2NUM(sz.ws_col));
|
281
|
+
#else
|
282
|
+
rb_notimplement();
|
283
|
+
#endif
|
284
|
+
}
|
285
|
+
|
286
|
+
/*
|
287
|
+
* TermInfo.tiocswinsz(io, row, col)
|
288
|
+
*
|
289
|
+
* TermInfo.tiocgwinsz update the screen size information of the terminal refered by io,
|
290
|
+
* using TIOCSWINSZ ioctl.
|
291
|
+
*
|
292
|
+
* It returns nil.
|
293
|
+
*/
|
294
|
+
static VALUE
|
295
|
+
rt_tiocswinsz(VALUE self, VALUE io, VALUE row, VALUE col)
|
296
|
+
{
|
297
|
+
#ifdef TIOCSWINSZ
|
298
|
+
rb_io_t *fptr;
|
299
|
+
struct winsize sz;
|
300
|
+
int ret;
|
301
|
+
|
302
|
+
GetOpenFile(io, fptr);
|
303
|
+
|
304
|
+
sz.ws_row = NUM2INT(row);
|
305
|
+
sz.ws_col = NUM2INT(col);
|
306
|
+
|
307
|
+
ret = ioctl(FILENO(fptr), TIOCSWINSZ, &sz);
|
308
|
+
if (ret == -1) rb_raise(rb_eIOError, "TIOCSWINSZ failed");
|
309
|
+
|
310
|
+
return Qnil;
|
311
|
+
#else
|
312
|
+
rb_notimplement();
|
313
|
+
#endif
|
314
|
+
}
|
315
|
+
|
316
|
+
void
|
317
|
+
Init_terminfo()
|
318
|
+
{
|
319
|
+
putfunc_output = Qnil;
|
320
|
+
rb_global_variable(&putfunc_output);
|
321
|
+
|
322
|
+
cTermInfo = rb_define_class("TermInfo", rb_cData);
|
323
|
+
eTermInfoError = rb_define_class_under(cTermInfo, "TermInfoError", rb_eRuntimeError);
|
324
|
+
|
325
|
+
rb_define_alloc_func(cTermInfo, rt_alloc);
|
326
|
+
|
327
|
+
rb_define_method(cTermInfo, "setupterm", rt_setupterm, 2);
|
328
|
+
rb_define_method(cTermInfo, "tigetflag", rt_tigetflag, 1);
|
329
|
+
rb_define_method(cTermInfo, "tigetnum", rt_tigetnum, 1);
|
330
|
+
rb_define_method(cTermInfo, "tigetstr", rt_tigetstr, 1);
|
331
|
+
rb_define_method(cTermInfo, "tparm", rt_tparm, -1);
|
332
|
+
rb_define_method(cTermInfo, "tputs", rt_tputs, 2);
|
333
|
+
|
334
|
+
rb_define_module_function(cTermInfo, "tiocgwinsz", rt_tiocgwinsz, 1);
|
335
|
+
rb_define_module_function(cTermInfo, "tiocswinsz", rt_tiocswinsz, 3);
|
336
|
+
}
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-terminfo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tanaka Akira
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-13 00:00:00 +13:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: terminfo binding for Ruby
|
17
|
+
email: akr@fsij.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions:
|
21
|
+
- extconf.rb
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
- ChangeLog
|
25
|
+
files:
|
26
|
+
- README
|
27
|
+
- ChangeLog
|
28
|
+
- Rakefile
|
29
|
+
- lib/terminfo.rb
|
30
|
+
- terminfo.c
|
31
|
+
- extconf.rb
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://ruby-terminfo.rubyforge.org
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --title
|
39
|
+
- ruby-terminfo documentation
|
40
|
+
- --charset
|
41
|
+
- utf-8
|
42
|
+
- --opname
|
43
|
+
- index.html
|
44
|
+
- --line-numbers
|
45
|
+
- --main
|
46
|
+
- README
|
47
|
+
- --inline-source
|
48
|
+
- --exclude
|
49
|
+
- ^(examples|extras)/
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project: ruby-terminfo
|
67
|
+
rubygems_version: 1.3.5
|
68
|
+
signing_key:
|
69
|
+
specification_version: 2
|
70
|
+
summary: terminfo binding for Ruby
|
71
|
+
test_files: []
|
72
|
+
|