--dDRMvlgZJXvWKvBx
Content-Type: text/plain; charset=iso-2022-jp
Content-Transfer-Encoding: 7bit

s-lang MLにポストしてたんですが、昨日までい浸点え
やはりrubyの専門家にも見ていただいたほうがいいかと
思い、こっちにもクロスポストしようと思います。

では前回までの:

-- 
人生を背負い投げ

菊谷 誠(Kikutani Makoto)  kikutani / sprintmail.com

--dDRMvlgZJXvWKvBx
Content-Type: text/plain; charset=iso-2022-jp
Content-Disposition: attachment; filename=ruby-de-manabu
Content-Transfer-Encoding: 7bit

Date: Sat, 27 Feb 99 05:56:24 +0900
Subject: [s-lang:00701] ruby so-2022-jp?B?GyRCJEczWCRWGyhC? -Lang
 so-2022-jp?B?GyRCMmhMTEApOGYbKEI 
To: s-lang / juice.or.jp (s-lang ML)

きくたにです。昨日は変なメール靴討垢澆泙擦鵝

いや、最近このML止ってるかと思って(debian-usersとクロスポスト
したの届いてないですよね)テストしたのだけど、
開発中のruby/slangのメイラで靴燭藾信部分が腐っていたのでした;

さて、rubyのslang拡張モジュールですが

ftp://ftp.netlab.co.jp/pub/lang/ruby/contrib/slanglib-0.11.tar.gz

にあります。昨日でかいbugを見つけたのでバージョンは上るでしょうが。

しかしエンジニア(特にプログラマ?)共通の気質、

     「ドキュメントをのは大嫌いだ;

によって何も説明が入ってません。

けれど、メールをのは好きなので、ここで連載の形で説明したいえ
思います。のない人ごめんなさい。

ruby-listとクロスにしようかとも思ったけど、(まつもとさんが
まだいるか知りませんが)前田さんが嘘をチェックしてくれるだろうから
こっちだけにします。

-- 
人生を背負い投げ

菊谷 誠(Kikutani Makoto)  kikutani / sprintmail.com

Date: Sat, 27 Feb 99 07:01:36 +0900
Subject: [s-lang:00702] Re: ruby so-2022-jp?B?GyRCJEczWCRWGyhC? -Lang 
 so-2022-jp?B?GyRCMmhMTEApOGYbKEI 
To: s-lang / juice.or.jp (s-lang ML)

rubyのslang拡張モジュールは、SLsmg_ の関数をrubyから使えるようにした
ものです。たぶんこの接頭語は SLang Screen ManaGer かなんかの略。
これはslangのスに含まれる cslang.txt というドキュメントに
解説されてます。

気になればインタプリタ言語としてのS-Langを使えるようにも
できるけど、まあrubyから使う必要はあまりないでしょう。

いうわけで、以後は画面制御の話だけになります。

[1] 仮想ディスプレイ

ディスプレイモデル非常に単す。画面の左上を座標(0,0)とし、
SLsmg_gotorc で描きたい場帽圓辰栃源造体)をと;

ではrubyでの簡単な例。

---------------------------------------------------------
require "slang"
include Slang

slang_init_terminal(1, 1)

slsmg_cls

slsmg_gotorc( (sltt_screen_rows-1)/2, (sltt_screen_cols-1)/2-1 )
slsmg_set_color SL_BG_WHITE+SL_RED
slsmg_write_string ">*<"
slsmg_set_color SL_BG_WHITE+SL_BLACK
slsmg_write_string "←ここが真中じゃよ"
slsmg_refresh
slkp_getkey

sl_reset
exit
---------------------------------------------------------

最造孫圓rubyからslang拡張を使うためのおまじない。
画面制御ライブラリを使うためには、まず slang_init_terminal(1, 1)
を呼びます。とりあえず引数の意味は無視しましょう。てプログラムを
する前に必ず sl_reset を呼びます。でないと抜けたあと画面が変になります。

slsmg_cls で画面を全部消します。slsmg_gotorc で画面中央に行きます。
slsmg_gotorcの第一引数が行数、第二が桁。
sltt_screen_rows と sltt_screen_cols はシステム変数で、rxvt等の
ミ伐の文字数です。これえ択瑤拡大すれば変ます。

なお、rubyのメド(関数みたいなもん)の引数は括弧でくくらなくても
いいのですが、上のslsmg_gotorcでは直後に括弧が来るんで全体を括弧で
くくらないと文法エラーになります。

slsmg_set_color で色を指定して slsmg_write_string で文字列描きます。
SL_BG_WHITE や SL_RED はシステム定数で、バックグラウンドやフォアグラウンド
の色を指定するのの使います。
いくらこれらで描いても、最後に slsmg_refresh が呼ばれるまで
しません。いちいち描画してたら画面がチカチカするでしょう。

slkp_getkey は下級のキー入力で、何かキーが押されるまで待ちます。
もう少し上等な slang_getkey というのもありますけど。

# 鵑呂海鵑覆箸海蹐え

 
人生を背負い投げ

菊谷 誠(Kikutani Makoto)  kikutani / sprintmail.com


Date: Sat, 27 Feb 99 09:11:48 +0900
Subject: [s-lang:00703] Re: ruby so-2022-jp?B?GyRCJEczWCRWGyhC? -Lang 
 so-2022-jp?B?GyRCMmhMTEApOGYbKEI 
To: s-lang / juice.or.jp (s-lang ML)

金曜の夜なんか仕事する気にならぬので続けてしまおう。

[2] Pagerクラス

いきなりslang拡張モジュールの核心、Pagerクラスっす。

slangアプリのslrnとかmuttとか眺めてると「基本はページャ」
であることに気付きます。slrnで言えば、記事本体を読むページャ、
あるNewsgroupのサブジェクトを見るページャ、Newsgroup全体のページャ
という具合。ヘルプ画面もページャでしょ?

で汎用的なページャ用クラスを作りました。
典型的な使用例がslanglibのスにサンプルとしてpager.rbが入ってます。
短いのでて解説してしまおう。

----------------------------------------------------------------------
#!/usr/local/bin/ruby
require "kconv"
require "slang"
include Slang

if ENV['LANG'] /sjis/i # 文字コードに関係なく読めるように
  $lang  conv::SJIS
else
  $lang  conv::EUC
end

class Pager2 < Pager # ページャクラスのサブクラスを作る
  def build_lines # newで呼ばれるメド
    if $file nil  
      fp  stdin;
    else 
      begin
	fp  pen($file, "r")
      rescue
	printf($stderr, "Unable to read %s\n", $file)
	exit
      end
    end

    while fp.gets
      str  conv.kconv($_, $lang, Kconv::AUTO)
      add_line(str) # Pagerクラスで定義されている。一行加える
    end
    
    init_lines # Pagerクラスで定義されている
  end

 def draw_bottom # 画面最下部のステータス行に何をするか
   slsmg_gotorc(@rmax, @cmin)
   reverse_color
   if $file
     slsmg_printf("%s ", $file)
   else
     slsmg_printf("<stdin> ")
   end
   slsmg_erase_eol
 end

end

# --------- main 

if ARGV.length 1   # 引数があればファイルとして読み
  $file  RGV.shift  # なければ標力かえ
繻黼
  蜀 倉盃跂鈑  ん粡隨    頏蜴罔ん粤鴪專瘍絅 枡斌杜鼠倒樌↑ ぐ
    纔蜚
  緕  ゆ蛹 蛹
緕
鼬瘤艤迴黼熄 マウスを使おう
exit unless slang_init_terminal(1, 1)

# クラスにインスタンス(実体襖息
 瘍纈荻鈬┛ 鼬燗竰繞釼鳫鶚碓 鼬燗竰繞釼竢踈
キーやマウスのキーシーケンスの定義
slkp_define_keysym("\033>", PAGER_EOB)
slkp_define_keysym("\033<", PAGER_BOB)
slkp_define_keysym("q", PAGER_QUIT)
slkp_define_keysym("b", PAGER_PPAGE)
slkp_define_keysym(" ", PAGER_NPAGE)
slkp_define_keysym("\033[M\040", SL_MOUSE_B0)
slkp_define_keysym("\033[M\041", SL_MOUSE_B1)
slkp_define_keysym("\033[M\042", SL_MOUSE_B2)
# インスタンスのメインを回す
p.main_loop
----------------------------------------------------------------------
今見直すと $fileなどというグローバル変数で渡してるのは
ダサダサだけど、昔のコーディングということで勘弁。

このくらいの短かいので、一応まともなページャになるのが面白いでしょ。
マウスを使っているのは蛇足かも。ステータス行をマウスの
左クリックか右クリックするとスクロールするのです。
左右カーで水平スクロールもできますよ。

しかし、上のrubyスを眺めただけでは、なぜ動作するか
さっぱりわからないでしょう。スーパークラスたるPagerクラスの
中身がわからないので当然です。

では次回からを説明します。

-- 
人生を背負い投げ

菊谷 誠(Kikutani Makoto)  kikutani / sprintmail.com


Date: Thu, 4 Mar 99 08:53:42 +0900
Subject: [s-lang:00704] Re: ruby so-2022-jp?B?GyRCJEczWCRWGyhC? -Lang 
 so-2022-jp?B?GyRCMmhMTEApOGYbKEI 
To: s-lang / juice.or.jp (s-lang ML)

Sat, Feb 27, 1999 at 09:11:48AM +0900 において
Kikutani, Makoto 曰く:

> しかし、上のrubyスを眺めただけでは、なぜ動作するか
> さっぱりわからないでしょう。スーパークラスたるPagerクラスの
> 中身がわからないので当然です。
> 
> では次回からを説明します。

間が空いてしまった。

普通にslanglibを入れてrubyをmake install すると
/usr/local/lib/ruby/slang.rb などができてますが、
ここにPagerクラスの定義があります。

まずはメインループ。

    def main_loop
      @do_break  ALSE
      while TRUE
	check_winch
	update_display
	key_actions_call(slkp_getkey)
	break if @do_break
      end
    end

インスタンス変数の@do_breakがTRUEになるまで無限ループします。
は key_actions_call のどっかでTRUEになるでしょう(q押されたとか)。
普通は key_actions_call というのは slkp_getkey で受けとったキー入力により
画面をスクロールしたりします。
check_winchはrxvtのサイズが変更されたかチェック
(ちょっとこの実装はまだ貧弱)。

update_display が画面の中心で以下のメドです。

    def update_display
      slsig_block_signals # CTL-Cとか受けつけないように
      
      normal_color
      
      update_region(@rmin, @rmax, @cmin, @cmax, @col_start, -1)
      draw_bottom
      if @with_mini_buffer
        slsmg_gotorc(sltt_screen_rows-1, 0) 
	slsmg_erase_eol
      end
      slsmg_gotorc(@rmin, @cmin)

      slsmg_refresh
      slsig_unblock_signals # CTL-Cとか受けつけるように
    end

ホンモノではカー移動がある場合とで分けてるけどここでは省略。
上でやってるのは、指定された領域をupdate_regionで描画した
のちdraw_bottomでステータス行を、ミニバッファ付きなら
(画面最下部)をクリアしとくと。

指定された領域というのはPagerクラス(かサブクラス)
のインスタンスを作るとき、x  ager.new(x0, y0, x1, y1) のように
指定します。update_region はCでれたルーチンを呼ぶだえ

    粤鞣癆縷鱚芍闔皙 絳 竄竇閹罨 笊鳰蜴紿
      鱚鴈 蜀   竇       栴竰鞣癆縷鱚芍闔黼趾皙 絳 竄竇閹罨 笊鳰蜴紿
    緕鞣癆縷鱚芍
あー、だんだん説明が困難になってきた。実体はこれです。

rubyの拡張モジュールの講座ではないので詳しく説明できませんので
わからないところは無視しましょう。

static VALUE
scrW_update_region(obj, self, rbegin, rend, cbegin, 
		   cend, offset, cursor_line)
    VALUE obj;
    VALUE self;
    VALUE rbegin;
    VALUE rend;
    VALUE cbegin;
    VALUE cend;
    VALUE offset;
    VALUE cursor_line;
{
   int row;
   File_Line_Type *line;
   scrWdata *scrWp;
   int b  UM2INT(rbegin); // 領域の行のはじまり
   int e  UM2INT(rend);   //           おわり
   int cb  UM2INT(cbegin); // 領域の列のはじまり
   int ce  UM2INT(cend);   //           おわり
   int off  UM2INT(offset); // 水平スクロールのときのオフセット
   int curline  UM2INT(cursor_line); // カー行
   Data_Get_Struct(obj, scrWdata, scrWp);

   if (e <  || ce < b) return FALSE;
   scrWp->Line_Window->nrows   - b + 1;
   if (scrWp->Line_Window->top_window_line ! ULL) {
      scrWp->Line_Window->current_line  crWp->Line_Window->top_window_line;
      SLscroll_find_line_num (scrWp->Line_Window);
   }
   
   SLscroll_find_top (scrWp->Line_Window);

   row  ;
   line  File_Line_Type *) scrWp->Line_Window->top_window_line;
   // キモはここからっすよ   
   while (row < e) { // b から e までの行をアップデートします
      SLsmg_gotorc (row, cb);
   
      // カー移動モードのときカーのある行に色をつける
      if (SLtt_Use_Ansi_Colors) {
	 if (row curline)
	   SLsmg_set_color(scrWp->cursor_line_color);
	 else
	   SLsmg_set_color(scrWp->normal_color);
      }
      else {
	 if (row curline)
	   SLsmg_normal_video ();
	 else
	   SLsmg_reverse_video ();
      }

      if (line NULL) { // ドキュメントの最後で空の部分にチルダを
	 if (scrWp->tilde ! )
	   SLsmg_write_char ('~');
      }
      else { // 実際に一行を
	 蜀 鰲熬纉竟鈔熹鍜黼趾 鰲煢銓纈遖†鱸縷跚鈬〒	    崛盲癇莓朮飮
	    癇莓朧 蜴絖鞘癆畛
	    癇莓霸 鳫笊鳰蜴紿 墟嫖 徳毛纏
	    鰲煕釿瘡豌黼趾鰲煢銓纈遖†鱸縷跚鈬〒 岡 癇莓
	 	 繻黼 嗅鼾艤鱸縷竏癇 ├色

	 跚鈬  蜴絖嬢纔
            纈癈縷繽讎竇黹鰈隲鐘癇


      鳫
      鱚鴈 墟嫖
鰲煕釿瘡譴箸いΔ里苦心のあとでしてね、単に一行だけなえ
嗅鼾艤鱸縷齡鱸鈑 かなんか呼べば済む話。しかしでは面白くない。
ここで、ruby側で定義したwrite_lineというのを呼んでるのです。
すると、たとえば中のメイラでは

  def write_line(l, cursor)
    line  .data
    .....
    elsif line $quote_pattern  # quote
      slsmg_set_color($quote_color)
    .....
    sl_write_string_with_offset(line, @col_start, @cmax)
    slsmg_erase_eol
  end

などとなって、引用された部分の行だけ色を変えられるわけです。

C側で line->data の体データを rubyでは l.data のように
受けるのですが、ここのデータは単なる文字列ではなく、rubyの
インスタンスだったりもするので、一行の中でフィールド別に色を
変えたりすることもできます。この例はslang.rbのTree_Pagerという、
Pagerクラスのサブクラスに例があります。


はー疲れた。続く(かもしれない)。

-- 
人生を背負い投げ

菊谷 誠(Kikutani Makoto)  kikutani / sprintmail.com

--dDRMvlgZJXvWKvBx--