retrograph_easy 0.1
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.
- data/COPYING +19 -0
- data/README +13 -0
- data/Rakefile +64 -0
- data/demos/defense.rb +413 -0
- data/demos/logo.rb +34 -0
- data/lib/retrograph/easy/app.rb +257 -0
- data/lib/retrograph/easy/constants.rb +35 -0
- data/lib/retrograph/easy/controllers.rb +184 -0
- data/lib/retrograph/easy/display.rb +173 -0
- data/lib/retrograph/easy/logo.rb +287 -0
- data/lib/retrograph/easy/periodic.rb +99 -0
- data/lib/retrograph/easy/sprites.rb +133 -0
- data/lib/retrograph/easy.rb +32 -0
- data/lib/retrograph/sdl.rb +26 -0
- data/lib/retrograph.rb +29 -0
- data/spec/easy_spec.rb +6 -0
- metadata +83 -0
@@ -0,0 +1,257 @@
|
|
1
|
+
# retrograph/easy/app
|
2
|
+
#
|
3
|
+
# Copyright (c) 2009 MenTaLguY <mental@rydia.net>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
#
|
23
|
+
|
24
|
+
require 'sdl'
|
25
|
+
require 'retrograph/easy/display'
|
26
|
+
require 'retrograph/easy/periodic'
|
27
|
+
require 'retrograph/easy/sprites'
|
28
|
+
require 'retrograph/easy/controllers'
|
29
|
+
|
30
|
+
module Retrograph
|
31
|
+
module Easy
|
32
|
+
|
33
|
+
class Quit < Exception
|
34
|
+
def initialize(message="User quit")
|
35
|
+
super(message)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class App
|
40
|
+
def initialize(display)
|
41
|
+
@_retrograph_held_event = nil
|
42
|
+
@_retrograph_display = display
|
43
|
+
@_retrograph_sprite_manager = SpriteManager.new(display)
|
44
|
+
@_retrograph_controller_manager = ControllerManager.new
|
45
|
+
@_retrograph_controllers =
|
46
|
+
@_retrograph_controller_manager.controllers.freeze
|
47
|
+
@_retrograph_periodic_manager = PeriodicManager.new
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :_retrograph_controllers
|
51
|
+
alias_method :controllers, :_retrograph_controllers
|
52
|
+
remove_method :_retrograph_controllers
|
53
|
+
|
54
|
+
def title(new_title)
|
55
|
+
@_retrograph_display.title = new_title
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def start(label, interval, phases=1)
|
60
|
+
@_retrograph_periodic_manager.start label, interval, phases
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def stop(label)
|
65
|
+
@_retrograph_periodic_manager.stop label
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def frame_for?(label)
|
70
|
+
@_retrograph_periodic_manager.frame_for? label
|
71
|
+
end
|
72
|
+
|
73
|
+
def phase_of(label)
|
74
|
+
@_retrograph_periodic_manager.phase_of label
|
75
|
+
end
|
76
|
+
|
77
|
+
def already?(label)
|
78
|
+
@_retrograph_periodic_manager.already? label
|
79
|
+
end
|
80
|
+
|
81
|
+
def clear_sprites
|
82
|
+
@_retrograph_sprite_manager.clear_sprites
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def sprite(x, y, pattern, flags)
|
87
|
+
@_retrograph_sprite_manager.add_sprite(x, y, pattern, flags)
|
88
|
+
end
|
89
|
+
|
90
|
+
def multi_sprite(x, y, pattern, width_tiles, height_tiles, flags)
|
91
|
+
@_retrograph_sprite_manager.add_multi_sprite(x, y, pattern, width_tiles, height_tiles, flags)
|
92
|
+
end
|
93
|
+
|
94
|
+
def sprite_group(x, y, width, height, flags)
|
95
|
+
@_retrograph_sprite_manager.add_sprite_group(x, y, width, height, flags) do
|
96
|
+
yield
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def palette_entry_rgb(index, r, g, b)
|
101
|
+
r = (r * 255).round if Float === r
|
102
|
+
g = (g * 255).round if Float === g
|
103
|
+
b = (b * 255).round if Float === b
|
104
|
+
raise RangeError, "index out of range" if index < 0 or index > 31
|
105
|
+
entry = ((r >> 2) & 0x30) |
|
106
|
+
((g >> 4) & 0x0c) |
|
107
|
+
((b >> 6) & 0x03)
|
108
|
+
palette_entry(index, entry)
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def palette_entry(index, entry)
|
113
|
+
@_retrograph_display.write_vdu_byte(0x7f00+index, entry)
|
114
|
+
end
|
115
|
+
|
116
|
+
def reset_vdu
|
117
|
+
write_vdu(0x0000, "\000" * 0x4000)
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
def vdu_mode(mode)
|
122
|
+
write_vdu_byte(0x7ff8, mode)
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def write_vdu(offset, data)
|
127
|
+
@_retrograph_display.write_vdu(offset, data)
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
def write_vdu_byte(offset, byte)
|
132
|
+
@_retrograph_display.write_vdu_byte(offset, byte)
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
def tile_pattern_1bit(base, index, data)
|
137
|
+
@_retrograph_display.write_vdu(base + index * 8, data)
|
138
|
+
self
|
139
|
+
end
|
140
|
+
|
141
|
+
def tile_pattern_2bit(base, index, data)
|
142
|
+
@_retrograph_display.write_vdu(base + index * 16, data)
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
def tile_pattern_4bit(base, index, data)
|
147
|
+
@_retrograph_display.write_vdu(base + index * 32, data)
|
148
|
+
self
|
149
|
+
end
|
150
|
+
|
151
|
+
def sprite_pattern(base, index, data)
|
152
|
+
tile_pattern_4bit(base + 0x2000, index, data)
|
153
|
+
self
|
154
|
+
end
|
155
|
+
|
156
|
+
def wait_scanlines(count)
|
157
|
+
@_retrograph_display.wait_scanlines(count)
|
158
|
+
self
|
159
|
+
end
|
160
|
+
|
161
|
+
def quit
|
162
|
+
raise Quit
|
163
|
+
end
|
164
|
+
|
165
|
+
def _suspend
|
166
|
+
while event = SDL::Event2.wait
|
167
|
+
case event
|
168
|
+
when SDL::Event2::Quit
|
169
|
+
quit
|
170
|
+
when SDL::Event2::Active
|
171
|
+
if event.gain and event.state == 2
|
172
|
+
break
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
while event = SDL::Event2.wait
|
177
|
+
case event
|
178
|
+
when SDL::Event2::Quit
|
179
|
+
quit
|
180
|
+
when SDL::Event2::KeyDown
|
181
|
+
case event.sym
|
182
|
+
when SDL::Key::RALT, SDL::Key::LALT
|
183
|
+
# ignore alt presses
|
184
|
+
else
|
185
|
+
@_retrograph_held_event = event
|
186
|
+
break
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
private :_suspend
|
192
|
+
|
193
|
+
def each_of_n_frames(n_frames)
|
194
|
+
each_frame(n_frames) { yield }
|
195
|
+
end
|
196
|
+
|
197
|
+
def each_frame(n_frames=nil)
|
198
|
+
count = 0
|
199
|
+
@_retrograph_display.each_frame do
|
200
|
+
break if n_frames == count
|
201
|
+
while event = (@_retrograph_held_event || SDL::Event2.poll)
|
202
|
+
@_retrograph_held_event = nil
|
203
|
+
case event
|
204
|
+
when SDL::Event2::Quit
|
205
|
+
quit
|
206
|
+
when SDL::Event2::Active
|
207
|
+
if not event.gain and event.state == 2
|
208
|
+
_suspend
|
209
|
+
next
|
210
|
+
end
|
211
|
+
when SDL::Event2::KeyDown
|
212
|
+
@_retrograph_controller_manager.key_pressed(event.sym)
|
213
|
+
if (event.mod & SDL::Key::MOD_ALT).nonzero?
|
214
|
+
case event.sym
|
215
|
+
when SDL::Key::RETURN
|
216
|
+
@_retrograph_display.toggle_fullscreen
|
217
|
+
when SDL::Key::F4
|
218
|
+
quit
|
219
|
+
when SDL::Key::TAB
|
220
|
+
if @_retrograph_display.managed and @_retrograph_display.fullscreen
|
221
|
+
@_retrograph_display.minimize
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
case event.sym
|
226
|
+
when SDL::Key::ESCAPE
|
227
|
+
quit
|
228
|
+
when SDL::Key::F11
|
229
|
+
@_retrograph_display.toggle_fullscreen
|
230
|
+
end
|
231
|
+
when SDL::Event2::KeyUp
|
232
|
+
@_retrograph_controller_manager.key_released(event.sym)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
yield
|
236
|
+
@_retrograph_periodic_manager.tick()
|
237
|
+
count += 1 if n_frames
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
@display = Display.new
|
243
|
+
|
244
|
+
class << self
|
245
|
+
def app(&block)
|
246
|
+
begin
|
247
|
+
@display.open do
|
248
|
+
app = App.new(@display)
|
249
|
+
app.instance_eval &block
|
250
|
+
end
|
251
|
+
rescue Quit
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# retrograph/easy/constants
|
2
|
+
#
|
3
|
+
# Copyright (c) 2009 MenTaLguY <mental@rydia.net>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
#
|
23
|
+
|
24
|
+
module Retrograph
|
25
|
+
module Easy
|
26
|
+
|
27
|
+
TEXT_MODE_A = 0
|
28
|
+
TEXT_MODE_B = 1
|
29
|
+
GRAPHICS_MODE_A = 2
|
30
|
+
GRAPHICS_MODE_B = 3
|
31
|
+
ENABLE_SPRITES = 1 << 3
|
32
|
+
ENABLE_BG = 1 << 2
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# retrograph/easy/controllers
|
2
|
+
#
|
3
|
+
# Copyright (c) 2009 MenTaLguY <mental@rydia.net>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
#
|
23
|
+
|
24
|
+
module Retrograph
|
25
|
+
module Easy
|
26
|
+
|
27
|
+
class Rocker
|
28
|
+
attr_reader :state
|
29
|
+
|
30
|
+
def initialize(minus_state, neutral_state, plus_state)
|
31
|
+
@minus_state = minus_state
|
32
|
+
@neutral_state = neutral_state
|
33
|
+
@plus_state = plus_state
|
34
|
+
@state = @neutral_state
|
35
|
+
@both_pressed = false
|
36
|
+
end
|
37
|
+
|
38
|
+
def plus_pressed
|
39
|
+
@both_pressed = true if @state != @neutral_state
|
40
|
+
@state = @plus_state
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def minus_pressed
|
45
|
+
@both_pressed = true if @state != @neutral_state
|
46
|
+
@state = @minus_state
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def plus_released
|
51
|
+
if @both_pressed
|
52
|
+
@both_pressed = false
|
53
|
+
@state = @minus_state
|
54
|
+
else
|
55
|
+
@state = @neutral_state
|
56
|
+
end
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def minus_released
|
61
|
+
if @both_pressed
|
62
|
+
@both_pressed = false
|
63
|
+
@state = @plus_state
|
64
|
+
else
|
65
|
+
@state = @neutral_state
|
66
|
+
end
|
67
|
+
self
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
button_names = %w(up down left right a b select start)
|
72
|
+
controller_fields = button_names.map { |n| "#{n}_pressed".intern } +
|
73
|
+
[:horizontal, :vertical]
|
74
|
+
|
75
|
+
Controller = Struct.new *controller_fields
|
76
|
+
Controller.class_eval do
|
77
|
+
FIELD_DEFAULTS = button_names.map { false } + [0, 0]
|
78
|
+
|
79
|
+
def initialize
|
80
|
+
super(*FIELD_DEFAULTS)
|
81
|
+
end
|
82
|
+
|
83
|
+
for name in button_names
|
84
|
+
field = "#{name}_pressed".intern
|
85
|
+
alias_method "#{field}?".intern, field
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class ControllerManager
|
90
|
+
KEYMAP = {
|
91
|
+
SDL::Key::UP => :up,
|
92
|
+
SDL::Key::DOWN => :down,
|
93
|
+
SDL::Key::LEFT => :left,
|
94
|
+
SDL::Key::RIGHT => :right,
|
95
|
+
SDL::Key::LCTRL => :a,
|
96
|
+
SDL::Key::RCTRL => :a,
|
97
|
+
SDL::Key::SPACE => :b,
|
98
|
+
SDL::Key::LSHIFT => :select,
|
99
|
+
SDL::Key::RSHIFT => :select,
|
100
|
+
SDL::Key::TAB => :select,
|
101
|
+
SDL::Key::RETURN => :start,
|
102
|
+
SDL::Key::W => :up,
|
103
|
+
SDL::Key::S => :down,
|
104
|
+
SDL::Key::D => :right,
|
105
|
+
SDL::Key::A => :left,
|
106
|
+
SDL::Key::Z => :select,
|
107
|
+
SDL::Key::X => :b,
|
108
|
+
SDL::Key::C => :a,
|
109
|
+
SDL::Key::COMMA => :a,
|
110
|
+
SDL::Key::PERIOD => :b,
|
111
|
+
SDL::Key::SLASH => :select
|
112
|
+
}
|
113
|
+
|
114
|
+
def initialize
|
115
|
+
@controllers = (0...2).map { Controller.new }
|
116
|
+
@horizontal = @controllers.map { Rocker.new(-1, 0, 1) }
|
117
|
+
@vertical = @controllers.map { Rocker.new(-1, 0, 1) }
|
118
|
+
end
|
119
|
+
|
120
|
+
def controllers
|
121
|
+
@controllers.dup
|
122
|
+
end
|
123
|
+
|
124
|
+
def key_pressed(key)
|
125
|
+
case KEYMAP[key]
|
126
|
+
when :up
|
127
|
+
@controllers[0].up_pressed = true
|
128
|
+
@vertical[0].minus_pressed
|
129
|
+
@controllers[0].vertical = @vertical[0].state
|
130
|
+
when :down
|
131
|
+
@controllers[0].down_pressed = true
|
132
|
+
@vertical[0].plus_pressed
|
133
|
+
@controllers[0].vertical = @vertical[0].state
|
134
|
+
when :left
|
135
|
+
@controllers[0].left_pressed = true
|
136
|
+
@horizontal[0].minus_pressed
|
137
|
+
@controllers[0].horizontal = @horizontal[0].state
|
138
|
+
when :right
|
139
|
+
@controllers[0].right_pressed = true
|
140
|
+
@horizontal[0].plus_pressed
|
141
|
+
@controllers[0].horizontal = @horizontal[0].state
|
142
|
+
when :a
|
143
|
+
@controllers[0].a_pressed = true
|
144
|
+
when :b
|
145
|
+
@controllers[0].b_pressed = true
|
146
|
+
when :select
|
147
|
+
@controllers[0].select_pressed = true
|
148
|
+
when :start
|
149
|
+
@controllers[0].start_pressed = true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def key_released(key)
|
154
|
+
case KEYMAP[key]
|
155
|
+
when :up
|
156
|
+
@controllers[0].up_pressed = false
|
157
|
+
@vertical[0].minus_pressed
|
158
|
+
@controllers[0].vertical = @vertical[0].state
|
159
|
+
when :down
|
160
|
+
@controllers[0].down_pressed = false
|
161
|
+
@vertical[0].plus_released
|
162
|
+
@controllers[0].vertical = @vertical[0].state
|
163
|
+
when :left
|
164
|
+
@controllers[0].left_pressed = false
|
165
|
+
@horizontal[0].minus_released
|
166
|
+
@controllers[0].horizontal = @horizontal[0].state
|
167
|
+
when :right
|
168
|
+
@controllers[0].right_pressed = false
|
169
|
+
@horizontal[0].plus_released
|
170
|
+
@controllers[0].horizontal = @horizontal[0].state
|
171
|
+
when :a
|
172
|
+
@controllers[0].a_pressed = false
|
173
|
+
when :b
|
174
|
+
@controllers[0].b_pressed = false
|
175
|
+
when :select
|
176
|
+
@controllers[0].select_pressed = false
|
177
|
+
when :start
|
178
|
+
@controllers[0].start_pressed = false
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# retrograph/easy/display
|
2
|
+
#
|
3
|
+
# Copyright (c) 2009 MenTaLguY <mental@rydia.net>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
#
|
23
|
+
|
24
|
+
require 'retrograph/sdl'
|
25
|
+
|
26
|
+
module Retrograph
|
27
|
+
module Easy
|
28
|
+
|
29
|
+
FRAMES_PER_SECOND = 25
|
30
|
+
FRAME_TIME_MS = 1000/FRAMES_PER_SECOND
|
31
|
+
|
32
|
+
class Display
|
33
|
+
BEZEL_THICKNESS = 16
|
34
|
+
|
35
|
+
def initialize(sdl=SDL, vdu_class=VDU)
|
36
|
+
@sdl = sdl
|
37
|
+
@vdu = nil
|
38
|
+
@vdu_class = vdu_class
|
39
|
+
@surface = nil
|
40
|
+
@managed = false
|
41
|
+
@fullscreen = false
|
42
|
+
@old_fullscreen = false
|
43
|
+
@screen = nil
|
44
|
+
@bezel_image = nil
|
45
|
+
@title = "Retrograph application"
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :title
|
49
|
+
attr_reader :fullscreen
|
50
|
+
attr_reader :managed
|
51
|
+
|
52
|
+
def title=(value)
|
53
|
+
@title = value.to_s
|
54
|
+
@sdl::WM.set_caption(@title, @title)
|
55
|
+
value
|
56
|
+
end
|
57
|
+
|
58
|
+
def fullscreen=(value)
|
59
|
+
@fullscreen = !!value
|
60
|
+
value
|
61
|
+
end
|
62
|
+
|
63
|
+
def toggle_fullscreen
|
64
|
+
@fullscreen = !@fullscreen
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def write_vdu(offset, data)
|
69
|
+
@vdu.write(offset, data)
|
70
|
+
end
|
71
|
+
|
72
|
+
def write_vdu_byte(offset, byte)
|
73
|
+
@vdu.write_byte(offset, byte)
|
74
|
+
end
|
75
|
+
|
76
|
+
def wait_scanlines(count)
|
77
|
+
@vdu.wait_scanlines(count)
|
78
|
+
end
|
79
|
+
|
80
|
+
def each_frame
|
81
|
+
while true
|
82
|
+
start_time = @sdl.get_ticks
|
83
|
+
@surface = @vdu.render_frame_sdl { yield }
|
84
|
+
_set_mode if @managed and @old_fullscreen != @fullscreen
|
85
|
+
@old_fullscreen = @fullscreen
|
86
|
+
_update_screen
|
87
|
+
end_time = @sdl.get_ticks
|
88
|
+
sleep_time = FRAME_TIME_MS - (end_time - start_time)
|
89
|
+
sleep(sleep_time / 1000.0) if sleep_time > 0
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def open
|
94
|
+
@sdl.init SDL::INIT_VIDEO
|
95
|
+
info = @sdl::Screen.info
|
96
|
+
@managed = info.wm_available
|
97
|
+
@vdu = @vdu_class.new
|
98
|
+
@surface = @vdu.render_frame_sdl
|
99
|
+
_set_mode
|
100
|
+
if block_given?
|
101
|
+
begin
|
102
|
+
yield
|
103
|
+
ensure
|
104
|
+
close
|
105
|
+
end
|
106
|
+
else
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def minimize
|
112
|
+
@sdl::WM.iconify
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
|
116
|
+
def close
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
def _bezel_func(d)
|
121
|
+
((1.0-(d-0.5)*2))**2 + 0.07
|
122
|
+
end
|
123
|
+
private :_bezel_func
|
124
|
+
|
125
|
+
def _generate_bezel
|
126
|
+
bezel_color = @bezel_image.format.map_rgb(8, 8, 8)
|
127
|
+
clear_color = @bezel_image.format.map_rgb(255, 255, 255)
|
128
|
+
@bezel_image.fill_rect(0, 0, OUTPUT_WIDTH, OUTPUT_HEIGHT, clear_color)
|
129
|
+
for i in 0...BEZEL_THICKNESS
|
130
|
+
section = _bezel_func(i.to_f / (BEZEL_THICKNESS-1))
|
131
|
+
section = 1.0 if section > 1.0
|
132
|
+
hlength = (OUTPUT_WIDTH / 2 * section).to_i
|
133
|
+
vlength = (OUTPUT_HEIGHT / 2 * section).to_i
|
134
|
+
@bezel_image.fill_rect(0, i, hlength, 1, bezel_color)
|
135
|
+
@bezel_image.fill_rect(0, OUTPUT_HEIGHT - 1 - i, hlength, 1, bezel_color)
|
136
|
+
@bezel_image.fill_rect(OUTPUT_WIDTH - hlength, i, hlength, 1, bezel_color)
|
137
|
+
@bezel_image.fill_rect(OUTPUT_WIDTH - hlength, OUTPUT_HEIGHT - 1 - i, hlength, 1, bezel_color)
|
138
|
+
@bezel_image.fill_rect(i, 0, 1, vlength, bezel_color)
|
139
|
+
@bezel_image.fill_rect(OUTPUT_WIDTH - 1 - i, 0, 1, vlength, bezel_color)
|
140
|
+
@bezel_image.fill_rect(i, OUTPUT_HEIGHT - vlength, 1, vlength, bezel_color)
|
141
|
+
@bezel_image.fill_rect(OUTPUT_WIDTH - 1 - i, OUTPUT_HEIGHT - vlength, 1, vlength, bezel_color)
|
142
|
+
end
|
143
|
+
@bezel_image.set_color_key(SDL::SRCCOLORKEY | SDL::RLEACCEL, clear_color)
|
144
|
+
end
|
145
|
+
private :_generate_bezel
|
146
|
+
|
147
|
+
def _set_mode
|
148
|
+
flags = SDL::HWSURFACE | SDL::DOUBLEBUF
|
149
|
+
flags |= SDL::FULLSCREEN if fullscreen?
|
150
|
+
@screen = @sdl::Screen.open(OUTPUT_WIDTH, OUTPUT_HEIGHT, 32, flags)
|
151
|
+
@bezel_image = @sdl::Surface.new(0, OUTPUT_WIDTH, OUTPUT_HEIGHT,
|
152
|
+
@screen.format)
|
153
|
+
_generate_bezel
|
154
|
+
@sdl::WM.set_caption(@title, @title)
|
155
|
+
@sdl::Mouse.hide
|
156
|
+
_update_screen
|
157
|
+
end
|
158
|
+
private :_set_mode
|
159
|
+
|
160
|
+
def _update_screen
|
161
|
+
@sdl::Surface.blit(@surface, 0, 0, 0, 0, @screen, 0, 0)
|
162
|
+
@sdl::Surface.blit(@bezel_image, 0, 0, 0, 0, @screen, 0, 0)
|
163
|
+
@screen.flip
|
164
|
+
end
|
165
|
+
private :_update_screen
|
166
|
+
|
167
|
+
def fullscreen?
|
168
|
+
not @managed or @fullscreen
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|