ektoplayer 0.1.6 → 0.1.11
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 +26 -12
- data/lib/ektoplayer/application.rb +6 -5
- data/lib/ektoplayer/bindings.rb +61 -55
- data/lib/ektoplayer/common.rb +19 -6
- data/lib/ektoplayer/compat.rb +3 -3
- data/lib/ektoplayer/config.rb +1 -11
- data/lib/ektoplayer/controllers/browser.rb +7 -5
- data/lib/ektoplayer/controllers/help.rb +1 -1
- data/lib/ektoplayer/controllers/info.rb +1 -1
- data/lib/ektoplayer/controllers/playlist.rb +24 -12
- data/lib/ektoplayer/icurses.rb +21 -0
- data/lib/ektoplayer/icurses/curses.rb +53 -0
- data/lib/ektoplayer/icurses/ffi_ncurses.rb +69 -0
- data/lib/ektoplayer/icurses/ncurses.rb +79 -0
- data/lib/ektoplayer/icurses/ncursesw.rb +1 -0
- data/lib/ektoplayer/icurses/sugar.rb +65 -0
- data/lib/ektoplayer/icurses/test.rb +99 -0
- data/lib/ektoplayer/models/player.rb +2 -2
- data/lib/ektoplayer/{mp3player.rb → players/mpg_portaudio_player.rb} +3 -3
- data/lib/ektoplayer/players/mpg_wrapper_player.rb +107 -0
- data/lib/ektoplayer/theme.rb +1 -6
- data/lib/ektoplayer/ui.rb +100 -129
- data/lib/ektoplayer/ui/colors.rb +14 -14
- data/lib/ektoplayer/ui/widgets.rb +4 -4
- data/lib/ektoplayer/ui/widgets/labelwidget.rb +1 -1
- data/lib/ektoplayer/ui/widgets/listwidget.rb +115 -46
- data/lib/ektoplayer/views/help.rb +7 -10
- data/lib/ektoplayer/views/info.rb +29 -38
- data/lib/ektoplayer/views/mainwindow.rb +2 -5
- data/lib/ektoplayer/views/playinginfo.rb +15 -20
- data/lib/ektoplayer/views/progressbar.rb +30 -10
- data/lib/ektoplayer/views/splash.rb +24 -25
- data/lib/ektoplayer/views/tabbar.rb +6 -5
- data/lib/ektoplayer/views/trackrenderer.rb +20 -14
- metadata +15 -47
- data/lib/ektoplayer/views/volumemeter.rb +0 -76
@@ -1,5 +1,5 @@
|
|
1
1
|
require_relative 'model'
|
2
|
-
require_relative '../
|
2
|
+
require_relative '../players/mpg_wrapper_player'
|
3
3
|
|
4
4
|
module Ektoplayer
|
5
5
|
module Models
|
@@ -7,7 +7,7 @@ module Ektoplayer
|
|
7
7
|
def initialize(client)
|
8
8
|
super()
|
9
9
|
@client = client
|
10
|
-
@player =
|
10
|
+
@player = MpgWrapperPlayer.new
|
11
11
|
@events.register(:position_change, :track_completed, :pause, :stop, :play)
|
12
12
|
@player.events.on_all(&@events.method(:trigger))
|
13
13
|
|
@@ -2,7 +2,7 @@ require 'thread'
|
|
2
2
|
require 'mpg123'
|
3
3
|
require 'portaudio'
|
4
4
|
|
5
|
-
require_relative 'events'
|
5
|
+
require_relative '../events'
|
6
6
|
|
7
7
|
class Mpg123
|
8
8
|
alias :samples_per_frame :spf
|
@@ -38,7 +38,7 @@ class Mpg123
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
class
|
41
|
+
class MpgPortaudioPlayer
|
42
42
|
attr_reader :events
|
43
43
|
|
44
44
|
def initialize(buffer_size = 2**12)
|
@@ -148,4 +148,4 @@ class Mp3Player
|
|
148
148
|
def forward(seconds = 2)
|
149
149
|
seek(position + seconds)
|
150
150
|
end
|
151
|
-
|
151
|
+
e
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'open3'
|
3
|
+
require 'scanf'
|
4
|
+
|
5
|
+
require_relative '../events'
|
6
|
+
|
7
|
+
fail 'MpgWrapperPlayer: mpg123 not found' unless system('which mpg123 >/dev/null')
|
8
|
+
|
9
|
+
class MpgWrapperPlayer
|
10
|
+
attr_reader :events, :file
|
11
|
+
|
12
|
+
STATE_STOPPED, STATE_PAUSED, STATE_PLAYING = 0, 1, 2
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@events = Events.new(:play, :pause, :stop, :position_change)
|
16
|
+
@lock = Mutex.new
|
17
|
+
@mpg123_in, @mpg123_out, @mpg123_thread = nil, nil, nil
|
18
|
+
@state = 0
|
19
|
+
@file = ''
|
20
|
+
|
21
|
+
@frames_played = @frames_remaining =
|
22
|
+
@seconds_played = @seconds_remaining = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def play(file=nil)
|
26
|
+
start_mpg123_thread
|
27
|
+
@file = file if file
|
28
|
+
write("L #{@file}")
|
29
|
+
end
|
30
|
+
|
31
|
+
def pause; write(?P) if @state == STATE_PLAYING end
|
32
|
+
def stop; write(?S) if @state != STATE_STOPPED end
|
33
|
+
def toggle; write(?P) end
|
34
|
+
|
35
|
+
def level; 30 end
|
36
|
+
def paused?; @state == STATE_PAUSED end
|
37
|
+
def stopped?; @state == STATE_STOPPED end
|
38
|
+
def playing?; @state == STATE_PLAYING end
|
39
|
+
|
40
|
+
def status
|
41
|
+
return :playing if playing?
|
42
|
+
return :paused if paused?
|
43
|
+
return :stopped if stopped?
|
44
|
+
end
|
45
|
+
|
46
|
+
def position; @seconds_played end
|
47
|
+
def length; @seconds_played + @seconds_remaining end
|
48
|
+
def position_percent; Float(@seconds_played) / length end
|
49
|
+
|
50
|
+
def seek(seconds) write("J #{seconds}s") end
|
51
|
+
def rewind(seconds = 2) write("J -#{seconds}s") end
|
52
|
+
def forward(seconds = 2) write("J +#{seconds}s") end
|
53
|
+
alias :backward :rewind
|
54
|
+
|
55
|
+
private def write(string)
|
56
|
+
@lock.synchronize do
|
57
|
+
@mpg123_in.write(string + ?\n)
|
58
|
+
end
|
59
|
+
rescue
|
60
|
+
Ektoplayer::Application.log(self, $!)
|
61
|
+
end
|
62
|
+
|
63
|
+
private def start_mpg123_thread
|
64
|
+
@lock.synchronize do
|
65
|
+
unless @mpg123_thread
|
66
|
+
Thread.new do
|
67
|
+
begin
|
68
|
+
@mpg123_in, @mpg123_out, _, @mpg123_thread =
|
69
|
+
Open3.popen3('mpg123', '-o', 'jack,pulse,alsa,oss', '--fuzzy', '-R')
|
70
|
+
|
71
|
+
while (line = @mpg123_out.readline)
|
72
|
+
if line[1] == ?F
|
73
|
+
@frames_played, @frames_remaining,
|
74
|
+
@seconds_played, @seconds_remaining =
|
75
|
+
line.scanf('@F %d %d %f %f')
|
76
|
+
@events.trigger(:position_change)
|
77
|
+
elsif line[1] == ?P
|
78
|
+
if (@state = line[3].to_i) == STATE_STOPPED
|
79
|
+
if @seconds_remaining < 3
|
80
|
+
@events.trigger(:stop, :track_completed)
|
81
|
+
else
|
82
|
+
@events.trigger(:stop)
|
83
|
+
end
|
84
|
+
elsif @state == STATE_PAUSED
|
85
|
+
@events.trigger(:pause)
|
86
|
+
elsif @state == STATE_PLAYING
|
87
|
+
@events.trigger(:play)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
rescue
|
92
|
+
Ektoplayer::Application.log(self, $!)
|
93
|
+
ensure
|
94
|
+
# shouldn't reach here
|
95
|
+
Ektoplayer::Application.log(self, 'player closed')
|
96
|
+
@mpg123_thread.kill
|
97
|
+
@mpg123_thread = nil
|
98
|
+
@mpg123_in.close
|
99
|
+
@mpg123_out.close
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
sleep 0.1 while not @mpg123_thread
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/ektoplayer/theme.rb
CHANGED
@@ -24,14 +24,12 @@ module Ektoplayer
|
|
24
24
|
:'progressbar.progress' => [:blue ].freeze,
|
25
25
|
:'progressbar.rest' => [:black ].freeze,
|
26
26
|
|
27
|
-
:'volumemeter.level' => [:magenta ].freeze,
|
28
|
-
:'volumemeter.rest' => [:black ].freeze,
|
29
|
-
|
30
27
|
:'tabbar.selected' => [:blue ].freeze,
|
31
28
|
:'tabbar.unselected' => [:none ].freeze,
|
32
29
|
|
33
30
|
:'list.item_even' => [:blue ].freeze,
|
34
31
|
:'list.item_odd' => [:blue ].freeze,
|
32
|
+
:'list.item_selection' => [:magenta ].freeze,
|
35
33
|
|
36
34
|
:'playinginfo.position' => [:magenta ].freeze,
|
37
35
|
:'playinginfo.state' => [:cyan ].freeze,
|
@@ -54,9 +52,6 @@ module Ektoplayer
|
|
54
52
|
:'progressbar.progress' => [23 ].freeze,
|
55
53
|
:'progressbar.rest' => [236 ].freeze,
|
56
54
|
|
57
|
-
:'volumemeter.level' => [:magenta ].freeze,
|
58
|
-
:'volumemeter.rest' => [236 ].freeze,
|
59
|
-
|
60
55
|
:'tabbar.selected' => [75 ].freeze,
|
61
56
|
:'tabbar.unselected' => [250 ].freeze,
|
62
57
|
|
data/lib/ektoplayer/ui.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative 'icurses'
|
2
2
|
require 'readline'
|
3
3
|
require 'io/console'
|
4
4
|
|
@@ -6,19 +6,17 @@ require_relative 'ui/colors'
|
|
6
6
|
require_relative 'events'
|
7
7
|
|
8
8
|
module UI
|
9
|
-
CONDITION_SIGNALS = ConditionSignals.new
|
10
|
-
|
11
9
|
class WidgetSizeError < Exception; end
|
12
10
|
|
13
11
|
class Canvas
|
14
|
-
extend
|
12
|
+
extend ICurses
|
15
13
|
|
16
14
|
def self.size
|
17
|
-
UI::Size.new(height:
|
15
|
+
UI::Size.new(height: ICurses.lines, width: ICurses.cols)
|
18
16
|
end
|
19
17
|
|
20
18
|
def self.cursor
|
21
|
-
UI::Point.new(y:
|
19
|
+
#UI::Point.new(y: ICurses.cury, x: ICurses.curx)
|
22
20
|
end
|
23
21
|
|
24
22
|
def self.pos
|
@@ -28,10 +26,10 @@ module UI
|
|
28
26
|
def self.start
|
29
27
|
@@widget = nil
|
30
28
|
|
31
|
-
%w(
|
32
|
-
each {|_|
|
33
|
-
|
34
|
-
|
29
|
+
%w(initscr cbreak noecho start_color use_default_colors).
|
30
|
+
each {|_|ICurses.send(_)}
|
31
|
+
ICurses.mousemask(ICurses::ALL_MOUSE_EVENTS | ICurses::REPORT_MOUSE_POSITION)
|
32
|
+
ICurses.stdscr.keypad(true)
|
35
33
|
UI::Colors.start
|
36
34
|
|
37
35
|
self.enable_resize_detection
|
@@ -43,7 +41,7 @@ module UI
|
|
43
41
|
|
44
42
|
def self.widget; @@widget end
|
45
43
|
def self.widget=(w) @@widget = w end
|
46
|
-
def self.stop;
|
44
|
+
def self.stop; ICurses.endwin end
|
47
45
|
def self.visible?; true end
|
48
46
|
def self.inivsibile?; false end
|
49
47
|
|
@@ -52,36 +50,33 @@ module UI
|
|
52
50
|
widget
|
53
51
|
end
|
54
52
|
|
55
|
-
def self.getch(timeout=-1)
|
56
|
-
|
57
|
-
|
58
|
-
end
|
53
|
+
#def self.getch(timeout=-1)
|
54
|
+
# ICurses.stdscr.timeout(timeout)
|
55
|
+
# UI::Input::KEYMAP_WORKAROUND[ICurses.stdscr.getch]
|
56
|
+
#end
|
59
57
|
|
60
|
-
def self.update_screen(force_redraw=false)
|
58
|
+
def self.update_screen(force_redraw=false, force_resize=false)
|
61
59
|
@@updating ||= Mutex.new
|
62
60
|
@@want_resize ||= false
|
63
61
|
|
64
62
|
if @@updating.try_lock
|
65
63
|
begin
|
66
|
-
if @@want_resize
|
64
|
+
if @@want_resize or force_resize
|
67
65
|
@@want_resize = false
|
68
66
|
h, w = IO.console.winsize()
|
69
|
-
|
70
|
-
Curses.clear
|
71
|
-
Curses.refresh
|
67
|
+
ICurses.resizeterm(h, w)
|
72
68
|
@@widget.size=(Size.new(height: h, width: w)) if @@widget
|
73
69
|
@@widget.display(true, true, true) if @@widget
|
74
70
|
else
|
75
71
|
@@widget.display(true, force_redraw) if @@widget
|
76
72
|
end
|
77
73
|
rescue UI::WidgetSizeError
|
78
|
-
|
79
|
-
|
74
|
+
ICurses.stdscr.clear
|
75
|
+
ICurses.stdscr.addstr('terminal too small!')
|
80
76
|
rescue
|
81
77
|
Ektoplayer::Application.log(self, $!)
|
82
78
|
end
|
83
79
|
|
84
|
-
Curses.doupdate
|
85
80
|
@@updating.unlock
|
86
81
|
end
|
87
82
|
end
|
@@ -96,8 +91,8 @@ module UI
|
|
96
91
|
|
97
92
|
class Input
|
98
93
|
KEYMAP_WORKAROUND = {
|
99
|
-
13 =>
|
100
|
-
127 =>
|
94
|
+
13 => ICurses::KEY_ENTER,
|
95
|
+
127 => ICurses::KEY_BACKSPACE
|
101
96
|
}
|
102
97
|
KEYMAP_WORKAROUND.default_proc = proc { |h,k| k }
|
103
98
|
KEYMAP_WORKAROUND.freeze
|
@@ -111,45 +106,51 @@ module UI
|
|
111
106
|
|
112
107
|
loop do
|
113
108
|
unless @@readline_obj.active?
|
114
|
-
|
115
|
-
|
109
|
+
ICurses.curs_set(0)
|
110
|
+
ICurses.nonl
|
116
111
|
|
117
112
|
begin
|
118
|
-
UI::Canvas.widget.win.keypad
|
119
|
-
c = KEYMAP_WORKAROUND[UI::Canvas.widget.win.getch1]
|
113
|
+
UI::Canvas.widget.win.keypad(true)
|
120
114
|
|
121
|
-
if c
|
122
|
-
if c
|
123
|
-
|
115
|
+
if (c = UI::Canvas.widget.win.getch1(600))
|
116
|
+
if c == ICurses::KEY_MOUSE
|
117
|
+
if c = ICurses.getmouse
|
118
|
+
UI::Canvas.widget.mouse_click(c)
|
119
|
+
end
|
120
|
+
else
|
121
|
+
c = KEYMAP_WORKAROUND[c.ord]
|
122
|
+
UI::Canvas.widget.key_press(c) if c >= 0
|
124
123
|
end
|
125
|
-
elsif c # (not nil)
|
126
|
-
UI::Canvas.widget.key_press(c.is_a?(Integer) ? c : c.to_sym)
|
127
124
|
end
|
125
|
+
|
126
|
+
ICurses.doupdate
|
128
127
|
end while !@@readline_obj.active?
|
129
128
|
else
|
130
|
-
|
131
|
-
|
129
|
+
ICurses.curs_set(1)
|
130
|
+
ICurses.nl
|
132
131
|
|
133
132
|
begin
|
134
133
|
win = UI::Canvas.widget.win
|
135
|
-
win.keypad
|
136
|
-
|
134
|
+
win.keypad(false)
|
135
|
+
@@readline_obj.redraw
|
136
|
+
next unless (c = (win.getch1(100).ord rescue -1)) > 0
|
137
137
|
|
138
138
|
if c == 10 or c == 4
|
139
|
-
@@readline_obj.feed(?\n)
|
139
|
+
@@readline_obj.feed(?\n.ord)
|
140
140
|
else
|
141
|
-
@@readline_obj.feed(c
|
141
|
+
@@readline_obj.feed(c)
|
142
142
|
|
143
143
|
if c == 27 # pass 3-character escape sequence
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
144
|
+
win.timeout(5)
|
145
|
+
if (c = (win.getch.ord rescue -1)) > 0
|
146
|
+
@@readline_obj.feed(c)
|
147
|
+
if (c = (win.getch.ord rescue -1)) > 0
|
148
|
+
@@readline_obj.feed(c)
|
148
149
|
end
|
149
150
|
end
|
150
151
|
end
|
151
152
|
end
|
152
|
-
rescue
|
153
|
+
#rescue
|
153
154
|
# getch() returned something weird that could not be chr()d
|
154
155
|
end while @@readline_obj.active?
|
155
156
|
end
|
@@ -157,15 +158,14 @@ module UI
|
|
157
158
|
end
|
158
159
|
|
159
160
|
def self.readline(*args, **opts, &block)
|
160
|
-
(@@readline_obj ||= ReadlineWindow.new).readline(*args, **opts) do
|
161
|
-
Canvas.class_variable_get('@@updating').synchronize { yield }
|
161
|
+
(@@readline_obj ||= ReadlineWindow.new).readline(*args, **opts) do |result|
|
162
|
+
Canvas.class_variable_get('@@updating').synchronize { yield result }
|
162
163
|
end
|
163
164
|
end
|
164
165
|
end
|
165
166
|
|
166
167
|
class ReadlineWindow
|
167
168
|
def initialize
|
168
|
-
@mutex, @cond = Mutex.new, ConditionVariable.new
|
169
169
|
Readline.input, @readline_in_write = IO.pipe
|
170
170
|
Readline.output = File.open(File::NULL, ?w)
|
171
171
|
@thread = nil
|
@@ -173,35 +173,38 @@ module UI
|
|
173
173
|
|
174
174
|
def active?; @thread; end
|
175
175
|
|
176
|
+
def redraw
|
177
|
+
return unless @window
|
178
|
+
@window.erase
|
179
|
+
buffer = @prompt + Readline.line_buffer.to_s
|
180
|
+
@window.addstr(buffer[(buffer.size - @size.width).clamp(0, buffer.size)..-1])
|
181
|
+
@window.move(0, Readline.point + @prompt.size)
|
182
|
+
@window.refresh
|
183
|
+
end
|
184
|
+
|
176
185
|
def readline(pos, size, prompt: '', add_hist: false, &block)
|
177
186
|
@thread ||= Thread.new do
|
178
|
-
|
179
|
-
|
180
|
-
rlt = Thread.new { Readline.readline(prompt, add_hist) }
|
181
|
-
Readline.set_screen_size(size.height, size.width)
|
182
|
-
Readline.delete_text
|
183
|
-
@readline_in_write.read_nonblock(100) rescue nil
|
184
|
-
|
185
|
-
while rlt.alive?
|
186
|
-
window.erase
|
187
|
-
buffer = prompt + Readline.line_buffer.to_s
|
188
|
-
window << buffer[(buffer.size - size.width).clamp(0, buffer.size)..-1]
|
189
|
-
window.cursor=(Point.new(x: Readline.point + prompt.size, y: 0))
|
190
|
-
window.refresh
|
191
|
-
CONDITION_SIGNALS.wait(:readline, 0.2)
|
192
|
-
end
|
187
|
+
@size, @prompt = size, prompt
|
188
|
+
@window = ICurses.newwin(size.height, size.width, pos.y, pos.x)
|
193
189
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
190
|
+
begin
|
191
|
+
Readline.set_screen_size(size.height, size.width)
|
192
|
+
Readline.delete_text
|
193
|
+
@readline_in_write.read_nonblock(100) rescue nil
|
194
|
+
block.(Readline.readline(prompt, add_hist))
|
195
|
+
ensure
|
196
|
+
@window.clear
|
197
|
+
@window = @thread = nil
|
198
|
+
UI::Canvas.update_screen(true)
|
199
|
+
end
|
198
200
|
end
|
199
201
|
end
|
200
202
|
|
201
203
|
def feed(c)
|
202
|
-
@readline_in_write.
|
203
|
-
|
204
|
-
@thread = nil if c == ?\n
|
204
|
+
@readline_in_write.putc(c)
|
205
|
+
Thread.pass
|
206
|
+
@thread = nil if c == ?\n.ord
|
207
|
+
redraw
|
205
208
|
end
|
206
209
|
end
|
207
210
|
|
@@ -251,49 +254,9 @@ module UI
|
|
251
254
|
def to_s; "[(Size) height=#{height}, width=#{width}]" end
|
252
255
|
end
|
253
256
|
|
254
|
-
# We want to change the mouse coordinates as we pass the mouse event
|
255
|
-
# through the widgets. The attributes of Curses::MouseEvent are
|
256
|
-
# readonly, therefore we need to carry out our own MouseEvent class.
|
257
|
-
class FakeMouseEvent
|
258
|
-
attr_accessor :x, :y, :z, :bstate
|
259
|
-
|
260
|
-
def initialize(mouse_event=nil)
|
261
|
-
if mouse_event
|
262
|
-
from_mouse_event!(mouse_event)
|
263
|
-
else
|
264
|
-
@x, @y, @z, @bstate = 0, 0, 0, Curses::BUTTON1_CLICKED
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
def from_mouse_event!(m)
|
269
|
-
@x, @y, @z, @bstate = m.x, m.y, m.z, m.bstate
|
270
|
-
end
|
271
|
-
|
272
|
-
def update!(x: nil, y: nil, z: nil, bstate: nil)
|
273
|
-
@x, @y, @z = (x or @x), (y or @y), (z or @z)
|
274
|
-
@bstate = (bstate or @bstate)
|
275
|
-
end
|
276
|
-
|
277
|
-
def pos
|
278
|
-
Point.new(x: @x, y: @y)
|
279
|
-
end
|
280
|
-
|
281
|
-
def to_fake
|
282
|
-
FakeMouseEvent.new(self)
|
283
|
-
end
|
284
|
-
|
285
|
-
def to_s
|
286
|
-
name = Curses.constants.
|
287
|
-
select { |c| c =~ /^BUTTON_/ }.
|
288
|
-
select { |c| Curses.const_get(c) & @bstate > 0 }[0]
|
289
|
-
name ||= @button
|
290
|
-
"[(FakeMouseEvent) button=#{name}, x=#{x}, y=#{y}, z=#{z}]"
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
257
|
class MouseEvents < Events
|
295
258
|
def on(mouse_event, &block)
|
296
|
-
return on_all(&block) if mouse_event ==
|
259
|
+
return on_all(&block) if mouse_event == ICurses::ALL_MOUSE_EVENTS
|
297
260
|
super(mouse_event, &block)
|
298
261
|
end
|
299
262
|
|
@@ -334,8 +297,8 @@ module UI
|
|
334
297
|
end
|
335
298
|
end
|
336
299
|
|
337
|
-
module
|
338
|
-
class
|
300
|
+
module ICurses
|
301
|
+
class IWindow
|
339
302
|
alias :height :maxy
|
340
303
|
alias :width :maxx
|
341
304
|
alias :clear_line :clrtoeol
|
@@ -345,11 +308,11 @@ module Curses
|
|
345
308
|
def size; UI::Size.new(height: maxy, width: maxx) end
|
346
309
|
|
347
310
|
def cursor=(new)
|
348
|
-
|
311
|
+
move(new.y, new.x) # or fail "Could not set cursor: #{new} #{size}"
|
349
312
|
end
|
350
313
|
|
351
314
|
def pos=(new)
|
352
|
-
|
315
|
+
mvwin(new.y, new.x)
|
353
316
|
end
|
354
317
|
|
355
318
|
def size=(new)
|
@@ -361,18 +324,18 @@ module Curses
|
|
361
324
|
end
|
362
325
|
|
363
326
|
def getch1(timeout=-1)
|
364
|
-
self.timeout
|
327
|
+
self.timeout(timeout)
|
365
328
|
getch
|
366
329
|
end
|
367
330
|
|
368
|
-
def on_line(n)
|
369
|
-
def on_column(n)
|
370
|
-
def next_line;
|
371
|
-
def mv_left(n)
|
372
|
-
def line_start(l=0)
|
373
|
-
def from_left(size)
|
374
|
-
def from_right(size)
|
375
|
-
def center(size)
|
331
|
+
def on_line(n) move(n, curx) ;self;end
|
332
|
+
def on_column(n) move(cury, n) ;self;end
|
333
|
+
def next_line; move(cury + 1, 0) ;self;end
|
334
|
+
def mv_left(n) move(cury, curx - 1) ;self;end
|
335
|
+
def line_start(l=0) move(l, 0) ;self;end
|
336
|
+
def from_left(size) move(cury, size) ;self;end
|
337
|
+
def from_right(size) move(cury, (maxx - size)) ;self;end
|
338
|
+
def center(size) move(cury, (maxx / 2) - (size / 2)) ;self;end
|
376
339
|
|
377
340
|
def center_string(string)
|
378
341
|
center(string.size)
|
@@ -380,24 +343,32 @@ module Curses
|
|
380
343
|
self end
|
381
344
|
|
382
345
|
def insert_top
|
383
|
-
|
346
|
+
move(0, 0)
|
384
347
|
insertln
|
385
348
|
self end
|
386
349
|
|
387
350
|
def append_bottom
|
388
|
-
|
351
|
+
move(0, 0)
|
389
352
|
deleteln
|
390
|
-
|
353
|
+
move(maxy - 1, 0)
|
391
354
|
self end
|
392
355
|
end
|
393
356
|
|
394
|
-
class
|
357
|
+
class IMouseEvent
|
395
358
|
def pos
|
396
359
|
UI::Point.new(x: x, y: y)
|
397
360
|
end
|
398
361
|
|
399
362
|
def to_fake
|
400
|
-
|
363
|
+
IMouseEvent.new(self)
|
364
|
+
end
|
365
|
+
|
366
|
+
def to_s
|
367
|
+
name = ICurses.constants.
|
368
|
+
select { |c| c =~ /^BUTTON_/ }.
|
369
|
+
select { |c| ICurses.const_get(c) & @bstate > 0 }[0]
|
370
|
+
name ||= @button
|
371
|
+
"[(IMouseEvent) button=#{name}, x=#{x}, y=#{y}, z=#{z}]"
|
401
372
|
end
|
402
373
|
end
|
403
374
|
end
|