tea 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +674 -0
- data/COPYING.LESSER +165 -0
- data/README.rdoc +93 -0
- data/doc/example/bitmap_draw.rb +21 -0
- data/doc/example/bitmap_load.rb +14 -0
- data/doc/example/bitmap_new.rb +19 -0
- data/doc/example/circle.rb +24 -0
- data/doc/example/circle_alpha.rb +45 -0
- data/doc/example/circle_alpha_bitmap.rb +51 -0
- data/doc/example/circle_bitmap.rb +18 -0
- data/doc/example/clip.rb +46 -0
- data/doc/example/event_app.rb +45 -0
- data/doc/example/event_keyboard.rb +43 -0
- data/doc/example/event_mouse.rb +85 -0
- data/doc/example/font_hello.rb +22 -0
- data/doc/example/font_word_wrap.rb +44 -0
- data/doc/example/grab.rb +28 -0
- data/doc/example/init.rb +10 -0
- data/doc/example/lines.rb +49 -0
- data/doc/example/lines_aa.rb +44 -0
- data/doc/example/lines_alpha.rb +33 -0
- data/doc/example/point.rb +26 -0
- data/doc/example/rect.rb +15 -0
- data/doc/example/rect_alpha.rb +75 -0
- data/doc/example/screen_set_mode.rb +18 -0
- data/doc/example/screen_update.rb +14 -0
- data/doc/example/sfont_hello.rb +22 -0
- data/doc/example/smile.png +0 -0
- data/doc/example/smile_bounce.rb +44 -0
- data/doc/example/smile_move.rb +58 -0
- data/doc/example/smile_move_2.rb +78 -0
- data/doc/example/sound.rb +101 -0
- data/doc/example/state_app.rb +33 -0
- data/doc/example/state_keyboard.rb +23 -0
- data/doc/example/state_mouse.rb +60 -0
- data/doc/key_constants.textile +129 -0
- data/doc/key_modifiers.textile +19 -0
- data/doc/reference.textile +421 -0
- data/lib/tea.rb +34 -0
- data/lib/tea/c_bitmap.rb +122 -0
- data/lib/tea/c_error.rb +11 -0
- data/lib/tea/c_font.rb +302 -0
- data/lib/tea/c_sound.rb +144 -0
- data/lib/tea/m_color.rb +50 -0
- data/lib/tea/m_event.rb +65 -0
- data/lib/tea/m_event_app.rb +96 -0
- data/lib/tea/m_event_dispatch.rb +54 -0
- data/lib/tea/m_event_keyboard.rb +311 -0
- data/lib/tea/m_event_mouse.rb +189 -0
- data/lib/tea/mix_blitting.rb +31 -0
- data/lib/tea/mix_clipping.rb +71 -0
- data/lib/tea/mix_grabbing.rb +86 -0
- data/lib/tea/mix_image_saving.rb +70 -0
- data/lib/tea/mix_primitive.rb +613 -0
- data/lib/tea/o_screen.rb +98 -0
- metadata +137 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
# This file holds the mouse motion and button events.
|
2
|
+
|
3
|
+
require 'sdl'
|
4
|
+
|
5
|
+
#
|
6
|
+
module Tea
|
7
|
+
|
8
|
+
module Mouse
|
9
|
+
|
10
|
+
# Mouse button constants for mouse events.
|
11
|
+
LEFT = :LEFT
|
12
|
+
MIDDLE = :MIDDLE
|
13
|
+
RIGHT = :RIGHT
|
14
|
+
|
15
|
+
# Event generated when the mouse cursor is moved.
|
16
|
+
#
|
17
|
+
# +x+, +y+:: coordinates of the mouse cursor
|
18
|
+
# +buttons+:: an array that may contain +:left+, +:middle+ and +:right+
|
19
|
+
class Move
|
20
|
+
attr_reader :x, :y, :buttons
|
21
|
+
def initialize(sdl_event)
|
22
|
+
@x = sdl_event.x
|
23
|
+
@y = sdl_event.y
|
24
|
+
@buttons = {}
|
25
|
+
@buttons[Mouse::LEFT] = (sdl_event.state & SDL::Mouse::BUTTON_LMASK) != 0
|
26
|
+
@buttons[Mouse::MIDDLE] = (sdl_event.state & SDL::Mouse::BUTTON_MMASK) != 0
|
27
|
+
@buttons[Mouse::RIGHT] = (sdl_event.state & SDL::Mouse::BUTTON_RMASK) != 0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Event generated when a mouse button is held down.
|
32
|
+
#
|
33
|
+
# +x+, +y+:: coordinates of the mouse cursor
|
34
|
+
# +button+:: the mouse button that is down: +:left+, +:middle+ or +:right+
|
35
|
+
class Down
|
36
|
+
attr_reader :x, :y, :button
|
37
|
+
def initialize(sdl_event)
|
38
|
+
@x = sdl_event.x
|
39
|
+
@y = sdl_event.y
|
40
|
+
case sdl_event.button
|
41
|
+
when SDL::Mouse::BUTTON_LEFT then @button = Mouse::LEFT
|
42
|
+
when SDL::Mouse::BUTTON_MIDDLE then @button = Mouse::MIDDLE
|
43
|
+
when SDL::Mouse::BUTTON_RIGHT then @button = Mouse::RIGHT
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Event generated when a mouse button that was held down is released.
|
49
|
+
#
|
50
|
+
# +x+, +y+:: coordinates of the mouse cursor
|
51
|
+
# +button+:: the mouse button that was released: +:left+, +:middle+ or
|
52
|
+
# +:right+
|
53
|
+
class Up
|
54
|
+
attr_reader :x, :y, :button
|
55
|
+
def initialize(sdl_event)
|
56
|
+
@x = sdl_event.x
|
57
|
+
@y = sdl_event.y
|
58
|
+
case sdl_event.button
|
59
|
+
when SDL::Mouse::BUTTON_LEFT then @button = Mouse::LEFT
|
60
|
+
when SDL::Mouse::BUTTON_MIDDLE then @button = Mouse::MIDDLE
|
61
|
+
when SDL::Mouse::BUTTON_RIGHT then @button = Mouse::RIGHT
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Event generated when the mouse wheel is scrolled.
|
67
|
+
#
|
68
|
+
# +x+, +y+:: coordinates of the mouse cursor.
|
69
|
+
# +delta+:: 1 when scrolling down, -1 when scrolling up.
|
70
|
+
class Scroll
|
71
|
+
attr_reader :x, :y, :delta
|
72
|
+
def initialize(sdl_event)
|
73
|
+
@x = sdl_event.x
|
74
|
+
@y = sdl_event.y
|
75
|
+
case sdl_event.button
|
76
|
+
when Event::BUTTON_WHEELDOWN_ then @delta = 1
|
77
|
+
when Event::BUTTON_WHEELUP_ then @delta = -1
|
78
|
+
else
|
79
|
+
raise Tea::Error, "Tea::Mouse::Scroll given an unexpected event: #{sdl_event.inspect}", caller
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Defaults for Mouse.x, Mouse.y, Mouse.left?, Mouse.middle?, Mouse.right?
|
85
|
+
# and Mouse.in_app?
|
86
|
+
@x = 0
|
87
|
+
@y = 0
|
88
|
+
@left = false
|
89
|
+
@middle = false
|
90
|
+
@right = false
|
91
|
+
@in_app = false
|
92
|
+
|
93
|
+
# Report the x position of the mouse in the screen window. Updated when
|
94
|
+
# Event.get is called.
|
95
|
+
def Mouse.x
|
96
|
+
@x
|
97
|
+
end
|
98
|
+
|
99
|
+
# Report the y position of the mouse in the screen window. Updated when
|
100
|
+
# Event.get is called.
|
101
|
+
def Mouse.y
|
102
|
+
@y
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns true if the left mouse button is down. Updated when Event.get is
|
106
|
+
# called.
|
107
|
+
def Mouse.left?
|
108
|
+
@left
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns true if the middle mouse button is down. Updated when Event.get
|
112
|
+
# is called.
|
113
|
+
def Mouse.middle?
|
114
|
+
@middle
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns true if the right mouse button is down. Updated when Event.get
|
118
|
+
# is called.
|
119
|
+
def Mouse.right?
|
120
|
+
@right
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns true if the mouse is in the screen window
|
124
|
+
def Mouse.in_app?
|
125
|
+
@in_app
|
126
|
+
end
|
127
|
+
|
128
|
+
# Update the mouse state, so that Mouse.x, Mouse.y, Mouse.left?,
|
129
|
+
# Mouse.middle? and Mouse.right? return recent data.
|
130
|
+
def Mouse.update_state(tea_event)
|
131
|
+
case tea_event
|
132
|
+
when Move
|
133
|
+
@x = tea_event.x
|
134
|
+
@y = tea_event.y
|
135
|
+
when Down
|
136
|
+
case tea_event.button
|
137
|
+
when LEFT then @left = true
|
138
|
+
when MIDDLE then @middle = true
|
139
|
+
when RIGHT then @right = true
|
140
|
+
end
|
141
|
+
when Up
|
142
|
+
case tea_event.button
|
143
|
+
when LEFT then @left = false
|
144
|
+
when MIDDLE then @middle = false
|
145
|
+
when RIGHT then @right = false
|
146
|
+
end
|
147
|
+
when Lost
|
148
|
+
@in_app = false
|
149
|
+
when Gained
|
150
|
+
@in_app = true
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
module Event
|
156
|
+
|
157
|
+
# Missing mouse wheel button constants from rubysdl. For internal use only.
|
158
|
+
BUTTON_WHEELUP_ = 4
|
159
|
+
BUTTON_WHEELDOWN_ = 5
|
160
|
+
|
161
|
+
# Convert a mouse-related SDL::Event into a Tea event. For internal use only.
|
162
|
+
def Event.translate_mouse_event(sdl_event)
|
163
|
+
out_events = []
|
164
|
+
|
165
|
+
case sdl_event
|
166
|
+
when SDL::Event::MouseMotion
|
167
|
+
out_events.push Mouse::Move.new(sdl_event)
|
168
|
+
when SDL::Event::MouseButtonDown
|
169
|
+
case sdl_event.button
|
170
|
+
when SDL::Mouse::BUTTON_LEFT, SDL::Mouse::BUTTON_MIDDLE, SDL::Mouse::BUTTON_RIGHT
|
171
|
+
out_events.push Mouse::Down.new(sdl_event)
|
172
|
+
when BUTTON_WHEELDOWN_, BUTTON_WHEELUP_
|
173
|
+
out_events.push Mouse::Scroll.new(sdl_event)
|
174
|
+
end
|
175
|
+
when SDL::Event::MouseButtonUp
|
176
|
+
# Ignore MouseButtonUp for the scroll wheel.
|
177
|
+
case sdl_event.button
|
178
|
+
when SDL::Mouse::BUTTON_LEFT, SDL::Mouse::BUTTON_MIDDLE, SDL::Mouse::BUTTON_RIGHT
|
179
|
+
out_events.push Mouse::Up.new(sdl_event)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
out_events
|
184
|
+
end
|
185
|
+
private_class_method :translate_mouse_event
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# This file contains the Blitting mixin.
|
2
|
+
|
3
|
+
require 'sdl'
|
4
|
+
|
5
|
+
#
|
6
|
+
module Tea
|
7
|
+
|
8
|
+
# The Blitting mixin allows objects with SDL::Surface to draw or 'blit' onto
|
9
|
+
# each other.
|
10
|
+
#
|
11
|
+
# To use this mixin, include it and write/alias a blitting_buffer method
|
12
|
+
# that gets the SDL::Surface.
|
13
|
+
#
|
14
|
+
# include Blitting
|
15
|
+
# def blitting_buffer
|
16
|
+
# @my_sdl_surface
|
17
|
+
# end
|
18
|
+
module Blitting
|
19
|
+
|
20
|
+
# Draw the source_blittable onto the current object at (x, y).
|
21
|
+
#
|
22
|
+
# source_blittable needs to include the Blitting mixin too.
|
23
|
+
def blit(source_blittable, x, y)
|
24
|
+
src = source_blittable.blitting_buffer
|
25
|
+
dest = blitting_buffer
|
26
|
+
SDL::Surface.blit src, 0, 0, src.w, src.h, dest, x, y
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# This file holds the Clipping mixin.
|
2
|
+
|
3
|
+
require 'sdl'
|
4
|
+
|
5
|
+
#
|
6
|
+
module Tea
|
7
|
+
|
8
|
+
# The Clipping mixin enables anything with an SDL-like buffer can get, set
|
9
|
+
# and use a clipping rectangle to restrict where drawing takes place.
|
10
|
+
#
|
11
|
+
# To use the Clipping mixin, include it, and provide a clipping_buffer method
|
12
|
+
# that returns the buffer to be clipped.
|
13
|
+
#
|
14
|
+
# include Tea::Clipping
|
15
|
+
# def clipping_buffer
|
16
|
+
# @sdl_buffer
|
17
|
+
# end
|
18
|
+
module Clipping
|
19
|
+
|
20
|
+
# Get, set or run a block with a clipping rectangle that restricts where
|
21
|
+
# drawing can take place.
|
22
|
+
#
|
23
|
+
# Getting the current clipping rectangle:
|
24
|
+
#
|
25
|
+
# x, y, width, height = clippable_object.clip
|
26
|
+
#
|
27
|
+
# Setting a new clipping rectangle:
|
28
|
+
#
|
29
|
+
# old_x, old_y, old_w, old_h = clippable_object.clip(new_x, new_y, new_w, new_h)
|
30
|
+
#
|
31
|
+
# Running a block with a clipping rectangle temporarily set:
|
32
|
+
#
|
33
|
+
# clippable_object.clip(clip_x, clip_y, clip_w, clip_h) do
|
34
|
+
# # Draw onto clippable_object within clip rect here.
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
def clip(x=nil, y=nil, w=nil, h=nil)
|
38
|
+
buffer = clipping_buffer
|
39
|
+
|
40
|
+
# rubysdl's SDL::Surface#get_clip_rect doesn't return anything useful.
|
41
|
+
# Seems to be a bug, or at least compilation quirk.
|
42
|
+
@clipping_rect = [0, 0, buffer.w, buffer.h] unless @clipping_rect
|
43
|
+
old_clipping_rect = @clipping_rect
|
44
|
+
|
45
|
+
case
|
46
|
+
when !(x || y || w || h) # Get clip rect.
|
47
|
+
# Do nothing.
|
48
|
+
when x && y && w && h && !block_given? # Set clip rect.
|
49
|
+
@clipping_rect = [x, y, w, h]
|
50
|
+
buffer.set_clip_rect *@clipping_rect
|
51
|
+
when x && y && w && h && block_given? # Run block with clip rect.
|
52
|
+
@clipping_rect = [x, y, w, h]
|
53
|
+
buffer.set_clip_rect *@clipping_rect
|
54
|
+
begin
|
55
|
+
yield
|
56
|
+
ensure
|
57
|
+
@clipping_rect = old_clipping_rect
|
58
|
+
buffer.set_clip_rect *old_clipping_rect
|
59
|
+
end
|
60
|
+
else
|
61
|
+
arg_count = 0
|
62
|
+
[x, y, w, h].each { |arg| arg_count += 1 if arg == nil }
|
63
|
+
raise ArgumentError, "wrong number of arguments (#{arg_count} for 0 or 4)", caller
|
64
|
+
end
|
65
|
+
|
66
|
+
old_clipping_rect
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# This file contains the Grabbing mixin, which makes a new Bitmap object based
|
2
|
+
# on some part of a bitmap-like pixel buffer.
|
3
|
+
|
4
|
+
require 'sdl'
|
5
|
+
|
6
|
+
require 'tea/c_bitmap'
|
7
|
+
require 'tea/m_color'
|
8
|
+
|
9
|
+
#
|
10
|
+
module Tea
|
11
|
+
|
12
|
+
# This module gives bitmap-like objects a +grab+ method that takes a
|
13
|
+
# 'snapshot' of some part of it and returns it as a new Bitmap.
|
14
|
+
#
|
15
|
+
# To use this mixin, include it and define a +grabbing_buffer+ method:
|
16
|
+
#
|
17
|
+
# include Grabbing
|
18
|
+
# def grabbing_buffer
|
19
|
+
# @my_sdl_buffer
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
module Grabbing
|
23
|
+
|
24
|
+
# Grab some part of the bitmap-like object, and return it as a new
|
25
|
+
# Tea::Bitmap object.
|
26
|
+
#
|
27
|
+
# This can be called in two ways:
|
28
|
+
#
|
29
|
+
# * ():: create a deep-copy of the whole bitmap-like object.
|
30
|
+
# * (x, y, w, h):: copy the pixels within the box at (x, y) of size w * h.
|
31
|
+
#
|
32
|
+
# May raise Tea::Error if the box given (if any) is outside the bitmap.
|
33
|
+
def grab(*box)
|
34
|
+
buffer = grabbing_buffer
|
35
|
+
|
36
|
+
case box.length
|
37
|
+
when 0
|
38
|
+
x = 0
|
39
|
+
y = 0
|
40
|
+
w = buffer.w
|
41
|
+
h = buffer.h
|
42
|
+
when 4
|
43
|
+
if clipped_box = grabbing_clip(*box, 0, 0, buffer.w, buffer.h)
|
44
|
+
x, y, w, h = clipped_box
|
45
|
+
else
|
46
|
+
raise Tea::Error, "cannot grab (#{x}, #{y}, #{w}, #{h}), not within (#{buffer.x}, #{buffer.y}, #{buffer.w}, #{buffer.h})", caller
|
47
|
+
end
|
48
|
+
else
|
49
|
+
raise ArgumentError, "wrong number of arguments (#{box.length} for 0 or 4)", caller
|
50
|
+
end
|
51
|
+
|
52
|
+
bitmap = Bitmap.new(w, h, Tea::Color::CLEAR)
|
53
|
+
|
54
|
+
for buf_y in y...(y + h)
|
55
|
+
for buf_x in x...(x + w)
|
56
|
+
bitmap[buf_x - x, buf_y - y] = Color.mix(*buffer.get_rgba(buffer[buf_x, buf_y]))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
bitmap
|
61
|
+
end
|
62
|
+
|
63
|
+
# Keep the rect (x, y, w, h) within the rect (bound_x, bound_y, bound_w, bound_h).
|
64
|
+
#
|
65
|
+
# Returns [in_x, in_y, in_w, in_h], defining a rect like (x, y, w, h), but
|
66
|
+
# within the bounding rect. Returns nil if the rect given is not within
|
67
|
+
# the bounding rect at all, i.e. w or h would be less than 1.
|
68
|
+
def grabbing_clip(x, y, w, h, bound_x, bound_y, bound_w, bound_h)
|
69
|
+
return nil if x + w <= bound_x || x >= bound_x + bound_w || y + h <= bound_y || y >= bound_y + bound_h
|
70
|
+
|
71
|
+
in_x = (x >= bound_x) ? x : bound_x
|
72
|
+
in_y = (y >= bound_y) ? y : bound_y
|
73
|
+
|
74
|
+
x2 = x + w
|
75
|
+
y2 = y + h
|
76
|
+
in_x2 = (x2 <= bound_x + bound_w) ? x2 : bound_x + bound_w
|
77
|
+
in_y2 = (y2 <= bound_y + bound_h) ? y2 : bound_y + bound_h
|
78
|
+
in_w = in_x2 - in_x
|
79
|
+
in_h = in_y2 - in_y
|
80
|
+
|
81
|
+
[in_x, in_y, in_w, in_h]
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# This file holds the ImageSaving mixin.
|
2
|
+
|
3
|
+
require 'RMagick'
|
4
|
+
require 'sdl'
|
5
|
+
|
6
|
+
require 'tea/c_error'
|
7
|
+
|
8
|
+
#
|
9
|
+
module Tea
|
10
|
+
|
11
|
+
# This mixin allows SDL surface-backed classes to be saved as image files.
|
12
|
+
#
|
13
|
+
# To use, include the mixin and implement an image_saving_buffer method:
|
14
|
+
#
|
15
|
+
# include ImageSaving
|
16
|
+
# def image_saving_buffer
|
17
|
+
# @my_sdl_buffer
|
18
|
+
# end
|
19
|
+
module ImageSaving
|
20
|
+
|
21
|
+
# Save the object as an image file at the given path.
|
22
|
+
#
|
23
|
+
# The format is determined by the extension at the end of the path. ".bmp"
|
24
|
+
# will save it as Windows Bitmap image.
|
25
|
+
#
|
26
|
+
# May raise Tea::Error if the desired image file format can't be
|
27
|
+
# determined, or isn't supported.
|
28
|
+
def save(path)
|
29
|
+
extension_match = /\..+$/.match(path)
|
30
|
+
|
31
|
+
if extension_match
|
32
|
+
ext = extension_match[0]
|
33
|
+
case ext
|
34
|
+
when '.bmp'
|
35
|
+
image_saving_buffer.save_bmp path
|
36
|
+
when '.png'
|
37
|
+
image_saving_cheat_save path
|
38
|
+
else
|
39
|
+
raise Tea::Error, "can't determine image format '#{ext}' for saving", caller
|
40
|
+
end
|
41
|
+
else
|
42
|
+
raise Tea::Error, "can't determine desired image file format for #{path}", caller
|
43
|
+
end
|
44
|
+
|
45
|
+
rescue SDL::Error => e
|
46
|
+
raise Tea::Error, e.message, e.backtrace
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Cheat by using RMagick to save in formats other than BMP.
|
52
|
+
def image_saving_cheat_save(path)
|
53
|
+
buffer = image_saving_buffer
|
54
|
+
|
55
|
+
buffer_string = String.new
|
56
|
+
for y in 0...buffer.h
|
57
|
+
for x in 0...buffer.w
|
58
|
+
r, g, b, a = buffer.get_rgba(buffer[x, y])
|
59
|
+
buffer_string << r << g << b << a
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
image = Magick::Image.new(buffer.w, buffer.h)
|
64
|
+
image.import_pixels 0, 0, buffer.h, buffer.w, 'RGBA', buffer_string, Magick::CharPixel
|
65
|
+
image.write path
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|