【Ruby】GTKでなんか外部から攻撃を受けているときのアレを再現

GTKを使って、アニメなどでよくあるオペレータやハッカーっぽい人がなんか外部から攻撃を受けているとき画面いっぱいに警告ウィンドウが出てくるアレをつくりました。

![warning_window.gif](https://qiita-image-store.s3.amazonaws.com/0/54587/b5a885e0-6a49-11f6-9c29-d5c773472a3e.gif)


## 動作環境

* Ubuntu 14.04
* Ruby 2.2.0
* gtk2 3.0.7 (gem)

必要なもののインストール

    $ gem install gtk2


## ソースコード

使用した画像はソースコードと併せてここ(→ [https://gist.github.com/seinosuke/9e615852063807427dab](https://gist.github.com/seinosuke/9e615852063807427dab) )においてあります。自分ではしょぼい画像しかつくれなかったので、もっとそれっぽい画像を用意すればそれっぽさが増すと思います。

ソースコードは以下の通りです。Rubyです。

```ruby:main.rb
require 'gtk2'
include Math

WIDTH = 1280 # 画面の幅
HEIGHT = 800 # 画面の高さ
MAX = 30 # 表示するウィンドウの数

# 使用する色
COLOR_DICT = {
  :yellow => Gdk::Color.new(65535, 65535, 0),
  :black => Gdk::Color.new(0, 0, 0),
}
colormap = Gdk::Colormap.system
COLOR_DICT.each do |_, color|
  colormap.alloc_color(color, false, true)
end

# 生成される警告ウィンドウ
class WarningWindow < Gtk::Window
  def initialize
    super(Gtk::Window::POPUP)
    @width = 300  # ウィンドウの幅
    @height = 150 # ウィンドウの高さ

    m = [WIDTH/2 - @width/2, HEIGHT/2 - @height/2]
    s = [WIDTH/8, HEIGHT/8]
    @x, @y = box_muller(m, s)
    @x_adder = [-2, -1, 1, 2].sample
    @y_adder = [-2, -1, 1, 2].sample

    self.move(@x, @y)
    self.set_size_request(@width, @height)
    self.set_app_paintable(true)
    self.set_title("WARNING!!")

    self.realize
    @drawable = self.window
    @gc = Gdk::GC.new(@drawable)
    @gc.set_line_attributes(20, Gdk::GC::LINE_SOLID, Gdk::GC::CAP_ROUND, Gdk::GC::JOIN_BEVEL)

    # 画像を表示
    source = ["./msg_warning.png", "./msg_danger.png"].sample
    @image = Gtk::Image.new(source)
    self.add(@image)

    # 縞模様用のカウンタ
    @counters = (-1..5).map do |i|
      ->(init, max) do
        n = init
        -> { n == max - 1 ? n = -50 : n += 1 }
      end[i*50, @width]
    end
  end

  # 縞模様を流すアニメーション
  def add_task
    Gtk.timeout_add(20) do
      @x += @x_adder
      @y += @y_adder
      @x_adder *= -1 if @x < 0 || @x > WIDTH - @width
      @y_adder *= -1 if @y < 0 || @y > HEIGHT - @height
      # self.move(@x, @y)
      clear
      draw
    end
  end

  # 黒い斜め線を描画
  def draw
    @gc.set_foreground(COLOR_DICT[:black])
    @counters.each do |counter|
      from = [counter[], 0]
      to = [from[0] + 50 ,@height]
      @drawable.draw_line(@gc, *from, *to)
    end
  end

  # 背景を黄色で描画
  def clear
    @gc.set_foreground(COLOR_DICT[:yellow])
    @drawable.draw_rectangle(@gc, true, 0, 0, @width, @height)
  end

  private

  # ウィンドウの出現位置を散らすため
  def box_muller(m = [0, 0], s = [1, 1])
    r1 = rand
    r2 = rand
    x = sqrt(-2 * log(r1)) * cos(2*PI * r2)
    y = sqrt(-2 * log(r1)) * sin(2*PI * r2)
    [s[0] * x + m[0], s[1] * y + m[1]]
  end
end


wins = []
Gtk.timeout_add(100) do
  wins << WarningWindow.new
  wins.last.show_all

  # たまにどれか消える
  if rand < 0.3
    win = wins.sample
    win.destroy
    wins.delete(win)
  end

  # MAXを越えたらこれ以上ウィンドウは増えない
  if wins.size > MAX
    sleep 1
    wins.each(&:add_task)
    true
  end.!
end

begin
  Gtk.main
rescue Interrupt
  Gtk.main_quit
end
```

add_task メソッド内は指定した数のウィンドウが全て出力し終わったら定期的に実行される処理です(最後に縞模様を流してるのはそれっぽい演出のつもりです)。その中の `self.move(@x, @y)` のコメントアウトを外すと以下のように画面内をウィンドウがごちゃごちゃ動き回るようになります。

![warning_window_move.gif](https://qiita-image-store.s3.amazonaws.com/0/54587/70ca80e2-9d9f-74f5-67dc-e9d8c7343acb.gif)



## おわりに

これを使えば誰でも、なんか攻撃を受けてるハッカーごっこができます。また、突然画面を隠さないといけない時などにも便利です。

GTKについてはまだあまり詳しくないので、ご意見ご指摘等ありましたらよろしくお願いします。

参考サイト
[ウインドウへの直接描画(Gdk::Drawable編)](http://ruby-gnome2.osdn.jp/ja/hiki.cgi?%A5%A6%A5%A4%A5%F3%A5%C9%A5%A6%A4%D8%A4%CE%C4%BE%C0%DC%C9%C1%B2%E8%28Gdk%3A%3ADrawable%CA%D4%29)
[自作ペイントソフトで筆のような効果を出す ](http://enthusiastickcoding.blogspot.jp/2013/05/blog-post.html)