From c5965c88d9cbc3d900b9fb78fa6025382523e8ca Mon Sep 17 00:00:00 2001 From: Andrew Shadura Date: Tue, 24 Apr 2018 17:35:40 +0200 Subject: New upstream version 1.2.4 --- .gitignore | 15 ++ .gitmodules | 3 + .travis.yml | 13 ++ Gemfile | 4 + History.md | 90 ++++++++ Manifest.txt | 16 -- README.md | 66 +++++- Rakefile | 95 +++++--- appveyor.yml | 16 ++ checksums.yaml.gz.sig | Bin 256 -> 0 bytes curses.gemspec | 18 +- data.tar.gz.sig | Bin 256 -> 0 bytes ext/curses/curses.c | 600 ++++++++++++++++++++++++++++++++++++++++++++++---- ext/curses/extconf.rb | 28 ++- lib/curses.rb | 24 +- metadata.gz.sig | 1 - sample/hello.rb | 27 ++- sample/mouse.rb | 32 +-- sample/rain.rb | 96 ++++---- sample/view.rb | 61 ++--- sample/view2.rb | 85 ++++--- 21 files changed, 1024 insertions(+), 266 deletions(-) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .travis.yml create mode 100644 Gemfile delete mode 100644 Manifest.txt create mode 100644 appveyor.yml delete mode 100644 checksums.yaml.gz.sig delete mode 100644 data.tar.gz.sig delete mode 100644 metadata.gz.sig 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 Binary files a/checksums.yaml.gz.sig and /dev/null 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 Binary files a/data.tar.gz.sig and /dev/null 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 -- cgit v1.2.3