-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1e04c64
commit 91c1b5e
Showing
5 changed files
with
204 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# minimal keyboard event support | ||
""" | ||
read_key() -> control_value | ||
read control key from keyboard input. | ||
# Reference table | ||
| value | control_value | effect | | ||
| ------------------- | ----------------- | ------------------- | | ||
| UP, LEFT, b | :CONTROL_BACKWARD | show previous frame | | ||
| DOWN, RIGHT, f | :CONTROL_FORWARD | show next frame | | ||
| SPACE, p | :CONTROL_PAUSE | pause/resume play | | ||
| CTRL-c, q | :CONTROL_EXIT | exit current play | | ||
| others... | :CONTROL_VOID | no effect | | ||
""" | ||
function read_key(io=stdin) | ||
control_value = :CONTROL_VOID | ||
try | ||
_setraw!(io, true) | ||
keyin = read(io, Char) | ||
if keyin == '\e' | ||
# some special keys are more than one byte, e.g., left key is `\e[D` | ||
# reference: https://en.wikipedia.org/wiki/ANSI_escape_code | ||
keyin = read(io, Char) | ||
if keyin == '[' | ||
keyin = read(io, Char) | ||
if keyin in ['A', 'D'] # up, left | ||
control_value = :CONTROL_BACKWARD | ||
elseif keyin in ['B', 'C'] # down, right | ||
control_value = :CONTROL_FORWARD | ||
end | ||
end | ||
elseif 'A' <= keyin <= 'Z' || 'a' <= keyin <= 'z' | ||
keyin = lowercase(keyin) | ||
if keyin == 'p' | ||
control_value = :CONTROL_PAUSE | ||
elseif keyin == 'q' | ||
control_value = :CONTROL_EXIT | ||
elseif keyin == 'f' | ||
control_value = :CONTROL_FORWARD | ||
elseif keyin == 'b' | ||
control_value = :CONTROL_BACKWARD | ||
end | ||
elseif keyin == ' ' | ||
control_value = :CONTROL_PAUSE | ||
end | ||
catch e | ||
if e isa InterruptException # Ctrl-C | ||
control_value = :CONTROL_EXIT | ||
else | ||
rethrow(e) | ||
end | ||
finally | ||
_setraw!(io, false) | ||
end | ||
return control_value | ||
end | ||
|
||
_setraw!(io::Base.TTY, raw) = ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), io.handle, raw) | ||
_setraw!(::IO, raw) = nothing |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using ImageShow | ||
using ImageShow: read_key | ||
|
||
@testset "keyboard" begin | ||
# TODO: Ctrl-C (InterruptException) is not tested | ||
@testset "read_key" begin | ||
inputs = [ | ||
(UInt8['\e', '[', 'A'], :CONTROL_BACKWARD), # UP | ||
(UInt8['\e', '[', 'D'], :CONTROL_BACKWARD), # LEFT | ||
(UInt8['\e', '[', 'B'], :CONTROL_FORWARD), # DOWN | ||
(UInt8['\e', '[', 'C'], :CONTROL_FORWARD), # RIGHT | ||
|
||
(UInt8[' '], :CONTROL_PAUSE), # SPACE | ||
(UInt8['p'], :CONTROL_PAUSE), | ||
(UInt8['P'], :CONTROL_PAUSE), | ||
(UInt8['q'], :CONTROL_EXIT), | ||
(UInt8['Q'], :CONTROL_EXIT), | ||
(UInt8['f'], :CONTROL_FORWARD), | ||
(UInt8['F'], :CONTROL_FORWARD), | ||
(UInt8['b'], :CONTROL_BACKWARD), | ||
(UInt8['B'], :CONTROL_BACKWARD), | ||
|
||
# key events that currenctly has no effect | ||
|
||
# although VIM users might want this :) | ||
(UInt8['j'], :CONTROL_VOID), | ||
(UInt8['k'], :CONTROL_VOID), | ||
(UInt8['h'], :CONTROL_VOID), | ||
(UInt8['l'], :CONTROL_VOID), | ||
] | ||
for (chs, ref) in inputs | ||
io = IOBuffer(chs) | ||
@test ref == read_key(io) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using ImageShow, ImageCore | ||
using TestImages, FileIO | ||
using Test | ||
|
||
function check_summary(n, msg) | ||
function generate_summary_regex(n) | ||
summary_regex = raw"Frame: \d+/\d+ FPS: \d+\.\d+\s+\nexit: ctrl-c\. play\/pause: space-bar\. seek: arrow keys\n" | ||
Regex(mapreduce(i->summary_regex, (x,y)->x*raw".*"*y, 1:n)) | ||
end | ||
function _check_summary(n, msg) | ||
return !isnothing(match(generate_summary_regex(n), msg)) | ||
end | ||
|
||
return _check_summary(n, msg) && !_check_summary(n+1, msg) | ||
end | ||
|
||
@testset "multipage" begin | ||
@testset "play" begin | ||
img = RGB.(testimage("mri-stack")) | ||
framestack = [img[:, :, i] for i in 1:size(img, 3)] | ||
|
||
workdir = "tmp" | ||
mktempdir() do workdir | ||
filename = joinpath(workdir, "multipage.png") | ||
fn = open(filename, "w") | ||
summary_output_io = IOBuffer() | ||
|
||
save(filename, framestack[1]) | ||
frame_size = stat(filename).size | ||
|
||
# Case: quit immediately | ||
key_input_io = IOBuffer(UInt8['q']) | ||
ImageShow._play(framestack; fps=1, paused=false, quit_after_play=true, display_io=fn, summary_io=summary_output_io, keyboard_io=key_input_io) | ||
summary_msg = String(take!(summary_output_io)) | ||
@test check_summary(2, summary_msg) # If paused=false, it will print summary twice | ||
@test RGB.(load(filename)) == framestack[1] | ||
@test 1 == stat(filename).size/frame_size | ||
|
||
# Case: quit after all play | ||
fn = open(filename, "w") | ||
summary_output_io = IOBuffer() | ||
# use a small fps to make sure each frame are actually written | ||
ImageShow._play(framestack; fps=15, paused=false, quit_after_play=true, display_io=fn, summary_io=summary_output_io, keyboard_io=stdin) | ||
summary_msg = String(take!(summary_output_io)) | ||
@test check_summary(length(framestack), summary_msg) | ||
# FIXME: show method will append to the given file handler, however, PNG reader will only | ||
# read the first valid png data; all extra data block are discarded. | ||
# This somehow still proves that we're writing more than one image to the display_io | ||
@test RGB.(load(filename)) == framestack[1] | ||
@test 20 < stat(filename).size/frame_size < length(framestack) | ||
end | ||
end | ||
|
||
@testset "utils" begin | ||
# Although it's a no-op, it gets blocked at fps=2, which is about 0.5 second | ||
t = @elapsed ImageShow.fixed_fps(2) do | ||
nothing | ||
end | ||
@test 0.4 < t < 0.6 | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters