pixelflut 0.0.13 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +13 -75
- data/bin/pxf +74 -23
- data/lib/pixelflut.rb +17 -6
- data/lib/pixelflut/convert.rb +28 -0
- data/lib/pixelflut/image.rb +32 -0
- data/lib/pixelflut/sender.rb +53 -0
- data/lib/pixelflut/version.rb +1 -1
- data/pixelflut.gemspec +8 -7
- metadata +18 -41
- data/TODO.md +0 -11
- data/bin/pxf-convert +0 -55
- data/bin/pxf-generate +0 -35
- data/bin/pxf-help +0 -23
- data/bin/pxf-info +0 -55
- data/bin/pxf-send +0 -44
- data/bin/pxf-server +0 -64
- data/bin/pxf-version +0 -17
- data/lib/pixelflut/app.rb +0 -81
- data/lib/pixelflut/canvas.rb +0 -4
- data/lib/pixelflut/canvas/base.rb +0 -99
- data/lib/pixelflut/canvas/buffered.rb +0 -27
- data/lib/pixelflut/canvas/streamed.rb +0 -20
- data/lib/pixelflut/client.rb +0 -35
- data/lib/pixelflut/client/socket.rb +0 -93
- data/lib/pixelflut/converter.rb +0 -53
- data/lib/pixelflut/server.rb +0 -58
- data/lib/pixelflut/server/configuration.rb +0 -26
- data/lib/pixelflut/server/connection.rb +0 -98
- data/lib/pixelflut/text_image.rb +0 -89
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
module Canvas
|
7
|
-
class Buffered < Base
|
8
|
-
def clear!
|
9
|
-
@lines = []
|
10
|
-
end
|
11
|
-
|
12
|
-
def each(&block)
|
13
|
-
block ? @lines.each(&block) : to_enum(__method__)
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_s
|
17
|
-
@lines.join
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def out(str)
|
23
|
-
@lines << str
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
module Canvas
|
7
|
-
class Streamed < Base
|
8
|
-
def initialize(stream = $stdout)
|
9
|
-
super()
|
10
|
-
@stream = stream
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
def out(str)
|
16
|
-
@stream.print(str)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/pixelflut/client.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'client/socket'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
class Client
|
7
|
-
def initialize(host, port, data, socket_count)
|
8
|
-
@sockets = create_sockets(Addrinfo.tcp(host, port), sliced(data, socket_count))
|
9
|
-
end
|
10
|
-
|
11
|
-
def run
|
12
|
-
loop{ break if call }
|
13
|
-
end
|
14
|
-
|
15
|
-
def call
|
16
|
-
@sockets.size == @sockets.count do |socket|
|
17
|
-
socket.call
|
18
|
-
socket.state == :closed
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def sliced(data, count)
|
25
|
-
data = data.split("\n") if data.is_a?(String)
|
26
|
-
data = data.each_slice(data.size / count).to_a
|
27
|
-
data[-2] += data.pop unless (data.size % count).zero?
|
28
|
-
data.map!(&:join)
|
29
|
-
end
|
30
|
-
|
31
|
-
def create_sockets(address, slices)
|
32
|
-
slices.map{ |data| NonblockSocket.new(address, data) }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'socket'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
class Client
|
7
|
-
class Socket < ::Socket
|
8
|
-
def initialize(address)
|
9
|
-
super(address.ipv6? ? :INET6 : :INET, :STREAM)
|
10
|
-
@addr = ::Socket.pack_sockaddr_in(address.ip_port, address.ip_address)
|
11
|
-
configure
|
12
|
-
end
|
13
|
-
|
14
|
-
def write_with_timout(data, timeout)
|
15
|
-
return true if write_nonblock(data, exception: false) == data.bytesize
|
16
|
-
return false unless wait_writable(timeout)
|
17
|
-
write_nonblock(data, exception: false) == data.bytesize
|
18
|
-
end
|
19
|
-
|
20
|
-
def readline_with_timeout(timeout)
|
21
|
-
deadline = Time.now + timeout
|
22
|
-
ret = ''
|
23
|
-
loop do
|
24
|
-
got = read_nonblock(16, exception: false)
|
25
|
-
if got == :wait_readable
|
26
|
-
remaining_time = deadline - Time.now
|
27
|
-
return nil if remaining_time <= 0 || wait_readable(remaining_time).nil?
|
28
|
-
next
|
29
|
-
end
|
30
|
-
idx = got.index("\n")
|
31
|
-
next ret += got unless idx
|
32
|
-
return ret + got[0, idx]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def connect?
|
37
|
-
:wait_writable != connect_nonblock(@addr, exception: false)
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def configure
|
43
|
-
self.sync = true
|
44
|
-
setsockopt(:TCP, :NODELAY, 1)
|
45
|
-
setsockopt(:SOCKET, :KEEPALIVE, 0)
|
46
|
-
self.do_not_reverse_lookup = true
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
class NonblockSocket < Socket
|
51
|
-
attr_reader :state
|
52
|
-
|
53
|
-
def initialize(address, data)
|
54
|
-
super(address)
|
55
|
-
@state = :not_connected
|
56
|
-
@data = data
|
57
|
-
@size = data.bytesize
|
58
|
-
end
|
59
|
-
|
60
|
-
def call
|
61
|
-
case @state
|
62
|
-
when :not_connected
|
63
|
-
do_connect
|
64
|
-
when :wait_connect
|
65
|
-
do_wait_for_connect
|
66
|
-
when :write
|
67
|
-
do_write
|
68
|
-
else
|
69
|
-
close
|
70
|
-
@state = :closed
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
def do_connect
|
77
|
-
@state = connect? ? :write : :wait_connect
|
78
|
-
end
|
79
|
-
|
80
|
-
def do_wait_for_connect
|
81
|
-
@state = :not_connected unless wait_writable(0.1).nil?
|
82
|
-
end
|
83
|
-
|
84
|
-
def do_write
|
85
|
-
written = write_nonblock(@data, exception: false)
|
86
|
-
return written if Symbol === written
|
87
|
-
@size -= written
|
88
|
-
return @state = :write_finished if @size <= 0
|
89
|
-
@data = @data.byteslice(written, @data.bytesize - written)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
data/lib/pixelflut/converter.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pixelflut
|
4
|
-
class Converter
|
5
|
-
Error = Class.new(RuntimeError)
|
6
|
-
end
|
7
|
-
|
8
|
-
begin
|
9
|
-
require 'rmagick'
|
10
|
-
|
11
|
-
class Converter
|
12
|
-
AVAIL = true
|
13
|
-
|
14
|
-
def initialize(file_name)
|
15
|
-
@images = load_images(file_name)
|
16
|
-
end
|
17
|
-
|
18
|
-
def resize_to(width, height = nil)
|
19
|
-
@images.each{ |image| image.resize_to_fit!(width, height) }
|
20
|
-
end
|
21
|
-
|
22
|
-
def each_pixel
|
23
|
-
return to_enum(__method__) unless block_given?
|
24
|
-
@images.each do |image|
|
25
|
-
image.each_pixel{ |color, x, y| yield(x, y, color) }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def each_rgba_pixel
|
30
|
-
return to_enum(__method__) unless block_given?
|
31
|
-
each_pixel{ |x, y, color| yield(x, y, as_rgba(color)) unless 0xffff == color.opacity }
|
32
|
-
end
|
33
|
-
|
34
|
-
def draw(canvas)
|
35
|
-
each_rgba_pixel{ |x, y, rgba| canvas[x, y] = rgba }
|
36
|
-
end
|
37
|
-
|
38
|
-
def as_rgba(color)
|
39
|
-
color.to_color(Magick::AllCompliance, true, 8, true)[1, 8]
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def load_images(file_name)
|
45
|
-
Magick::ImageList.new(file_name)
|
46
|
-
rescue Magick::ImageMagickError => e
|
47
|
-
raise(Error, e.message)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
rescue LoadError
|
51
|
-
Converter::AVAIL = false
|
52
|
-
end
|
53
|
-
end
|
data/lib/pixelflut/server.rb
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'socket'
|
4
|
-
require_relative 'server/connection'
|
5
|
-
require_relative 'server/configuration'
|
6
|
-
|
7
|
-
module Pixelflut
|
8
|
-
class Server
|
9
|
-
attr_reader :config
|
10
|
-
|
11
|
-
def initialize(canvas, config = Configuration.default)
|
12
|
-
@canvas, @config = canvas, config
|
13
|
-
@socket, @connections = nil, {}
|
14
|
-
@peers = Hash.new{ |h, k| h[k] = 0 }
|
15
|
-
@ccfg = Connection::Configuration.new(
|
16
|
-
keep_alive_time: config.keep_alive_time,
|
17
|
-
read_buffer_size: config.read_buffer_size,
|
18
|
-
command_limit: config.command_limit,
|
19
|
-
canvas: canvas,
|
20
|
-
size_result: "SIZE #{canvas.width} #{canvas.height}\n".freeze,
|
21
|
-
on_end: ->(conn){ @peers[conn.peeraddr] -= 1 if @connections.delete(conn) }
|
22
|
-
).freeze
|
23
|
-
end
|
24
|
-
|
25
|
-
def connection_count
|
26
|
-
@connections.size
|
27
|
-
end
|
28
|
-
|
29
|
-
def update
|
30
|
-
return create_socket unless @socket
|
31
|
-
incoming = @socket.accept_nonblock(exception: false)
|
32
|
-
create_connection(incoming) unless Symbol === incoming
|
33
|
-
@connections.keys.each(&:update)
|
34
|
-
end
|
35
|
-
|
36
|
-
def run
|
37
|
-
Thread.new do
|
38
|
-
loop{ update }
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def create_connection(socket)
|
45
|
-
peeraddr = socket.peeraddr(false)[-1]
|
46
|
-
count = @peers[peeraddr] + 1
|
47
|
-
return socket.close if count > @config.peer_limit
|
48
|
-
@peers[peeraddr] = count
|
49
|
-
con = Connection.new(socket, peeraddr, @ccfg)
|
50
|
-
@connections[con] = con
|
51
|
-
end
|
52
|
-
|
53
|
-
def create_socket
|
54
|
-
@socket = @config.host ? TCPServer.new(@config.host, @config.port) : TCPServer.new(@config.port)
|
55
|
-
@socket.listen(255)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pixelflut
|
4
|
-
class Server
|
5
|
-
Configuration = Struct.new(
|
6
|
-
:host,
|
7
|
-
:port,
|
8
|
-
:keep_alive_time,
|
9
|
-
:read_buffer_size,
|
10
|
-
:command_limit,
|
11
|
-
:peer_limit
|
12
|
-
) do
|
13
|
-
def self.default
|
14
|
-
new(nil, 1234, 1, 1024, 10, 8)
|
15
|
-
end
|
16
|
-
|
17
|
-
def to_s
|
18
|
-
"bind: #{host}:#{port}"\
|
19
|
-
", keep-alive-time: #{keep_alive_time}"\
|
20
|
-
", read-buffer-size: #{read_buffer_size}"\
|
21
|
-
", command-limit: #{command_limit}"\
|
22
|
-
", peer-limit: #{peer_limit}"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,98 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'socket'
|
4
|
-
|
5
|
-
module Pixelflut
|
6
|
-
class Server
|
7
|
-
class Connection
|
8
|
-
Configuration = Struct.new(
|
9
|
-
:keep_alive_time,
|
10
|
-
:read_buffer_size,
|
11
|
-
:command_limit,
|
12
|
-
:canvas,
|
13
|
-
:size_result,
|
14
|
-
:on_end,
|
15
|
-
keyword_init: true
|
16
|
-
)
|
17
|
-
|
18
|
-
attr_reader :peeraddr
|
19
|
-
|
20
|
-
def initialize(socket, peeraddr, config)
|
21
|
-
@socket, @peeraddr, @config = socket, peeraddr, config
|
22
|
-
@last_tm, @buffer = Time.now.to_f, ''
|
23
|
-
end
|
24
|
-
|
25
|
-
def close(_reason)
|
26
|
-
socket, @socket = @socket, nil
|
27
|
-
return unless socket
|
28
|
-
socket.close
|
29
|
-
@config.on_end.call(self)
|
30
|
-
false
|
31
|
-
end
|
32
|
-
|
33
|
-
def update
|
34
|
-
index = @buffer.index("\n")
|
35
|
-
return process_loop(index) if index
|
36
|
-
read_size = @config.read_buffer_size - @buffer.size
|
37
|
-
return close(:buffer_exceeded) if read_size <= 0
|
38
|
-
str = @socket.recv_nonblock(read_size, exception: false)
|
39
|
-
now = Time.now.to_f
|
40
|
-
return (now - @last_tm > @config.keep_alive_time ? close(:timeout) : nil) if Symbol === str
|
41
|
-
return close(:closed_by_peer) if str.size.zero?
|
42
|
-
@buffer += str
|
43
|
-
@last_tm = now
|
44
|
-
rescue Errno::ECONNRESET
|
45
|
-
close(:closed_by_peer)
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def next_command(index)
|
51
|
-
@buffer = @buffer[index, @buffer.size - index]
|
52
|
-
@last_tm = Time.now.to_f
|
53
|
-
true
|
54
|
-
end
|
55
|
-
|
56
|
-
def command_size(index)
|
57
|
-
@socket.sendmsg_nonblock(@config.size_result)
|
58
|
-
next_command(index)
|
59
|
-
end
|
60
|
-
|
61
|
-
def command_px(command, index)
|
62
|
-
_, x, y, color = command.split(' ', 4)
|
63
|
-
return close(:color_expected) unless color
|
64
|
-
@config.canvas[x.to_i, y.to_i] = color
|
65
|
-
next_command(index)
|
66
|
-
end
|
67
|
-
|
68
|
-
def command_rc(command, index)
|
69
|
-
_, x1, y1, x2, y2, color = command.split(' ', 6)
|
70
|
-
return close(:color_expected) unless color
|
71
|
-
@config.canvas.draw_rect(x1.to_i, y1.to_i, x2.to_i, y2.to_i, color)
|
72
|
-
next_command(index)
|
73
|
-
end
|
74
|
-
|
75
|
-
def process_loop(index)
|
76
|
-
command_count = @config.command_limit
|
77
|
-
while process_buffer(index)
|
78
|
-
index = @buffer.index("\n") or return
|
79
|
-
command_count -= 1
|
80
|
-
break if command_count <= 0
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def process_buffer(index)
|
85
|
-
return close(:max_command_size_exceeded) if index > 31 # 'RC 9999 9999 9999 9999 RRGGBBAA'.size
|
86
|
-
command = @buffer[0, index]
|
87
|
-
index += 1
|
88
|
-
return command_px(command, index) if command.start_with?('PX ')
|
89
|
-
return command_rc(command, index) if command.start_with?('RC ')
|
90
|
-
return close(:quit) if command == 'QUIT'
|
91
|
-
return command_size(index) if command == 'SIZE'
|
92
|
-
close(:bad_command)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
private_constant(:Connection)
|
97
|
-
end
|
98
|
-
end
|
data/lib/pixelflut/text_image.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pixelflut
|
4
|
-
class TextImage
|
5
|
-
attr_reader :columns, :rows, :to_blob, :changes
|
6
|
-
|
7
|
-
def initialize(width, height)
|
8
|
-
@columns, @rows = width, height
|
9
|
-
@row_inc = width * 4
|
10
|
-
clear
|
11
|
-
end
|
12
|
-
|
13
|
-
alias width columns
|
14
|
-
alias height rows
|
15
|
-
|
16
|
-
def clear
|
17
|
-
@to_blob = Black * (@columns * @rows)
|
18
|
-
@changes = 1
|
19
|
-
end
|
20
|
-
|
21
|
-
def changed
|
22
|
-
@changes = 0
|
23
|
-
self
|
24
|
-
end
|
25
|
-
|
26
|
-
# def [](x, y)
|
27
|
-
# @@to_blob[(4 * (x + @columns * y)) % @data.size, 4].bytes.map! do |b|
|
28
|
-
# b = b.to_s(16)
|
29
|
-
# b = '0' + b if b.size == 1
|
30
|
-
# b
|
31
|
-
# end.join
|
32
|
-
# end
|
33
|
-
|
34
|
-
def []=(x, y, rrggbbaa)
|
35
|
-
@to_blob[(4 * (x + @columns * y)) % @to_blob.size, 4] = as_color(rrggbbaa)
|
36
|
-
@changes += 1
|
37
|
-
end
|
38
|
-
|
39
|
-
def draw_rect(x1, y1, x2, y2, rrggbbaa)
|
40
|
-
x1, x2 = x2, x1 if x1 > x2
|
41
|
-
y1, y2 = y2, y1 if y1 > y2
|
42
|
-
color = as_color(rrggbbaa)
|
43
|
-
pos = (4 * (x1 + @columns * y1)) % @to_blob.size
|
44
|
-
pattern = color * (x2 - x1 + 1)
|
45
|
-
(y2 - y1 + 1).times do
|
46
|
-
@to_blob[pos, pattern.size] = pattern
|
47
|
-
pos += @row_inc
|
48
|
-
end
|
49
|
-
@changes += 1
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
ZZ = 0.chr.freeze
|
55
|
-
FF = 0xff.chr.freeze
|
56
|
-
Black = (ZZ + ZZ + ZZ + FF).freeze
|
57
|
-
|
58
|
-
def as_color(rrggbbaa)
|
59
|
-
case rrggbbaa.size
|
60
|
-
when 3 # RGB
|
61
|
-
(rrggbbaa[0] * 2).to_i(16).chr +
|
62
|
-
(rrggbbaa[1] * 2).to_i(16).chr +
|
63
|
-
(rrggbbaa[2] * 2).to_i(16).chr +
|
64
|
-
FF
|
65
|
-
when 4 # RGBA
|
66
|
-
(rrggbbaa[0] * 2).to_i(16).chr +
|
67
|
-
(rrggbbaa[1] * 2).to_i(16).chr +
|
68
|
-
(rrggbbaa[2] * 2).to_i(16).chr +
|
69
|
-
(rrggbbaa[3] * 2).to_i(16).chr
|
70
|
-
when 6 # RRGGBB
|
71
|
-
(rrggbbaa[0, 2]).to_i(16).chr +
|
72
|
-
(rrggbbaa[2, 2]).to_i(16).chr +
|
73
|
-
(rrggbbaa[4, 2]).to_i(16).chr +
|
74
|
-
FF
|
75
|
-
when 8 # RRGGBBAA
|
76
|
-
(rrggbbaa[0, 2]).to_i(16).chr +
|
77
|
-
(rrggbbaa[2, 2]).to_i(16).chr +
|
78
|
-
(rrggbbaa[4, 2]).to_i(16).chr +
|
79
|
-
(rrggbbaa[6, 2]).to_i(16).chr
|
80
|
-
else
|
81
|
-
Black
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# def pos(x, y)
|
86
|
-
# (4 * (x + @columns * y)) % @data.size
|
87
|
-
# end
|
88
|
-
end
|
89
|
-
end
|