#!/usr/bin/env ruby # $Id: rain.rb,v 1.6 2005-08-22 21:41:49 t-peters Exp $ # This program is a translation of the popular rain.c demo program from the # ncurses library distribution. # # Copyright (C) 2002 Tobias Peters # # I do not impose any additional restrictions over the copyright of the # ncurses library distribution. It has the following Copyright notice #/**************************************************************************** # * Copyright (c) 1998 Free Software Foundation, Inc. * # * * # * Permission is hereby granted, free of charge, to any person obtaining a * # * copy of this software and associated documentation files (the * # * "Software"), to deal in the Software without restriction, including * # * without limitation the rights to use, copy, modify, merge, publish, * # * distribute, distribute with modifications, sublicense, and/or sell * # * copies of the Software, and to permit persons to whom the Software is * # * furnished to do so, subject to the following conditions: * # * * # * The above copyright notice and this permission notice shall be included * # * in all copies or substantial portions of the Software. * # * * # * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * # * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * # * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * # * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * # * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * # * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * # * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * # * * # * Except as contained in this notice, the name(s) of the above copyright * # * holders shall not be used in advertising or otherwise to promote the * # * sale, use or other dealings in this Software without prior written * # * authorization. * # ****************************************************************************/ require "ncursesw" # A class responsible for raindrop drawing class Raindrop def initialize (window, color_pair = nil) @window = window @color_pair = color_pair lines = [] columns = [] window.getmaxyx(lines,columns) lines = (lines[0] <= 4) ? 1 : (lines[0] - 4) columns = (columns[0] <= 4) ? 1 : (columns[0] - 4) @current_phase = 0 @x = rand(columns)+2 @y = rand(lines)+2 end # draw_next_phase draws the next phase of a raindrop. If this was the last # phase, returns 0, otherwise returns the raindrop. def draw_next_phase if (@color_pair) if Ncurses.respond_to?(:color_set) @window.color_set(@color_pair, nil) else @window.attrset(Ncurses.COLOR_PAIR(@color_pair)) end end if (DRAWING_PROCS[@current_phase].call(@window,@y,@x)) @current_phase += 1 self end end DRAWING_PROCS = [ Proc.new{|window,y,x| window.mvaddstr(y,x, ".") }, Proc.new{|window,y,x| window.mvaddstr(y, x, "o") }, Proc.new{|window,y,x| window.mvaddstr(y, x, "O") }, Proc.new{|window,y,x| window.mvaddstr(y-1, x, "-") window.mvaddstr(y, x-1, "|.|") window.mvaddstr(y+1, x, "-") }, Proc.new{|window,y,x| window.mvaddstr(y-2, x, "-") window.mvaddstr(y-1, x-1, "/ \\") window.mvaddstr(y, x-2, "| O |") window.mvaddstr(y+1, x-1, "\\ /") window.mvaddstr(y+2, x, "-") }, Proc.new{|window,y,x| window.mvaddstr(y-2, x, " ") window.mvaddstr(y-1, x-1, " ") window.mvaddstr(y, x-2, " ") window.mvaddstr(y+1, x-1, " ") window.mvaddstr(y+2, x, " ") nil # signal the last raindrop phase } ] NUMBER_OF_PHASES = DRAWING_PROCS.size - 1 end # This class creates raindrops and tells them to draw on the screen class Rain AVERAGE_RAINDROP_SPACE = 475.1 # 4 simultaneous raindrops in a 80x24 Window def Rain.sigwinch_handler(sig = nil) ObjectSpace.each_object(Rain){|rain| rain.window_size_changed = true } end attr_writer :window_size_changed def initialize(window) @window = window @window_size_changed = true @raindrops = [] @has_colors = Ncurses.has_colors? if (@has_colors) @current_color = 1 end end def fall_for_a_moment adjust_to_new_window_size if (@window_size_changed) current_number_of_new_raindrops.times{ if (@has_colors) @raindrops.push(Raindrop.new(@window, @current_color)) @current_color = 3 - @current_color # alternate between 1 and 2 else @raindrops.push(Raindrop.new(@window)) end } @raindrops = @raindrops.collect{|raindrop| raindrop.draw_next_phase }.compact # erase raindrops that have expired from the list end def adjust_to_new_window_size @window_size_changed = false window_size = @window.getmaxx * @window.getmaxy average_number_of_raindrops = window_size / AVERAGE_RAINDROP_SPACE @average_number_of_new_raindrops = average_number_of_raindrops / Raindrop::NUMBER_OF_PHASES end def current_number_of_new_raindrops num_floor = @average_number_of_new_raindrops.floor num_ceil = @average_number_of_new_raindrops.ceil chance = @average_number_of_new_raindrops - num_floor if (rand > chance) num_floor else num_ceil end end def fall(pause = 0.1) begin fall_for_a_moment @window.refresh sleep(pause) end while (true) end end Ncurses.initscr begin if (Ncurses.has_colors?) bg = Ncurses::COLOR_BLACK Ncurses.start_color if (Ncurses.respond_to?("use_default_colors")) if (Ncurses.use_default_colors == Ncurses::OK) bg = -1 end end Ncurses.init_pair(1, Ncurses::COLOR_BLUE, bg); Ncurses.init_pair(2, Ncurses::COLOR_CYAN, bg); end Ncurses.nl() Ncurses.noecho() Ncurses.curs_set(0) Ncurses.stdscr.nodelay(true) rain = Rain.new(Ncurses.stdscr) begin case(Ncurses.getch()) when 'q'.ord, 'Q'.ord Ncurses.curs_set(1) Ncurses.endwin() exit when 's'.ord Ncurses.stdscr.nodelay(false) when ' '.ord Ncurses.stdscr.nodelay(true) when Ncurses::KEY_RESIZE Rain.sigwinch_handler end sleep(0.050) rain.fall_for_a_moment Ncurses.refresh end while true ensure Ncurses.curs_set(1) Ncurses.endwin() end