summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Shadura <andrewsh@debian.org>2018-04-24 17:35:40 +0200
committerAndrew Shadura <andrewsh@debian.org>2018-04-24 17:35:40 +0200
commitc5965c88d9cbc3d900b9fb78fa6025382523e8ca (patch)
tree00b3d9e771d74f8cb929d5f79e3ca7ac1866a520
parent4fdd938b873fb23c2a328df7e10930e6a7207985 (diff)
New upstream version 1.2.4
-rw-r--r--.gitignore15
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml13
-rw-r--r--Gemfile4
-rw-r--r--History.md90
-rw-r--r--Manifest.txt16
-rw-r--r--README.md66
-rw-r--r--Rakefile95
-rw-r--r--appveyor.yml16
-rw-r--r--checksums.yaml.gz.sigbin256 -> 0 bytes
-rw-r--r--curses.gemspec18
-rw-r--r--data.tar.gz.sigbin256 -> 0 bytes
-rw-r--r--ext/curses/curses.c600
-rw-r--r--ext/curses/extconf.rb28
-rw-r--r--lib/curses.rb24
-rw-r--r--metadata.gz.sig1
-rwxr-xr-xsample/hello.rb27
-rwxr-xr-xsample/mouse.rb32
-rwxr-xr-xsample/rain.rb96
-rwxr-xr-xsample/view.rb61
-rwxr-xr-xsample/view2.rb85
21 files changed, 1024 insertions, 266 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c09e7b9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*.rbc
+*.swp
+/TAGS
+/doc
+/lib/curses.bundle
+/lib/curses.so
+/lib/curses.dll
+/pkg
+/tmp
+/Gemfile.lock
+/vendor/x86-mingw32
+/vendor/x64-mingw32
+/vendor/x64-mswin64*
+/ext/curses/Makefile
+.ruby-version
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..0f5a59b
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor/PDCurses"]
+ path = vendor/PDCurses
+ url = https://github.com/Bill-Gray/PDCurses.git
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f2957f8
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+---
+language: ruby
+notifications:
+ email:
+ recipients:
+ - drbrain@segment7.net
+ - shugo@ruby-lang.org
+rvm:
+ - 2.2.7
+ - 2.3.4
+ - 2.4.1
+ - ruby-head
+script: rake compile
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..aadb347
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+source "https://rubygems.org"
+
+gemspec
diff --git a/History.md b/History.md
index d8ec9e7..565600d 100644
--- a/History.md
+++ b/History.md
@@ -1,3 +1,93 @@
+### 1.2.4 / 2017-09-13
+
+New features:
+
+* Update PDCurses.
+
+Bug fixes:
+
+* Fix the path of pdcurses.dll for i386-mingw. (Issue #36)
+
+Documentation:
+
+* Include reference to ncurses dependency. Pull request #34 by qume.
+
+### 1.2.3 / 2017-07-03
+
+Bug fixes:
+
+* Fixes for mswin. Pull requests #30, #31, #32 and #33 by unak.
+
+### 1.2.2 / 2017-04-22
+
+New features:
+
+* Add Curses.get_key_modifiers, Curses.return_key_modifiers, and
+ Curses.save_key_modifiers.
+* Support mswin native build. Pull request #29 by usa.
+
+### 1.2.1 / 2017-03-27
+
+New features:
+
+* Add touch, untouch, touched?, touch_line, and line_touched?.
+
+Bug fixes:
+
+* Fix Pad#subpad to use subpad(3). (Issue #23)
+* Fix build issues on macOS. Pull requests #24, #25, #26, #27 and #28 by nobu.
+
+### 1.2.0 / 2017-02-19
+
+New features:
+
+* Add Curses.assume_default_colors.
+
+Bug fixes:
+
+* Curses.unget_char should use String#ord even if unget_wch() is not available.
+* The default value of keyboard_encoding should be ASCII-8BIT if get_wch() is
+ not available.
+* NUM2ULONG() should be used in Window#bkgd etc.
+
+### 1.1.3 / 2017-02-08
+
+Bug fixes:
+
+* Update PDCurses to handle extended keys.
+
+### 1.1.2 / 2017-02-06
+
+Bug fixes:
+
+* Use the left-alt-fix branch of https://github.com/shugo/PDCurses.git to get
+ ALT + < etc.
+
+### 1.1.1 / 2017-01-25
+
+Bug fixes:
+
+* Add -DPDC_WIDE to CPPFLAGS when compiling with PDCurses.
+
+### 1.1.0 / 2017-01-24
+
+New features:
+
+* Use bundler instead of hoe. Pull request #18 by hsbt.
+* Enable appveyor. Pull request #19 by hsbt.
+* Add badges for build status to README.md. Pull request #20 by hsbt.
+* Add Curses.erase and Curses::Window.erase.
+* Add Curses::Window.redraw.
+* Add Curses.unget_char, Curses.get_char, and Curses::Window.get_char for
+ multibyte characters.
+* Add Curses.keyboard_encoding and Curses.terminal_encoding.
+* Support cross compilation for mingw32.
+
+Bug fixes:
+
+* Fix error in attron/attroff documentation. Pull request #14 by stomar.
+* Improve code samples. Pull request #15 by stomar.
+
### 1.0.2 / 2016-03-15
Bug fixes:
diff --git a/Manifest.txt b/Manifest.txt
deleted file mode 100644
index fff049d..0000000
--- a/Manifest.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-BSDL
-COPYING
-History.md
-Manifest.txt
-README.md
-Rakefile
-curses.gemspec
-ext/curses/curses.c
-ext/curses/depend
-ext/curses/extconf.rb
-lib/curses.rb
-sample/hello.rb
-sample/mouse.rb
-sample/rain.rb
-sample/view.rb
-sample/view2.rb
diff --git a/README.md b/README.md
index b166132..dd5f71a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
# curses
+[![Gem Version](https://badge.fury.io/rb/curses.svg)](https://badge.fury.io/rb/curses)
+[![Build Status](https://travis-ci.org/ruby/curses.svg?branch=master)](https://travis-ci.org/ruby/curses)
+[![Build status](https://ci.appveyor.com/api/projects/status/kdvksgjo4fyd3c4m/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/curses/branch/master)
+
* https://github.com/ruby/curses
* https://github.com/ruby/curses/issues
@@ -14,20 +18,74 @@ with the release of Ruby 2.1.0. (see [ruby/ruby@9c5b2fd][2])
## Install
$ gem install curses
+
+Requires ncurses or ncursesw (with wide character support).
+On Debian based distributions, you can install it with apt:
+
+ $ apt install libncurses5-dev
+
+Or
+
+ $ apt install libncursesw5-dev
## Developers
-After checking out the source, run:
+After checking out the repo, run `bundle install` to install dependencies.
+
+To compile the extension library, run `bundle exec rake compile`.
+
+To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `curses.gemspec`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
+
+### Cross compilation for Windows on Debian GNU/Linux based platforms
+
+1. Install development environment fo 32- and 64-bit Windows.
+
+ ```
+ $ sudo apt-get install mingw-w64
+ ```
+
+2. Install rake-compiler.
- $ rake newb
+ ```
+ $ gem install rake-compiler
+ ```
-This task will install any missing dependencies, run the tests/specs,
-and generate the RDoc.
+3. Compile multiple versions of Ruby.
+
+ ```
+ $ rake-compiler cross-ruby HOST=i686-w64-mingw32 VERSION=2.2.6
+ $ rake-compiler cross-ruby HOST=i686-w64-mingw32 VERSION=2.3.3
+ $ rake-compiler cross-ruby HOST=i686-w64-mingw32 VERSION=2.4.0
+ $ rake-compiler cross-ruby HOST=x86_64-w64-mingw32 VERSION=2.2.6
+ $ rake-compiler cross-ruby HOST=x86_64-w64-mingw32 VERSION=2.3.3
+ $ rake-compiler cross-ruby HOST=x86_64-w64-mingw32 VERSION=2.4.0
+ ```
+
+3. Compile PDCurses.
+
+ ```
+ $ rake build:pdcurses
+ ```
+
+5. Compile curses.gem.
+
+ ```
+ $ rake RUBY_CC_VERSION=2.3.3:2.4.0 cross clean compile native gem
+ ```
+
+Binary gems are generated in pkg/.
## License
curses is released under the Ruby and 2-clause BSD licenses. See COPYING for
details.
+Binary gems for mingw32 include a forked version of PDCurses, which is in
+the public domain:
+
+ https://github.com/Bill-Gray/PDCurses
+
+The version for Win32 console mode in the win32 subdirectory is used.
+
[1]: https://bugs.ruby-lang.org/issues/8584
[2]: https://github.com/ruby/ruby/commit/9c5b2fd8aa0fd343ad094d47a638cfd3f6ae0a81
diff --git a/Rakefile b/Rakefile
index 5d120a6..c911a3d 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,41 +1,84 @@
-# -*- ruby -*-
+require "bundler"
+
+Bundler::GemHelper.install_tasks
-require 'rubygems'
-require 'hoe'
begin
require 'rake/extensiontask'
rescue LoadError => e
warn "\nmissing #{e.path} (for rake-compiler)" if e.respond_to? :path
- warn "run: rake newb\n\n"
+ warn "run: bundle install\n\n"
end
-Hoe.plugin :git
-Hoe.plugin :minitest
-Hoe.plugin :travis
-
-HOE = Hoe.spec 'curses' do
- self.version = '1.0.2'
-
- developer 'Eric Hodel', 'drbrain@segment7.net'
- developer 'Shugo Maeda', ''
+$mswin = /mswin/ =~ RUBY_PLATFORM
- license 'Ruby'
- license 'BSD-2-Clause'
+CLOBBER.include("vendor/#{RUBY_PLATFORM}") if $mswin
+CLOBBER.include("vendor/x86-mingw32")
+CLOBBER.include("vendor/x64-mingw32")
+CLOBBER.include("lib/2.*")
+CLOBBER.include("pkg")
- self.extra_rdoc_files << 'ext/curses/curses.c'
- self.spec_extras[:extensions] = 'ext/curses/extconf.rb'
+namespace :build do
+ desc "Build PDCurses"
+ task :pdcurses do
+ mkdir_p "vendor/#{RUBY_PLATFORM}/PDCurses" if $mswin
+ mkdir_p "vendor/x86-mingw32/PDCurses"
+ mkdir_p "vendor/x64-mingw32/PDCurses"
+ chdir "vendor/PDCurses/win32" do
+ if $mswin
+ sh "nmake -f vcwin32.mak clean all WIDE=Y DLL=Y"
+ cp %w[pdcurses.dll pdcurses.lib], "../../#{RUBY_PLATFORM}/PDCurses"
+ else
+ sh "make -f mingwin32.mak clean all WIDE=Y DLL=Y"
+ cp "pdcurses.dll", "../../x86-mingw32/PDCurses"
- self.readme_file = 'README.md'
- self.history_file = 'History.md'
+ sh "make -f mingwin32.mak clean all _w64=1 WIDE=Y DLL=Y"
+ cp "pdcurses.dll", "../../x64-mingw32/PDCurses"
+ end
+ end
+ end
+end
- self.extra_dev_deps << ['rake-compiler', '~> 0.8']
+namespace :clean do
+ desc "Clean PDCurses"
+ task :pdcurses do
+ chdir "vendor/PDCurses/win32" do
+ sh "nmake -f vcwin32.mak clean" if $mswin
+ sh "make -f mingwin32.mak clean _linux_w64=1"
+ end
+ end
end
-if Rake.const_defined? :ExtensionTask then
- Rake::ExtensionTask.new 'curses', HOE.spec
+spec = eval(File.read(File.expand_path("curses.gemspec", __dir__)))
+Rake::ExtensionTask.new(spec.name, spec) do |ext|
+ if $mswin
+ ext.config_options << '--with-curses-include=' +
+ File.expand_path("vendor/PDCurses", __dir__) +
+ ' --with-curses-version=function --enable-pdcurses-wide' +
+ ' --enable-pdcurses-dll' +
+ ' --with-curses-lib=' +
+ File.expand_path("vendor/#{RUBY_PLATFORM}/PDCurses", __dir__)
+ spec.files += ["vendor/#{RUBY_PLATFORM}/PDCurses/pdcurses.dll"]
+ end
- task default: :compile
- task test: :compile
+ ext.cross_compile = true
+ ext.cross_platform = ["x86-mingw32", "x64-mingw32"]
+ ext.cross_config_options << '--with-curses-include=' +
+ File.expand_path("vendor/PDCurses", __dir__) +
+ ' --with-curses-version=function --enable-pdcurses-wide'
+ ext.cross_config_options << {
+ 'x86-mingw32' => '--with-curses-lib=' +
+ File.expand_path("vendor/x86-mingw32/PDCurses", __dir__),
+ 'x64-mingw32' => '--with-curses-lib=' +
+ File.expand_path("vendor/x64-mingw32/PDCurses", __dir__)
+ }
+ ext.cross_compiling do |_spec|
+ bin_file = "vendor/#{_spec.platform}/PDCurses/pdcurses.dll"
+ _spec.files += [bin_file]
+ stage_file = "#{ext.tmp_dir}/#{_spec.platform}/stage/#{bin_file}"
+ stage_dir = File.dirname(stage_file)
+ directory stage_dir
+ file stage_file => [stage_dir, bin_file] do
+ cp bin_file, stage_file
+ end
+ end
end
-
-# vim: syntax=ruby
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..b2355d2
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,16 @@
+---
+clone_depth: 10
+install:
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
+ - appveyor DownloadFile http://dl.bintray.com/oneclick/OpenKnapsack/x64/pdcurses-3.4-x64-windows.tar.lzma
+ - 7z e pdcurses-3.4-x64-windows.tar.lzma
+ - 7z x -y -oC:\Ruby%ruby_version% pdcurses-3.4-x64-windows.tar
+ - bundle install
+build_script:
+ - rake -rdevkit compile -- --with-curses-dir=C:\Ruby%ruby_version%
+test: off
+deploy: off
+environment:
+ matrix:
+ - ruby_version: "22-x64"
+ - ruby_version: "23-x64"
diff --git a/checksums.yaml.gz.sig b/checksums.yaml.gz.sig
deleted file mode 100644
index fdc971c..0000000
--- a/checksums.yaml.gz.sig
+++ /dev/null
Binary files differ
diff --git a/curses.gemspec b/curses.gemspec
index e933b6c..d774965 100644
--- a/curses.gemspec
+++ b/curses.gemspec
@@ -1,13 +1,17 @@
-require 'rubygems'
Gem::Specification.new { |s|
s.name = "curses"
- s.version = "1.0.2"
- s.author = "Shugo Maeda"
- s.email = "shugo@ruby-lang.org"
- s.homepage = "http://github.com/shugo/curses"
+ s.version = "1.2.4"
+ s.author = ["Shugo Maeda", 'Eric Hodel']
+ s.email = ["shugo@ruby-lang.org", 'drbrain@segment7.net']
+ s.homepage = "https://github.com/ruby/curses"
s.platform = Gem::Platform::RUBY
- s.summary = "curses binding for Ruby"
- s.files = Dir.glob('{lib,ext,sample}/**/*') + ['README.md', 'COPYING', 'BSDL']
+ s.summary = "A Ruby binding for curses, ncurses, and PDCurses. curses is an extension library for text UI applications. Formerly part of the Ruby standard library, [curses was removed and placed in this gem][1] with the release of Ruby 2.1.0. (see [ruby/ruby@9c5b2fd][2])"
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(vendor)/}) }
s.extensions = ["ext/curses/extconf.rb"]
s.require_path = "lib"
+ s.required_ruby_version = Gem::Requirement.new('>= 2.1.0')
+ s.licenses = ['Ruby', 'BSD-2-Clause']
+ s.add_development_dependency 'bundler'
+ s.add_development_dependency 'rake'
+ s.add_development_dependency 'rake-compiler'
}
diff --git a/data.tar.gz.sig b/data.tar.gz.sig
deleted file mode 100644
index a95ec0d..0000000
--- a/data.tar.gz.sig
+++ /dev/null
Binary files differ
diff --git a/ext/curses/curses.c b/ext/curses/curses.c
index 218e545..5d1e315 100644
--- a/ext/curses/curses.c
+++ b/ext/curses/curses.c
@@ -73,6 +73,9 @@ static VALUE cMouseEvent;
static VALUE rb_stdscr;
+static rb_encoding *keyboard_encoding;
+static rb_encoding *terminal_encoding;
+
struct windata {
WINDOW *window;
};
@@ -135,8 +138,21 @@ prep_window(VALUE class, WINDOW *window)
return obj;
}
+#if (defined(HAVE_ADDNWSTR) || defined(HAVE_WADDNWSTR)) && defined(_WIN32)
+static inline rb_encoding *
+get_wide_encoding(void)
+{
+ static rb_encoding *utf16le = NULL;
+ if (!utf16le) {
+ utf16le = rb_enc_find("utf-16le");
+ }
+ return utf16le;
+}
+#endif
+
/*-------------------------- module Curses --------------------------*/
+static void curses_finalize(VALUE);
/*
* Document-method: Curses.init_screen
*
@@ -152,6 +168,7 @@ curses_init_screen(void)
if (stdscr == 0) {
rb_raise(rb_eRuntimeError, "can't initialize curses");
}
+ rb_set_end_proc(curses_finalize, 0);
clear();
rb_stdscr = prep_window(cWindow, stdscr);
return rb_stdscr;
@@ -250,6 +267,23 @@ curses_clear(VALUE obj)
return Qnil;
}
+#ifdef HAVE_WERASE
+/*
+ * Document-method: Curses.erase
+ *
+ * Erase the screen.
+ */
+static VALUE
+curses_erase(VALUE obj)
+{
+ curses_stdscr();
+ werase(stdscr);
+ return Qnil;
+}
+#else
+#define curses_erase rb_f_notimplement
+#endif
+
/*
* Document-method: Curses.clrtoeol
*
@@ -628,11 +662,19 @@ static VALUE
curses_addstr(VALUE obj, VALUE str)
{
StringValue(str);
- str = rb_str_export_locale(str);
+#if defined(HAVE_ADDNWSTR) && defined(_WIN32)
+ str = rb_str_export_to_enc(str, get_wide_encoding());
+ curses_stdscr();
+ if (!NIL_P(str)) {
+ addnwstr((wchar_t *)RSTRING_PTR(str), RSTRING_LEN(str) / sizeof(wchar_t));
+ }
+#else
+ str = rb_str_export_to_enc(str, terminal_encoding);
curses_stdscr();
if (!NIL_P(str)) {
addstr(StringValueCStr(str));
}
+#endif
return Qnil;
}
@@ -663,7 +705,7 @@ curses_getch(VALUE obj)
if (rb_isprint(c)) {
char ch = (char)c;
- return rb_locale_str_new(&ch, 1);
+ return rb_external_str_new_with_enc(&ch, 1, keyboard_encoding);
}
return UINT2NUM(c);
}
@@ -696,7 +738,7 @@ curses_getstr(VALUE obj)
curses_stdscr();
rb_thread_call_without_gvl(getstr_func, rtn, RUBY_UBF_IO, 0);
- return rb_locale_str_new_cstr(rtn);
+ return rb_external_str_new_with_enc(rtn, strlen(rtn), keyboard_encoding);
}
/*
@@ -869,7 +911,8 @@ curses_setscrreg(VALUE obj, VALUE top, VALUE bottom)
* Document-method: Curses.attroff
* call-seq: attroff(attrs)
*
- * Turns on the named attributes +attrs+ without affecting any others.
+ * Turns off the named attributes +attrs+
+ * without affecting any others.
*
* See also Curses::Window.attrset for additional information.
*/
@@ -885,7 +928,7 @@ curses_attroff(VALUE obj, VALUE attrs)
* Document-method: Curses.attron
* call-seq: attron(attrs)
*
- * Turns off the named attributes +attrs+
+ * Turns on the named attributes +attrs+
* without turning any other attributes on or off.
*
* See also Curses::Window.attrset for additional information.
@@ -933,7 +976,7 @@ curses_bkgdset(VALUE obj, VALUE ch)
{
#ifdef HAVE_BKGDSET
curses_stdscr();
- bkgdset(NUM2CH(ch));
+ bkgdset(NUM2CHTYPE(ch));
#endif
return Qnil;
}
@@ -954,7 +997,7 @@ curses_bkgd(VALUE obj, VALUE ch)
{
#ifdef HAVE_BKGD
curses_stdscr();
- return (bkgd(NUM2CH(ch)) == OK) ? Qtrue : Qfalse;
+ return (bkgd(NUM2CHTYPE(ch)) == OK) ? Qtrue : Qfalse;
#else
return Qfalse;
#endif
@@ -977,6 +1020,23 @@ curses_use_default_colors(VALUE obj)
#define curses_use_default_colors rb_f_notimplement
#endif
+#if defined(HAVE_ASSUME_DEFAULT_COLORS)
+/*
+ * tells which colors to paint for color pair 0.
+ *
+ * see also the system manual for default_colors(3)
+ */
+static VALUE
+curses_assume_default_colors(VALUE obj, VALUE fg, VALUE bg)
+{
+ curses_stdscr();
+ assume_default_colors(NUM2INT(fg), NUM2INT(bg));
+ return Qnil;
+}
+#else
+#define curses_assume_default_colors rb_f_notimplement
+#endif
+
#if defined(HAVE_TABSIZE)
/*
* Document-method: Curses.TABSIZE=
@@ -1562,6 +1622,26 @@ window_clear(VALUE obj)
return Qnil;
}
+#ifdef HAVE_WERASE
+/*
+ * Document-method: Curses::Window.erase
+ *
+ * Erase the window.
+ */
+static VALUE
+window_erase(VALUE obj)
+{
+ struct windata *winp;
+
+ GetWINDOW(obj, winp);
+ werase(winp->window);
+
+ return Qnil;
+}
+#else
+#define curses_erase rb_f_notimplement
+#endif
+
/*
* Document-method: Curses::Window.clrtoeol
*
@@ -1618,6 +1698,145 @@ window_noutrefresh(VALUE obj)
return Qnil;
}
+#ifdef HAVE_REDRAWWIN
+/*
+ * Document-method: Curses::Window.redraw
+ *
+ * Redraws the entire window.
+ */
+static VALUE
+window_redraw(VALUE obj)
+{
+ struct windata *winp;
+
+ GetWINDOW(obj, winp);
+ redrawwin(winp->window);
+
+ return Qnil;
+}
+#else
+#define window_redraw rb_f_notimplement
+#endif
+
+#ifdef HAVE_TOUCHWIN
+/*
+ * Document-method: Curses::Window.touch
+ *
+ * Treat the window as if it has been modified since the last call of refresh.
+ */
+static VALUE
+window_touch(VALUE obj)
+{
+ struct windata *winp;
+
+ GetWINDOW(obj, winp);
+ touchwin(winp->window);
+
+ return Qnil;
+}
+#else
+#define window_touch rb_f_notimplement
+#endif
+
+#ifdef HAVE_UNTOUCHWIN
+/*
+ * Document-method: Curses::Window.untouch
+ *
+ * Treat the window as if it has not been modified since the last call of
+ * refresh.
+ */
+static VALUE
+window_untouch(VALUE obj)
+{
+ struct windata *winp;
+
+ GetWINDOW(obj, winp);
+ untouchwin(winp->window);
+
+ return Qnil;
+}
+#else
+#define window_touch rb_f_notimplement
+#endif
+
+#ifdef HAVE_IS_WINTOUCHED
+/*
+ * Document-method: Curses::Window.touched?
+ *
+ * Return true if the window has been modified since the last call of refresh.
+ */
+static VALUE
+window_touched(VALUE obj)
+{
+ struct windata *winp;
+
+ GetWINDOW(obj, winp);
+ return is_wintouched(winp->window) ? Qtrue : Qfalse;
+}
+#else
+#define window_touched rb_f_notimplement
+#endif
+
+#ifdef HAVE_WTOUCHLN
+/*
+ * Document-method: Curses::Window.touch_line
+ * call-seq: touch_line(y, n, changed = true)
+ *
+ * Make n lines from line y look as if they have (changed = true) or have not
+ * (changed = false) been modified since the last call of refresh.
+ */
+static VALUE
+window_touch_line(int argc, VALUE *argv, VALUE obj)
+{
+ struct windata *winp;
+ VALUE y, n, changed;
+ int result;
+
+ rb_scan_args(argc, argv, "12", &y, &n, &changed);
+ if (argc < 2) {
+ n = INT2NUM(1);
+ }
+ if (argc < 3) {
+ changed = Qtrue;
+ }
+ GetWINDOW(obj, winp);
+ result = wtouchln(winp->window, NUM2INT(y), NUM2INT(n), RTEST(changed));
+ if (result == ERR) {
+ rb_raise(rb_eRangeError, "Out of window");
+ }
+
+ return Qnil;
+}
+#else
+#define window_touch_line rb_f_notimplement
+#endif
+
+#ifdef HAVE_IS_LINETOUCHED
+/*
+ * Document-method: Curses::Window.line_touched?
+ * call-seq: line_touched?(line)
+ *
+ * Return true if the specified line has been modified since the last call of
+ * refresh.
+ */
+static VALUE
+window_line_touched(VALUE obj, VALUE line)
+{
+ struct windata *winp;
+ int result, n;
+
+ GetWINDOW(obj, winp);
+ n = NUM2INT(line);
+ result = is_linetouched(winp->window, n);
+ if (result == ERR) {
+ rb_raise(rb_eArgError, "Invalid line %d", n);
+ }
+ return result ? Qtrue : Qfalse;
+}
+#else
+#define window_line_touched rb_f_notimplement
+#endif
+
/*
* Document-method: Curses::Window.move
* call-seq: move(y,x)
@@ -1701,7 +1920,7 @@ window_maxy(VALUE obj)
return INT2FIX(getmaxy(winp->window));
#elif defined(getmaxyx)
{
- int x, y;
+ int RB_UNUSED_VAR(x), y;
getmaxyx(winp->window, y, x);
return INT2FIX(y);
}
@@ -1725,7 +1944,7 @@ window_maxx(VALUE obj)
return INT2FIX(getmaxx(winp->window));
#elif defined(getmaxyx)
{
- int x, y;
+ int x, RB_UNUSED_VAR(y);
getmaxyx(winp->window, y, x);
return INT2FIX(x);
}
@@ -1921,9 +2140,15 @@ window_addstr(VALUE obj, VALUE str)
struct windata *winp;
StringValue(str);
- str = rb_str_export_locale(str);
+#if defined(HAVE_WADDNWSTR) && defined(_WIN32)
+ str = rb_str_export_to_enc(str, get_wide_encoding());
+ GetWINDOW(obj, winp);
+ waddnwstr(winp->window, (wchar_t *)RSTRING_PTR(str), RSTRING_LEN(str) / sizeof(wchar_t));
+#else
+ str = rb_str_export_to_enc(str, terminal_encoding);
GetWINDOW(obj, winp);
waddstr(winp->window, StringValueCStr(str));
+#endif
}
return Qnil;
}
@@ -1981,7 +2206,7 @@ window_getch(VALUE obj)
if (rb_isprint(c)) {
char ch = (char)c;
- return rb_locale_str_new(&ch, 1);
+ return rb_external_str_new_with_enc(&ch, 1, keyboard_encoding);
}
return UINT2NUM(c);
}
@@ -2018,7 +2243,8 @@ window_getstr(VALUE obj)
GetWINDOW(obj, winp);
arg.win = winp->window;
rb_thread_call_without_gvl(wgetstr_func, (void *)&arg, RUBY_UBF_IO, 0);
- return rb_locale_str_new_cstr(arg.rtn);
+ return rb_external_str_new_with_enc(arg.rtn, strlen(arg.rtn),
+ keyboard_encoding);
}
/*
@@ -2219,7 +2445,8 @@ window_scrl(VALUE obj, VALUE n)
* Document-method: Curses::Window.attroff
* call-seq: attroff(attrs)
*
- * Turns on the named attributes +attrs+ without affecting any others.
+ * Turns off the named attributes +attrs+
+ * without affecting any others.
*
* See also Curses::Window.attrset
*/
@@ -2240,7 +2467,7 @@ window_attroff(VALUE obj, VALUE attrs)
* Document-method: Curses::Window.attron
* call-seq: attron(attrs)
*
- * Turns off the named attributes +attrs+
+ * Turns on the named attributes +attrs+
* without turning any other attributes on or off.
*
* See also Curses::Window.attrset
@@ -2322,7 +2549,7 @@ window_bkgdset(VALUE obj, VALUE ch)
struct windata *winp;
GetWINDOW(obj,winp);
- wbkgdset(winp->window, NUM2CH(ch));
+ wbkgdset(winp->window, NUM2CHTYPE(ch));
#endif
return Qnil;
}
@@ -2343,7 +2570,7 @@ window_bkgd(VALUE obj, VALUE ch)
struct windata *winp;
GetWINDOW(obj,winp);
- return (wbkgd(winp->window, NUM2CH(ch)) == OK) ? Qtrue : Qfalse;
+ return (wbkgd(winp->window, NUM2CHTYPE(ch)) == OK) ? Qtrue : Qfalse;
#else
return Qfalse;
#endif
@@ -2362,7 +2589,7 @@ window_getbkgd(VALUE obj)
struct windata *winp;
GetWINDOW(obj,winp);
- return (c = getbkgd(winp->window) != ERR) ? CH2FIX(c) : Qnil;
+ return (c = getbkgd(winp->window) != ERR) ? ULONG2NUM(c) : Qnil;
#else
return Qnil;
#endif
@@ -2521,9 +2748,6 @@ pad_initialize(VALUE obj, VALUE h, VALUE w)
return obj;
}
-#if 1
-#define pad_subpad window_subwin
-#else
/*
* Document-method: Curses::Pad.subpad
* call-seq:
@@ -2537,7 +2761,7 @@ static VALUE
pad_subpad(VALUE obj, VALUE height, VALUE width, VALUE begin_x, VALUE begin_y)
{
struct windata *padp;
- WINDOW *subpad;
+ WINDOW *sub_pad;
VALUE pad;
int h, w, x, y;
@@ -2546,12 +2770,11 @@ pad_subpad(VALUE obj, VALUE height, VALUE width, VALUE begin_x, VALUE begin_y)
x = NUM2INT(begin_x);
y = NUM2INT(begin_y);
GetWINDOW(obj, padp);
- subpad = subwin(padp->window, h, w, x, y);
- pad = prep_window(rb_obj_class(obj), subpad);
+ sub_pad = subpad(padp->window, h, w, x, y);
+ pad = prep_window(rb_obj_class(obj), sub_pad);
return pad;
}
-#endif
/*
* Document-method: Curses::Pad.refresh
@@ -2622,6 +2845,239 @@ pad_noutrefresh(VALUE obj, VALUE pminrow, VALUE pmincol, VALUE sminrow,
}
#endif /* HAVE_NEWPAD */
+/*
+ * Document-method: Curses.keyboard_encoding
+ * call-seq: Curses.keyboard_encoding
+ *
+ * Returns the encoding for keyboard input.
+ */
+static VALUE
+curses_get_keyboard_encoding(VALUE obj)
+{
+ return rb_enc_from_encoding(keyboard_encoding);
+}
+
+/*
+ * Document-method: Curses.keyboard_encoding=
+ * call-seq: Curses.keyboard_encoding = encoding
+ *
+ * Sets the encoding for keyboard input.
+ */
+static VALUE
+curses_set_keyboard_encoding(VALUE obj, VALUE enc)
+{
+ keyboard_encoding = rb_to_encoding(enc);
+ return enc;
+}
+
+/*
+ * Document-method: Curses.terminal_encoding
+ * call-seq: Curses.terminal_encoding
+ *
+ * Returns the encoding for terminal output.
+ */
+static VALUE
+curses_get_terminal_encoding(VALUE obj)
+{
+ return rb_enc_from_encoding(terminal_encoding);
+}
+
+/*
+ * Document-method: Curses.terminal_encoding=
+ * call-seq: Curses.terminal_encoding = encoding
+ *
+ * Sets the encoding for terminal output.
+ */
+static VALUE
+curses_set_terminal_encoding(VALUE obj, VALUE enc)
+{
+ terminal_encoding = rb_to_encoding(enc);
+ return enc;
+}
+
+/*
+ * Document-method: Curses.unget_char
+ * call-seq: unget_char(ch)
+ *
+ * Places +ch+ back onto the input queue to be returned by
+ * the next call to Curses.get_char etc.
+ *
+ * There is just one input queue for all windows.
+ */
+static VALUE
+curses_unget_char(VALUE obj, VALUE ch)
+{
+ ID id_ord;
+ unsigned int c;
+
+ curses_stdscr();
+ if (FIXNUM_P(ch)) {
+ ungetch(NUM2UINT(ch));
+ }
+ else {
+ StringValue(ch);
+ CONST_ID(id_ord, "ord");
+ c = NUM2UINT(rb_funcall(ch, id_ord, 0));
+#ifdef HAVE_UNGET_WCH
+ unget_wch(c);
+#else
+ if (c > 0xff) {
+ rb_raise(rb_eRangeError, "Out of range: %u", c);
+ }
+ ungetch(c);
+#endif
+ }
+ return Qnil;
+}
+
+static VALUE
+keyboard_uint_chr(unsigned int ch)
+{
+ return rb_enc_uint_chr(ch, keyboard_encoding);
+}
+
+#ifdef HAVE_GET_WCH
+struct get_wch_arg {
+ int retval;
+ wint_t ch;
+};
+
+static void *
+get_wch_func(void *_arg)
+{
+ struct get_wch_arg *arg = (struct get_wch_arg *) _arg;
+ arg->retval = get_wch(&arg->ch);
+ return 0;
+}
+#endif
+
+/*
+ * Document-method: Curses.get_char
+ *
+ * Read and returns a character or function key from the window.
+ * A single or multibyte character is represented by a String, and
+ * a function key is represented by an Integer.
+ * Returns nil if no input is ready.
+ *
+ * See Curses::Key to all the function KEY_* available
+ *
+ */
+static VALUE
+curses_get_char(VALUE obj)
+{
+#ifdef HAVE_GET_WCH
+ struct get_wch_arg arg;
+
+ curses_stdscr();
+ rb_thread_call_without_gvl(get_wch_func, &arg, RUBY_UBF_IO, 0);
+ switch (arg.retval) {
+ case OK:
+ return keyboard_uint_chr(arg.ch);
+ case KEY_CODE_YES:
+ return UINT2NUM(arg.ch);
+ }
+ return Qnil;
+#else
+ int c;
+
+ curses_stdscr();
+ rb_thread_call_without_gvl(getch_func, &c, RUBY_UBF_IO, 0);
+ if (c > 0xff) {
+ return INT2NUM(c);
+ }
+ else if (c >= 0) {
+ return keyboard_uint_chr(c);
+ }
+ else {
+ return Qnil;
+ }
+#endif
+}
+
+
+#ifdef HAVE_WGET_WCH
+struct wget_wch_arg {
+ WINDOW *win;
+ int retval;
+ wint_t ch;
+};
+
+static void *
+wget_wch_func(void *_arg)
+{
+ struct wget_wch_arg *arg = (struct wget_wch_arg *) _arg;
+ arg->retval = wget_wch(arg->win, &arg->ch);
+ return 0;
+}
+#endif
+
+/*
+ * Document-method: Curses::Window.get_char
+ *
+ * Read and returns a character or function key from the window.
+ * A single or multibyte character is represented by a String, and
+ * a function key is represented by an Integer.
+ * Returns nil if no input is ready.
+ *
+ * See Curses::Key to all the function KEY_* available
+ *
+ */
+static VALUE
+window_get_char(VALUE obj)
+{
+#ifdef HAVE_WGET_WCH
+ struct windata *winp;
+ struct wget_wch_arg arg;
+
+ GetWINDOW(obj, winp);
+ arg.win = winp->window;
+ rb_thread_call_without_gvl(wget_wch_func, &arg, RUBY_UBF_IO, 0);
+ switch (arg.retval) {
+ case OK:
+ return keyboard_uint_chr(arg.ch);
+ case KEY_CODE_YES:
+ return UINT2NUM(arg.ch);
+ }
+ return Qnil;
+#else
+ struct windata *winp;
+ struct wgetch_arg arg;
+
+ GetWINDOW(obj, winp);
+ arg.win = winp->window;
+ rb_thread_call_without_gvl(wgetch_func, (void *)&arg, RUBY_UBF_IO, 0);
+ if (arg.c > 0xff) {
+ return INT2NUM(arg.c);
+ }
+ else if (arg.c >= 0) {
+ return keyboard_uint_chr(arg.c);
+ }
+ else {
+ return Qnil;
+ }
+#endif
+}
+
+#ifdef HAVE_PDC_GET_KEY_MODIFIERS
+static VALUE
+curses_get_key_modifiers(VALUE obj)
+{
+ return ULONG2NUM(PDC_get_key_modifiers());
+}
+
+static VALUE
+curses_return_key_modifiers(VALUE obj, VALUE flag)
+{
+ return INT2NUM(PDC_return_key_modifiers(RTEST(flag)));
+}
+
+static VALUE
+curses_save_key_modifiers(VALUE obj, VALUE flag)
+{
+ return INT2NUM(PDC_save_key_modifiers(RTEST(flag)));
+}
+#endif
+
/*------------------------- Initialization -------------------------*/
/*
@@ -2656,6 +3112,13 @@ pad_noutrefresh(VALUE obj, VALUE pminrow, VALUE pmincol, VALUE sminrow,
void
Init_curses(void)
{
+#ifdef HAVE_GET_WCH
+ keyboard_encoding = rb_locale_encoding();
+#else
+ keyboard_encoding = rb_ascii8bit_encoding();
+#endif
+ terminal_encoding = rb_locale_encoding();
+
mCurses = rb_define_module("Curses");
/*
@@ -2700,6 +3163,7 @@ Init_curses(void)
rb_define_module_function(mCurses, "TABSIZE=", curses_tabsize_set, 1);
rb_define_module_function(mCurses, "use_default_colors", curses_use_default_colors, 0);
+ rb_define_module_function(mCurses, "assume_default_colors", curses_assume_default_colors, 2);
rb_define_module_function(mCurses, "init_screen", curses_init_screen, 0);
rb_define_module_function(mCurses, "close_screen", curses_close_screen, 0);
rb_define_module_function(mCurses, "closed?", curses_closed, 0);
@@ -2707,6 +3171,7 @@ Init_curses(void)
rb_define_module_function(mCurses, "refresh", curses_refresh, 0);
rb_define_module_function(mCurses, "doupdate", curses_doupdate, 0);
rb_define_module_function(mCurses, "clear", curses_clear, 0);
+ rb_define_module_function(mCurses, "erase", curses_erase, 0);
rb_define_module_function(mCurses, "clrtoeol", curses_clrtoeol, 0);
rb_define_module_function(mCurses, "echo", curses_echo, 0);
rb_define_module_function(mCurses, "noecho", curses_noecho, 0);
@@ -2770,6 +3235,17 @@ Init_curses(void)
rb_define_module_function(mCurses, "timeout=", curses_timeout, 1);
rb_define_module_function(mCurses, "def_prog_mode", curses_def_prog_mode, 0);
rb_define_module_function(mCurses, "reset_prog_mode", curses_reset_prog_mode, 0);
+ rb_define_module_function(mCurses, "keyboard_encoding", curses_get_keyboard_encoding, 0);
+ rb_define_module_function(mCurses, "keyboard_encoding=", curses_set_keyboard_encoding, 1);
+ rb_define_module_function(mCurses, "terminal_encoding", curses_get_terminal_encoding, 0);
+ rb_define_module_function(mCurses, "terminal_encoding=", curses_set_terminal_encoding, 1);
+ rb_define_module_function(mCurses, "unget_char", curses_unget_char, 1);
+ rb_define_module_function(mCurses, "get_char", curses_get_char, 0);
+#ifdef HAVE_PDC_GET_KEY_MODIFIERS
+ rb_define_module_function(mCurses, "get_key_modifiers", curses_get_key_modifiers, 0);
+ rb_define_module_function(mCurses, "return_key_modifiers", curses_return_key_modifiers, 1);
+ rb_define_module_function(mCurses, "save_key_modifiers", curses_save_key_modifiers, 1);
+#endif
{
VALUE version;
@@ -2808,23 +3284,23 @@ Init_curses(void)
*
* == Usage
*
- * require 'curses'
+ * require "curses"
*
- * Curses.init_screen()
+ * Curses.init_screen
*
* my_str = "LOOK! PONIES!"
- * bwin = Curses::Window.new( 10, (my_str.length + 10),
- * (Curses.lines - 10) / 2,
- * (Curses.cols - (my_str.length + 10)) / 2 )
+ *
+ * height, width = 12, my_str.length + 10
+ * top, left = (Curses.lines - height) / 2, (Curses.cols - width) / 2
+ * bwin = Curses::Window.new(height, width, top, left)
* bwin.box("\\", "/")
* bwin.refresh
- * win = bwin.subwin( 6, my_str.length + 6,
- * (Curses.lines - 6) / 2,
- * (Curses.cols - (my_str.length + 6)) / 2 )
- * win.setpos(2,3)
+ *
+ * win = bwin.subwin(height - 4, width - 4, top + 2, left + 2)
+ * win.setpos(2, 3)
* win.addstr(my_str)
* # or even
- * win << "\nORLY"
+ * win << "\nOH REALLY?"
* win << "\nYES!! " + my_str
* win.refresh
* win.getch
@@ -2837,9 +3313,16 @@ Init_curses(void)
rb_define_method(cWindow, "subwin", window_subwin, 4);
rb_define_method(cWindow, "close", window_close, 0);
rb_define_method(cWindow, "clear", window_clear, 0);
+ rb_define_method(cWindow, "erase", window_erase, 0);
rb_define_method(cWindow, "clrtoeol", window_clrtoeol, 0);
rb_define_method(cWindow, "refresh", window_refresh, 0);
rb_define_method(cWindow, "noutrefresh", window_noutrefresh, 0);
+ rb_define_method(cWindow, "redraw", window_redraw, 0);
+ rb_define_method(cWindow, "touch", window_touch, 0);
+ rb_define_method(cWindow, "untouch", window_untouch, 0);
+ rb_define_method(cWindow, "touched?", window_touched, 0);
+ rb_define_method(cWindow, "touch_line", window_touch_line, -1);
+ rb_define_method(cWindow, "line_touched?", window_line_touched, 1);
rb_define_method(cWindow, "box", window_box, -1);
rb_define_method(cWindow, "move", window_move, 2);
rb_define_method(cWindow, "setpos", window_setpos, 2);
@@ -2883,6 +3366,8 @@ Init_curses(void)
rb_define_method(cWindow, "nodelay=", window_nodelay, 1);
rb_define_method(cWindow, "timeout=", window_timeout, 1);
+ rb_define_method(cWindow, "get_char", window_get_char, 0);
+
#ifdef HAVE_NEWPAD
/*
* Document-class: Curses::Pad
@@ -2903,7 +3388,7 @@ Init_curses(void)
rb_undef_method(cPad, "subwin");
#endif
-#define rb_curses_define_const(c) rb_define_const(mCurses,#c,UINT2NUM(c))
+#define rb_curses_define_const(c) rb_define_const(mCurses,#c,CHTYPE2NUM(c))
#ifdef USE_COLOR
/* Document-const: A_ATTRIBUTES
@@ -4326,7 +4811,48 @@ Init_curses(void)
rb_define_const(mCurses, name, INT2FIX(c - 'A' + 1));
}
}
+#ifdef PDC_KEY_MODIFIER_SHIFT
+ /* PDCurses-specific keys */
+ rb_curses_define_const(ALT_0);
+ rb_curses_define_const(ALT_1);
+ rb_curses_define_const(ALT_2);
+ rb_curses_define_const(ALT_3);
+ rb_curses_define_const(ALT_4);
+ rb_curses_define_const(ALT_5);
+ rb_curses_define_const(ALT_6);
+ rb_curses_define_const(ALT_7);
+ rb_curses_define_const(ALT_8);
+ rb_curses_define_const(ALT_9);
+ rb_curses_define_const(ALT_A);
+ rb_curses_define_const(ALT_B);
+ rb_curses_define_const(ALT_C);
+ rb_curses_define_const(ALT_D);
+ rb_curses_define_const(ALT_E);
+ rb_curses_define_const(ALT_F);
+ rb_curses_define_const(ALT_G);
+ rb_curses_define_const(ALT_H);
+ rb_curses_define_const(ALT_I);
+ rb_curses_define_const(ALT_J);
+ rb_curses_define_const(ALT_K);
+ rb_curses_define_const(ALT_L);
+ rb_curses_define_const(ALT_M);
+ rb_curses_define_const(ALT_N);
+ rb_curses_define_const(ALT_O);
+ rb_curses_define_const(ALT_P);
+ rb_curses_define_const(ALT_Q);
+ rb_curses_define_const(ALT_R);
+ rb_curses_define_const(ALT_S);
+ rb_curses_define_const(ALT_T);
+ rb_curses_define_const(ALT_U);
+ rb_curses_define_const(ALT_V);
+ rb_curses_define_const(ALT_W);
+ rb_curses_define_const(ALT_X);
+ rb_curses_define_const(ALT_Y);
+ rb_curses_define_const(ALT_Z);
+ rb_curses_define_const(PDC_KEY_MODIFIER_SHIFT);
+ rb_curses_define_const(PDC_KEY_MODIFIER_CONTROL);
+ rb_curses_define_const(PDC_KEY_MODIFIER_ALT);
+ rb_curses_define_const(PDC_KEY_MODIFIER_NUMLOCK);
+#endif
#undef rb_curses_define_const
-
- rb_set_end_proc(curses_finalize, 0);
}
diff --git a/ext/curses/extconf.rb b/ext/curses/extconf.rb
index 9c7ca0e..7bebc1f 100644
--- a/ext/curses/extconf.rb
+++ b/ext/curses/extconf.rb
@@ -30,9 +30,9 @@ have_library("tinfo", "tgetent") or have_library("termcap", "tgetent")
header_library = nil
[
+ ["ncursesw/curses.h", ["ncursesw"]],
["ncurses.h", ["ncursesw", "ncurses"]],
["ncurses/curses.h", ["ncurses"]],
- ["ncursesw/curses.h", ["ncursesw"]],
["curses_colr/curses.h", ["cur_colr"]],
["curses.h", ["curses", "pdcurses"]],
# ["xcurses.h", ["XCurses"]], # XCurses (PDCurses for X11)
@@ -57,17 +57,20 @@ if header_library
for f in %w(beep bkgd bkgdset curs_set deleteln doupdate flash
getbkgd getnstr init isendwin keyname keypad resizeterm
- scrl set setscrreg ungetch
+ scrl set setscrreg ungetch addnwstr
wattroff wattron wattrset wbkgd wbkgdset wdeleteln wgetnstr
- wresize wscrl wsetscrreg
+ wresize wscrl wsetscrreg werase redrawwin waddnwstr
+ touchwin untouchwin wtouchln is_linetouched is_wintouched
def_prog_mode reset_prog_mode timeout wtimeout nodelay
- init_color wcolor_set use_default_colors newpad)
+ init_color wcolor_set use_default_colors assume_default_colors
+ newpad unget_wch get_wch wget_wch PDC_get_key_modifiers)
have_func(f) || (have_macro(f, curses) && $defs.push(format("-DHAVE_%s", f.upcase)))
end
+ convertible_int('chtype', [["#undef MOUSE_MOVED\n"]]+curses) or abort
flag = "-D_XOPEN_SOURCE_EXTENDED"
- if try_static_assert("sizeof(char*)>sizeof(int)",
- %w[stdio.h stdlib.h]+curses,
- flag)
+ if checking_for("_XOPEN_SOURCE_EXTENDED") {
+ try_compile(cpp_include(%w[stdio.h stdlib.h]+curses), flag, :werror => true)
+ }
$defs << flag
end
have_var("ESCDELAY", curses)
@@ -129,8 +132,17 @@ if header_library
else
warn "unexpected value for --with-curses-version: #{with_curses_version}"
end
-
+
+ if enable_config("pdcurses-wide")
+ $defs << '-DPDC_WIDE'
+ end
+
+ if enable_config("pdcurses-dll")
+ $defs << '-DPDC_DLL_BUILD'
+ end
+
if RUBY_VERSION >= '2.1'
+ create_header
create_makefile("curses")
else
# curses is part of ruby-core pre-2.1.0, so this gem is not required. But
diff --git a/lib/curses.rb b/lib/curses.rb
index f99ede3..9ea86f8 100644
--- a/lib/curses.rb
+++ b/lib/curses.rb
@@ -1 +1,23 @@
-require "curses.so"
+platform = RUBY_PLATFORM.sub(/i[3-7]86/, "x86")
+pdcurses_path = File.expand_path("../vendor/#{platform}/PDCurses", __dir__)
+pdcurses_bundled = File.directory?(pdcurses_path)
+if pdcurses_bundled
+ path = ENV["PATH"]
+ dir = File::ALT_SEPARATOR ?
+ pdcurses_path.tr("/", File::ALT_SEPARATOR) : dir
+ dirs = path.split(File::PATH_SEPARATOR)
+ if !dirs.include?(dir)
+ ENV["PATH"] = [dir, *dirs].join(File::PATH_SEPARATOR)
+ end
+end
+
+begin
+ major, minor, _ = RUBY_VERSION.split(/\./)
+ require "#{major}.#{minor}/curses.so"
+rescue LoadError
+ require "curses.so"
+end
+
+if pdcurses_bundled
+ Curses.keyboard_encoding = Encoding::UTF_8
+end
diff --git a/metadata.gz.sig b/metadata.gz.sig
deleted file mode 100644
index 083b0b3..0000000
--- a/metadata.gz.sig
+++ /dev/null
@@ -1 +0,0 @@
-]1&Ŕ|S[m;}S+Pגy]0X8Dt>p\]O`*xdv.aN!8DH^sűMhTFu}#ղ2reڲWbqƨdwbu#Ʉ*V͂XvMI?$9O&;Oz=*:溹Pl,I 4O.ApP۶)|O^oardEЎAzLW \ No newline at end of file
diff --git a/sample/hello.rb b/sample/hello.rb
index 6308a17..6ef31cd 100755
--- a/sample/hello.rb
+++ b/sample/hello.rb
@@ -1,13 +1,14 @@
#!/usr/bin/env ruby
require "curses"
-include Curses
def show_message(message)
- width = message.length + 6
- win = Window.new(5, width,
- (lines - 5) / 2, (cols - width) / 2)
- win.box('|', '-')
+ height = 5
+ width = message.length + 6
+ top = (Curses.lines - height) / 2
+ left = (Curses.cols - width) / 2
+ win = Curses::Window.new(height, width, top, left)
+ win.box("|", "-")
win.setpos(2, 3)
win.addstr(message)
win.refresh
@@ -15,16 +16,14 @@ def show_message(message)
win.close
end
-init_screen
+Curses.init_screen
begin
- crmode
-# show_message("Hit any key")
- setpos((lines - 5) / 2, (cols - 10) / 2)
- addstr("Hit any key")
- refresh
- getch
+ Curses.crmode
+ Curses.setpos((Curses.lines - 1) / 2, (Curses.cols - 11) / 2)
+ Curses.addstr("Hit any key")
+ Curses.refresh
+ Curses.getch
show_message("Hello, World!")
- refresh
ensure
- close_screen
+ Curses.close_screen
end
diff --git a/sample/mouse.rb b/sample/mouse.rb
index 49d4802..92c2b96 100755
--- a/sample/mouse.rb
+++ b/sample/mouse.rb
@@ -5,13 +5,13 @@ include Curses
def show_message(*msgs)
message = msgs.join
- width = message.length + 6
- win = Window.new(5, width,
- (lines - 5) / 2, (cols - width) / 2)
+ height, width = 5, message.length + 6
+ top, left = (lines - height) / 2, (cols - width) / 2
+ win = Window.new(height, width, top, left)
win.keypad = true
- win.attron(color_pair(COLOR_RED)){
- win.box(?|, ?-, ?+)
- }
+ win.attron(color_pair(COLOR_RED)) do
+ win.box("|", "-", "+")
+ end
win.setpos(2, 3)
win.addstr(message)
win.refresh
@@ -21,28 +21,28 @@ end
init_screen
start_color
-init_pair(COLOR_BLUE,COLOR_BLUE,COLOR_WHITE)
-init_pair(COLOR_RED,COLOR_RED,COLOR_WHITE)
+init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_WHITE)
+init_pair(COLOR_RED, COLOR_RED, COLOR_WHITE)
crmode
noecho
stdscr.keypad(true)
begin
mousemask(BUTTON1_CLICKED|BUTTON2_CLICKED|BUTTON3_CLICKED|BUTTON4_CLICKED)
- setpos((lines - 5) / 2, (cols - 10) / 2)
- attron(color_pair(COLOR_BLUE)|A_BOLD){
+ setpos((lines - 1) / 2, (cols - 5) / 2)
+ attron(color_pair(COLOR_BLUE)|A_BOLD) do
addstr("click")
- }
+ end
refresh
- while( true )
+ loop do
c = getch
case c
when KEY_MOUSE
m = getmouse
- if( m )
- show_message("getch = #{c.inspect}, ",
- "mouse event = #{'0x%x' % m.bstate}, ",
- "axis = (#{m.x},#{m.y},#{m.z})")
+ if m
+ show_message("getch = #{c.inspect}, ",
+ "mouse event = #{'0x%x' % m.bstate}, ",
+ "axis = (#{m.x},#{m.y},#{m.z})")
end
break
end
diff --git a/sample/rain.rb b/sample/rain.rb
index a5f8fc5..6ed5246 100755
--- a/sample/rain.rb
+++ b/sample/rain.rb
@@ -1,76 +1,72 @@
#!/usr/bin/env ruby
-# rain for a curses test
require "curses"
-include Curses
-def onsig(sig)
- close_screen
- exit sig
+def onsig(signal)
+ Curses.close_screen
+ exit signal
end
-def ranf
- rand(32767).to_f / 32767
+def place_string(y, x, string)
+ Curses.setpos(y, x)
+ Curses.addstr(string)
end
-# main #
-for i in %w[HUP INT QUIT TERM]
- if trap(i, "SIG_IGN") != 0 then # 0 for SIG_IGN
- trap(i) {|sig| onsig(sig) }
+def cycle_index(index)
+ (index + 1) % 5
+end
+
+%w[HUP INT QUIT TERM].each do |sig|
+ unless trap(sig, "IGNORE") == "IGNORE" # previous handler
+ trap(sig) {|s| onsig(s) }
end
end
-init_screen
-nl
-noecho
+Curses.init_screen
+Curses.nl
+Curses.noecho
+Curses.curs_set 0
srand
-xpos = {}
-ypos = {}
-r = lines - 4
-c = cols - 4
-for i in 0 .. 4
- xpos[i] = (c * ranf).to_i + 2
- ypos[i] = (r * ranf).to_i + 2
+xpos, ypos = {}, {}
+x_range = 2..(Curses.cols - 3)
+y_range = 2..(Curses.lines - 3)
+(0..4).each do |i|
+ xpos[i], ypos[i] = rand(x_range), rand(y_range)
end
i = 0
-while TRUE
- x = (c * ranf).to_i + 2
- y = (r * ranf).to_i + 2
+loop do
+ x, y = rand(x_range), rand(y_range)
+ place_string(y, x, ".")
- setpos(y, x); addstr(".")
+ place_string(ypos[i], xpos[i], "o")
- setpos(ypos[i], xpos[i]); addstr("o")
+ i = cycle_index(i)
+ place_string(ypos[i], xpos[i], "O")
- i = if i == 0 then 4 else i - 1 end
- setpos(ypos[i], xpos[i]); addstr("O")
+ i = cycle_index(i)
+ place_string(ypos[i] - 1, xpos[i], "-")
+ place_string(ypos[i], xpos[i] - 1, "|.|")
+ place_string(ypos[i] + 1, xpos[i], "-")
- i = if i == 0 then 4 else i - 1 end
- setpos(ypos[i] - 1, xpos[i]); addstr("-")
- setpos(ypos[i], xpos[i] - 1); addstr("|.|")
- setpos(ypos[i] + 1, xpos[i]); addstr("-")
+ i = cycle_index(i)
+ place_string(ypos[i] - 2, xpos[i], "-")
+ place_string(ypos[i] - 1, xpos[i] - 1, "/ \\")
+ place_string(ypos[i], xpos[i] - 2, "| O |")
+ place_string(ypos[i] + 1, xpos[i] - 1, "\\ /")
+ place_string(ypos[i] + 2, xpos[i], "-")
- i = if i == 0 then 4 else i - 1 end
- setpos(ypos[i] - 2, xpos[i]); addstr("-")
- setpos(ypos[i] - 1, xpos[i] - 1); addstr("/ \\")
- setpos(ypos[i], xpos[i] - 2); addstr("| O |")
- setpos(ypos[i] + 1, xpos[i] - 1); addstr("\\ /")
- setpos(ypos[i] + 2, xpos[i]); addstr("-")
+ i = cycle_index(i)
+ place_string(ypos[i] - 2, xpos[i], " ")
+ place_string(ypos[i] - 1, xpos[i] - 1, " ")
+ place_string(ypos[i], xpos[i] - 2, " ")
+ place_string(ypos[i] + 1, xpos[i] - 1, " ")
+ place_string(ypos[i] + 2, xpos[i], " ")
- i = if i == 0 then 4 else i - 1 end
- setpos(ypos[i] - 2, xpos[i]); addstr(" ")
- setpos(ypos[i] - 1, xpos[i] - 1); addstr(" ")
- setpos(ypos[i], xpos[i] - 2); addstr(" ")
- setpos(ypos[i] + 1, xpos[i] - 1); addstr(" ")
- setpos(ypos[i] + 2, xpos[i]); addstr(" ")
+ xpos[i], ypos[i] = x, y
-
- xpos[i] = x
- ypos[i] = y
- refresh
+ Curses.refresh
sleep(0.5)
end
-
-# end of main
diff --git a/sample/view.rb b/sample/view.rb
index 300b3b9..66e861a 100755
--- a/sample/view.rb
+++ b/sample/view.rb
@@ -3,54 +3,41 @@
require "curses"
include Curses
-#
-# main
-#
-
-if ARGV.size != 1 then
- printf("usage: view file\n");
+unless ARGV.size == 1
+ puts "usage: #{$0} file"
exit
end
+
begin
- fp = open(ARGV[0], "r")
+ data_lines = File.readlines(ARGV[0])
rescue
- raise "cannot open file: #{ARGV[1]}"
+ raise "cannot open file: #{ARGV[0]}"
end
-# signal(SIGINT, finish)
-
init_screen
-#keypad(stdscr, TRUE)
+#keypad(stdscr, true)
nonl
cbreak
noecho
-#scrollok(stdscr, TRUE)
-
-# slurp the file
-data_lines = []
-fp.each_line { |l|
- data_lines.push(l)
-}
-fp.close
-
+#scrollok(stdscr, true)
lptr = 0
-while TRUE
- i = 0
- while i < lines
+loop do
+ lines.times do |i|
setpos(i, 0)
#clrtoeol
- addstr(data_lines[lptr + i] || '')
- i += 1
+ addstr(data_lines[lptr + i] || "")
end
refresh
- explicit = FALSE
+ explicit = false
n = 0
- while TRUE
+ c = nil
+ loop do
c = getch
if c =~ /[0-9]/
n = 10 * n + c.to_i
+ explicit = true
else
break
end
@@ -61,31 +48,29 @@ while TRUE
case c
when "n" #when KEY_DOWN
i = 0
- while i < n
- if lptr + lines < data_lines.size then
- lptr += 1
+ n.times do
+ if lptr + lines < data_lines.size
+ lptr += 1
else
- break
+ break
end
i += 1
end
#wscrl(i)
-
when "p" #when KEY_UP
i = 0
- while i < n
- if lptr > 0 then
- lptr -= 1
+ n.times do
+ if lptr > 0
+ lptr -= 1
else
- break
+ break
end
i += 1
end
#wscrl(-i)
-
when "q"
break
end
-
end
+
close_screen
diff --git a/sample/view2.rb b/sample/view2.rb
index c29c0ce..0f3a49d 100755
--- a/sample/view2.rb
+++ b/sample/view2.rb
@@ -3,10 +3,10 @@
require "curses"
-# A curses based file viewer
+# A curses based file viewer.
class FileViewer
- # Create a new fileviewer, and view the file.
+ # Create a new FileViewer and view the file.
def initialize(filename)
@data_lines = []
@screen = nil
@@ -16,10 +16,8 @@ class FileViewer
interact
end
- # Perform the curses setup
+ # Perform the curses setup.
def init_curses
- # signal(SIGINT, finish)
-
Curses.init_screen
Curses.nonl
Curses.cbreak
@@ -28,37 +26,32 @@ class FileViewer
@screen = Curses.stdscr
@screen.scrollok(true)
- #$screen.keypad(true)
+ #@screen.keypad(true)
end
- # Load the file into memory, and put
- # the first part on the curses display.
+ # Load the file into memory and
+ # put the first part on the curses display.
def load_file(filename)
- fp = open(filename, "r") do |fp|
- # slurp the file
- fp.each_line { |l|
- @data_lines.push(l.chop)
- }
- end
+ @data_lines = File.readlines(filename).map(&:chomp)
@top = 0
- @data_lines[0..@screen.maxy-1].each_with_index{|line, idx|
+ @data_lines[0..@screen.maxy-1].each_with_index do |line, idx|
@screen.setpos(idx, 0)
@screen.addstr(line)
- }
- @screen.setpos(0,0)
+ end
+ @screen.setpos(0, 0)
@screen.refresh
rescue
raise "cannot open file '#{filename}' for reading"
end
- # Scroll the display up by one line
+ # Scroll the display up by one line.
def scroll_up
- if( @top > 0 )
+ if @top > 0
@screen.scrl(-1)
@top -= 1
str = @data_lines[@top]
- if( str )
+ if str
@screen.setpos(0, 0)
@screen.addstr(str)
end
@@ -68,13 +61,13 @@ class FileViewer
end
end
- # Scroll the display down by one line
+ # Scroll the display down by one line.
def scroll_down
- if( @top + @screen.maxy < @data_lines.length )
+ if @top + @screen.maxy < @data_lines.length
@screen.scrl(1)
@top += 1
str = @data_lines[@top + @screen.maxy - 1]
- if( str )
+ if str
@screen.setpos(@screen.maxy - 1, 0)
@screen.addstr(str)
end
@@ -85,53 +78,49 @@ class FileViewer
end
# Allow the user to interact with the display.
- # This uses EMACS-like keybindings, and also
+ # This uses Emacs-like keybindings, and also
# vi-like keybindings as well, except that left
# and right move to the beginning and end of the
# file, respectively.
def interact
- while true
+ loop do
result = true
c = Curses.getch
case c
- when Curses::KEY_DOWN, Curses::KEY_CTRL_N, ?j
+ when Curses::KEY_DOWN, Curses::KEY_CTRL_N, "j"
result = scroll_down
- when Curses::KEY_UP, Curses::KEY_CTRL_P, ?k
+ when Curses::KEY_UP, Curses::KEY_CTRL_P, "k"
result = scroll_up
- when Curses::KEY_NPAGE, ?\s # white space
- for i in 0..(@screen.maxy - 2)
- if( ! scroll_down )
- if( i == 0 )
- result = false
- end
+ when Curses::KEY_NPAGE, " "
+ (@screen.maxy - 1).times do |i|
+ if !scroll_down && i == 0
+ result = false
break
end
end
when Curses::KEY_PPAGE
- for i in 0..(@screen.maxy - 2)
- if( ! scroll_up )
- if( i == 0 )
- result = false
- end
+ (@screen.maxy - 1).times do |i|
+ if !scroll_up && i == 0
+ result = false
break
end
end
- when Curses::KEY_LEFT, Curses::KEY_CTRL_T, ?h
- while( scroll_up )
+ when Curses::KEY_LEFT, Curses::KEY_CTRL_T, "h"
+ while scroll_up
end
- when Curses::KEY_RIGHT, Curses::KEY_CTRL_B, ?l
- while( scroll_down )
+ when Curses::KEY_RIGHT, Curses::KEY_CTRL_B, "l"
+ while scroll_down
end
- when ?q
+ when "q"
break
else
- @screen.setpos(0,0)
+ @screen.setpos(0, 0)
@screen.addstr("[unknown key `#{Curses.keyname(c)}'=#{c}] ")
end
- if( !result )
+ if !result
Curses.beep
end
- @screen.setpos(0,0)
+ @screen.setpos(0, 0)
end
Curses.close_screen
end
@@ -140,8 +129,8 @@ end
# If we are being run as a main program...
if __FILE__ == $0
- if ARGV.size != 1 then
- printf("usage: #{$0} file\n");
+ unless ARGV.size == 1
+ puts "usage: #{$0} file"
exit
end