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
data/lib/tea/c_sound.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# This file contains the Sound class.
|
2
|
+
|
3
|
+
require 'sdl'
|
4
|
+
|
5
|
+
require 'tea/c_error'
|
6
|
+
|
7
|
+
#
|
8
|
+
module Tea
|
9
|
+
|
10
|
+
# The Sound class allows loading and playing of audio files.
|
11
|
+
#
|
12
|
+
# Currently supported formats: OGG, WAV, AIFF, RIFF, VOC.
|
13
|
+
class Sound
|
14
|
+
|
15
|
+
# Sound states returned by Tea::Sound#state.
|
16
|
+
STOPPED = :STOPPED
|
17
|
+
PLAYING = :PLAYING
|
18
|
+
PAUSED = :PAUSED
|
19
|
+
|
20
|
+
# As per Ruby/SDL's mixer API.
|
21
|
+
VOLUME_MAX = 128
|
22
|
+
|
23
|
+
# Tracked because Ruby/SDL's mixer API can't get the channel volumes.
|
24
|
+
@@volume = VOLUME_MAX
|
25
|
+
|
26
|
+
# Tracks which Sound objects are playing in which channels, to report
|
27
|
+
# accurate playing state info.
|
28
|
+
@@channel_sound_ids = []
|
29
|
+
|
30
|
+
# Get the master volume. Volume ranges from 0..128 inclusive.
|
31
|
+
def Sound.volume
|
32
|
+
@@volume
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set the master volume. new_volume should be between 0..128 inclusive.
|
36
|
+
def Sound.volume=(new_volume)
|
37
|
+
v = (new_volume >= 0) ? (new_volume <= VOLUME_MAX ? new_volume : VOLUME_MAX) : 0
|
38
|
+
SDL::Mixer.set_volume -1, new_volume
|
39
|
+
@@volume = v
|
40
|
+
end
|
41
|
+
|
42
|
+
# Pause all sound.
|
43
|
+
def Sound.pause_all
|
44
|
+
SDL::Mixer.pause -1
|
45
|
+
end
|
46
|
+
|
47
|
+
# Resume playing all paused sound.
|
48
|
+
def Sound.resume_all
|
49
|
+
SDL::Mixer.resume -1
|
50
|
+
end
|
51
|
+
|
52
|
+
# Stop all sounds, playing or paused.
|
53
|
+
def Sound.stop_all
|
54
|
+
SDL::Mixer.halt -1
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# Load a sound file.
|
59
|
+
#
|
60
|
+
# May raise Tea::Error on failure.
|
61
|
+
def initialize(path)
|
62
|
+
@wave = SDL::Mixer::Wave.load(path)
|
63
|
+
@channel = -1
|
64
|
+
|
65
|
+
# Tracked because Ruby/SDL's mixer API can't get sound volumes.
|
66
|
+
@volume = VOLUME_MAX
|
67
|
+
rescue SDL::Error => e
|
68
|
+
raise Tea::Error, e.message, e.backtrace
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get the sound's volume. Volume ranges from 0..128 inclusive.
|
72
|
+
def volume
|
73
|
+
@volume
|
74
|
+
end
|
75
|
+
|
76
|
+
# Set the sound's volume. new_volume should be between 0..128 inclusive.
|
77
|
+
def volume=(new_volume)
|
78
|
+
v = (new_volume >= 0) ? (new_volume <= VOLUME_MAX ? new_volume : VOLUME_MAX) : 0
|
79
|
+
@wave.set_volume v
|
80
|
+
@volume = v
|
81
|
+
end
|
82
|
+
|
83
|
+
# Play the sound. If this sound is still playing, cut it off and start
|
84
|
+
# playing it from the start.
|
85
|
+
#
|
86
|
+
# loops should be the number of times to repeat playing the sound. Use -1
|
87
|
+
# to loop the sound forever.
|
88
|
+
def play(loops=0)
|
89
|
+
SDL::Mixer.halt(@channel) if channel_valid?
|
90
|
+
|
91
|
+
@channel = SDL::Mixer.play_channel(-1, @wave, loops)
|
92
|
+
@@channel_sound_ids[@channel] = object_id
|
93
|
+
end
|
94
|
+
|
95
|
+
# Pause the sound if it's playing, otherwise do nothing.
|
96
|
+
def pause
|
97
|
+
SDL::Mixer.pause(@channel) if channel_valid?
|
98
|
+
end
|
99
|
+
|
100
|
+
# Resume the sound if it's paused, otherwise do nothing.
|
101
|
+
def resume
|
102
|
+
SDL::Mixer.resume(@channel) if channel_valid?
|
103
|
+
end
|
104
|
+
|
105
|
+
# Stop the sound if it's playing or paused, otherwise do nothing.
|
106
|
+
def stop
|
107
|
+
SDL::Mixer.halt(@channel) if channel_valid?
|
108
|
+
end
|
109
|
+
|
110
|
+
# Check if the sound is stopped, playing or paused. Returns one of
|
111
|
+
# Tea::Sound::STOPPED, Tea::Sound::PLAYING or Tea::Sound::PAUSED. A sound
|
112
|
+
# that isn't playing or paused is considered stopped.
|
113
|
+
def state
|
114
|
+
if channel_valid?
|
115
|
+
if SDL::Mixer.play?(@channel)
|
116
|
+
# For some reason, pause? returns 0 and 1 instead of true and false.
|
117
|
+
if SDL::Mixer.pause?(@channel) != 0
|
118
|
+
PAUSED
|
119
|
+
else
|
120
|
+
PLAYING
|
121
|
+
end
|
122
|
+
else
|
123
|
+
STOPPED
|
124
|
+
end
|
125
|
+
else
|
126
|
+
STOPPED
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
# Check if @channel really maps to this sound. Returns the channel number
|
133
|
+
# if so, otherwise nil.
|
134
|
+
def channel_valid?
|
135
|
+
if @channel >= 0 && @@channel_sound_ids[@channel] == object_id
|
136
|
+
@channel
|
137
|
+
else
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
data/lib/tea/m_color.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# This file holds the Color module.
|
2
|
+
|
3
|
+
#
|
4
|
+
module Tea
|
5
|
+
|
6
|
+
# The Color module holds utility methods that can mix and split colors.
|
7
|
+
module Color
|
8
|
+
|
9
|
+
# Create a color from red, green, blue and optionally alpha parts.
|
10
|
+
# All values passed in should be within 0..255 inclusive.
|
11
|
+
def Color.mix(red, green, blue, alpha=255)
|
12
|
+
((red & 0xff) << 24) |
|
13
|
+
((green & 0xff) << 16) |
|
14
|
+
((blue & 0xff) << 8) |
|
15
|
+
(alpha & 0xff)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Break a colour up into its red, green, blue and alpha parts.
|
19
|
+
# Returns a 4-element array of the form [red, green, blue, alpha], where
|
20
|
+
# each element is within 0..255 inclusive.
|
21
|
+
def Color.split(color)
|
22
|
+
[(color & 0xff000000) >> 24,
|
23
|
+
(color & 0x00ff0000) >> 16,
|
24
|
+
(color & 0x0000ff00) >> 8,
|
25
|
+
(color & 0x000000ff)]
|
26
|
+
end
|
27
|
+
|
28
|
+
CLEAR = Color.mix( 0, 0, 0, 0)
|
29
|
+
|
30
|
+
BLACK = Color.mix( 0, 0, 0)
|
31
|
+
DARK_RED = Color.mix(128, 0, 0)
|
32
|
+
DARK_GREEN = Color.mix( 0, 128, 0)
|
33
|
+
DARK_YELLOW = Color.mix(128, 128, 0)
|
34
|
+
DARK_BLUE = Color.mix( 0, 0, 128)
|
35
|
+
DARK_MAGENTA = Color.mix(128, 0, 128)
|
36
|
+
DARK_CYAN = Color.mix( 0, 128, 128)
|
37
|
+
DARK_GRAY = Color.mix(128, 128, 128)
|
38
|
+
|
39
|
+
GRAY = Color.mix(192, 192, 192)
|
40
|
+
RED = Color.mix(255, 0, 0)
|
41
|
+
GREEN = Color.mix( 0, 255, 0)
|
42
|
+
YELLOW = Color.mix(255, 255, 0)
|
43
|
+
BLUE = Color.mix( 0, 0, 255)
|
44
|
+
MAGENTA = Color.mix(255, 0, 255)
|
45
|
+
CYAN = Color.mix( 0, 255, 255)
|
46
|
+
WHITE = Color.mix(255, 255, 255)
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/tea/m_event.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# This file holds the core Event module.
|
2
|
+
|
3
|
+
require 'sdl'
|
4
|
+
|
5
|
+
require 'tea/c_error'
|
6
|
+
require 'tea/m_event_app'
|
7
|
+
require 'tea/m_event_dispatch'
|
8
|
+
require 'tea/m_event_keyboard'
|
9
|
+
require 'tea/m_event_mouse'
|
10
|
+
|
11
|
+
#
|
12
|
+
module Tea
|
13
|
+
|
14
|
+
# The Event module allows access to the event queue, and the classes of
|
15
|
+
# events that come out.
|
16
|
+
module Event
|
17
|
+
|
18
|
+
# Tea's generated event queue. Add to the back, get from the front.
|
19
|
+
@@event_queue = []
|
20
|
+
|
21
|
+
# Get the next event in the event queue. If wait is true and there are no
|
22
|
+
# events to return, this method will wait until there is one and return it.
|
23
|
+
# Otherwise, an empty event queue will return nil.
|
24
|
+
#
|
25
|
+
# May raise Tea::Error if getting an event fails.
|
26
|
+
def Event.get(wait=false)
|
27
|
+
if @@event_queue.length == 0
|
28
|
+
if wait
|
29
|
+
begin
|
30
|
+
sdl_event = SDL::Event.wait
|
31
|
+
if (out_events = translate_event(sdl_event))
|
32
|
+
@@event_queue.push *out_events
|
33
|
+
end
|
34
|
+
end until @@event_queue.length > 0
|
35
|
+
else
|
36
|
+
if (out_events = translate_event(SDL::Event.poll))
|
37
|
+
@@event_queue.push *out_events
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
tea_event = @@event_queue.shift
|
43
|
+
[App, Mouse, Kbd].each { |state_holder| state_holder.update_state tea_event }
|
44
|
+
tea_event
|
45
|
+
rescue SDL::Error => e
|
46
|
+
raise Tea::Error, e.message, e.backtrace
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convert an SDL::Event into one or more Tea events. May return nil, a
|
50
|
+
# single event object or multiple events in an array.
|
51
|
+
def Event.translate_event(sdl_event)
|
52
|
+
case sdl_event
|
53
|
+
when SDL::Event::Active, SDL::Event::Quit
|
54
|
+
translate_app_event sdl_event
|
55
|
+
when SDL::Event::MouseMotion, SDL::Event::MouseButtonDown, SDL::Event::MouseButtonUp
|
56
|
+
translate_mouse_event sdl_event
|
57
|
+
when SDL::Event::KeyDown, SDL::Event::KeyUp
|
58
|
+
translate_keyboard_event sdl_event
|
59
|
+
end
|
60
|
+
end
|
61
|
+
private_class_method :translate_event
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# This file holds app-related event handling.
|
2
|
+
|
3
|
+
require 'sdl'
|
4
|
+
|
5
|
+
#
|
6
|
+
module Tea
|
7
|
+
|
8
|
+
module Mouse
|
9
|
+
# Event generated when mouse focus is gained.
|
10
|
+
class Gained
|
11
|
+
end
|
12
|
+
|
13
|
+
# Event generated when mouse focus is lost.
|
14
|
+
class Lost
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Kbd
|
19
|
+
# Event generated when keyboard input focus is gained.
|
20
|
+
class Gained
|
21
|
+
end
|
22
|
+
|
23
|
+
# Event generated when keyboard input focus is lost.
|
24
|
+
class Lost
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module App
|
29
|
+
# Event generated when the player acts to close the screen window.
|
30
|
+
class Exit
|
31
|
+
end
|
32
|
+
|
33
|
+
# Event generated when the screen window is minimised.
|
34
|
+
class Minimized
|
35
|
+
end
|
36
|
+
|
37
|
+
# Event generated when the screen window is restored from being minimised.
|
38
|
+
class Restored
|
39
|
+
end
|
40
|
+
|
41
|
+
# Default for App.visible?.
|
42
|
+
@visible = true
|
43
|
+
|
44
|
+
# Returns true if the screen window has not been minimised, otherwise
|
45
|
+
# false.
|
46
|
+
def App.visible?
|
47
|
+
@visible
|
48
|
+
end
|
49
|
+
|
50
|
+
# Update the reported app state when a Tea event is retrieved, so
|
51
|
+
# App.visible? returns the right status. Called automatically by
|
52
|
+
# Event.get.
|
53
|
+
def App.update_state(tea_event)
|
54
|
+
case tea_event
|
55
|
+
when Minimized then @visible = false
|
56
|
+
when Restored then @visible = true
|
57
|
+
end
|
58
|
+
@visible
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module Event
|
63
|
+
|
64
|
+
# APP constants rubysdl is missing. For internal use only.
|
65
|
+
APPMOUSEFOCUS_ = 0x01
|
66
|
+
APPINPUTFOCUS_ = 0x02
|
67
|
+
APPACTIVE_ = 0x04
|
68
|
+
|
69
|
+
# Translates an app-related SDL::Event into an array of Tea::Event
|
70
|
+
# objects. For internal use only.
|
71
|
+
def Event.translate_app_event(sdl_event)
|
72
|
+
out_events = []
|
73
|
+
|
74
|
+
case sdl_event
|
75
|
+
when SDL::Event::Quit
|
76
|
+
out_events.push App::Exit.new
|
77
|
+
|
78
|
+
when SDL::Event::Active
|
79
|
+
if (sdl_event.state & APPACTIVE_) != 0
|
80
|
+
out_events.push sdl_event.gain ? App::Restored.new : App::Minimized.new
|
81
|
+
end
|
82
|
+
if (sdl_event.state & APPINPUTFOCUS_) != 0
|
83
|
+
out_events.push sdl_event.gain ? Kbd::Gained.new : Kbd::Lost.new
|
84
|
+
end
|
85
|
+
if (sdl_event.state & APPMOUSEFOCUS_) != 0
|
86
|
+
out_events.push sdl_event.gain ? Mouse::Gained.new : Mouse::Lost.new
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
out_events
|
91
|
+
end
|
92
|
+
private_class_method :translate_app_event
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# This file contains the event dispatch mixin.
|
2
|
+
|
3
|
+
require 'tea/c_error'
|
4
|
+
|
5
|
+
#
|
6
|
+
module Tea
|
7
|
+
|
8
|
+
module Event
|
9
|
+
|
10
|
+
# The Dispatch mixin gives any object the ability to handle Tea events with
|
11
|
+
# method calls. To use it, include the mixin in your class
|
12
|
+
#
|
13
|
+
# include Tea::Event::Dispatch
|
14
|
+
#
|
15
|
+
# define the handling methods (all optional)
|
16
|
+
#
|
17
|
+
# def app_exit; end
|
18
|
+
# def app_restored; end
|
19
|
+
# def app_minimized; end
|
20
|
+
# def kbd_down; end
|
21
|
+
# def kbd_up; end
|
22
|
+
# def mouse_move; end
|
23
|
+
# def mouse_down; end
|
24
|
+
# def mouse_up; end
|
25
|
+
# def mouse_scroll; end
|
26
|
+
#
|
27
|
+
# and when you need events handled, call your instance's dispatch_event
|
28
|
+
# method
|
29
|
+
#
|
30
|
+
# handler = MyEventHandler.new
|
31
|
+
# ...
|
32
|
+
# loop do
|
33
|
+
# event = Tea::Event.get
|
34
|
+
# handler.dispatch_event event if event
|
35
|
+
# ...
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# Tea will then call the method matching the event received.
|
39
|
+
module Dispatch
|
40
|
+
|
41
|
+
def dispatch_event(tea_event)
|
42
|
+
class_string = tea_event.class.to_s
|
43
|
+
unless class_string =~ /^Tea::(?:App|Kbd|Mouse)::[A-Za-z]+$/
|
44
|
+
raise Tea::Error, "Can't dispatch on class #{class_string}", caller
|
45
|
+
end
|
46
|
+
msg = class_string.split('::', 2)[1].sub('::', '_').downcase.intern
|
47
|
+
send msg, tea_event if respond_to?(msg)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,311 @@
|
|
1
|
+
# This file holds the key press and modifier events.
|
2
|
+
|
3
|
+
require 'sdl'
|
4
|
+
|
5
|
+
#
|
6
|
+
module Tea
|
7
|
+
|
8
|
+
module Kbd
|
9
|
+
|
10
|
+
# Superclass for all Kbd events.
|
11
|
+
class Event
|
12
|
+
@@sdl_key_table = {
|
13
|
+
SDL::Key::BACKSPACE => :BACKSPACE,
|
14
|
+
SDL::Key::TAB => :TAB,
|
15
|
+
#SDL::Key::CLEAR => clear, # ???
|
16
|
+
SDL::Key::RETURN => :ENTER,
|
17
|
+
SDL::Key::PAUSE => :PAUSE,
|
18
|
+
SDL::Key::ESCAPE => :ESCAPE,
|
19
|
+
SDL::Key::SPACE => :SPACE,
|
20
|
+
SDL::Key::EXCLAIM => :EXCLAMATION_MARK,
|
21
|
+
SDL::Key::QUOTEDBL => :DOUBLE_QUOTE,
|
22
|
+
SDL::Key::HASH => :HASH,
|
23
|
+
SDL::Key::DOLLAR => :DOLLAR,
|
24
|
+
SDL::Key::AMPERSAND => :AMPERSAND,
|
25
|
+
SDL::Key::QUOTE => :QUOTE,
|
26
|
+
SDL::Key::LEFTPAREN => :OPEN_PAREN,
|
27
|
+
SDL::Key::RIGHTPAREN => :CLOSE_PAREN,
|
28
|
+
SDL::Key::ASTERISK => :ASTERISK,
|
29
|
+
SDL::Key::PLUS => :PLUS,
|
30
|
+
SDL::Key::COMMA => :COMMA,
|
31
|
+
SDL::Key::MINUS => :MINUS,
|
32
|
+
SDL::Key::PERIOD => :PERIOD,
|
33
|
+
SDL::Key::SLASH => :SLASH,
|
34
|
+
SDL::Key::K0 => :K0,
|
35
|
+
SDL::Key::K1 => :K1,
|
36
|
+
SDL::Key::K2 => :K2,
|
37
|
+
SDL::Key::K3 => :K3,
|
38
|
+
SDL::Key::K4 => :K4,
|
39
|
+
SDL::Key::K5 => :K5,
|
40
|
+
SDL::Key::K6 => :K6,
|
41
|
+
SDL::Key::K7 => :K7,
|
42
|
+
SDL::Key::K8 => :K8,
|
43
|
+
SDL::Key::K9 => :K9,
|
44
|
+
SDL::Key::COLON => :COLON,
|
45
|
+
SDL::Key::SEMICOLON => :SEMICOLON,
|
46
|
+
SDL::Key::LESS => :LESS_THAN,
|
47
|
+
SDL::Key::EQUALS => :EQUALS,
|
48
|
+
SDL::Key::GREATER => :GREATER_THAN,
|
49
|
+
SDL::Key::QUESTION => :QUESTION_MARK,
|
50
|
+
SDL::Key::AT => :AT,
|
51
|
+
SDL::Key::LEFTBRACKET => :OPEN_SQUARE_BRACKET,
|
52
|
+
SDL::Key::BACKSLASH => :BACKSLASH,
|
53
|
+
SDL::Key::RIGHTBRACKET => :CLOSE_SQUARE_BRACKET,
|
54
|
+
SDL::Key::CARET => :CARET,
|
55
|
+
SDL::Key::UNDERSCORE => :UNDERSCORE,
|
56
|
+
SDL::Key::BACKQUOTE => :BACKTICK,
|
57
|
+
SDL::Key::A => :A,
|
58
|
+
SDL::Key::B => :B,
|
59
|
+
SDL::Key::C => :C,
|
60
|
+
SDL::Key::D => :D,
|
61
|
+
SDL::Key::E => :E,
|
62
|
+
SDL::Key::F => :F,
|
63
|
+
SDL::Key::G => :G,
|
64
|
+
SDL::Key::H => :H,
|
65
|
+
SDL::Key::I => :I,
|
66
|
+
SDL::Key::J => :J,
|
67
|
+
SDL::Key::K => :K,
|
68
|
+
SDL::Key::L => :L,
|
69
|
+
SDL::Key::M => :M,
|
70
|
+
SDL::Key::N => :N,
|
71
|
+
SDL::Key::O => :O,
|
72
|
+
SDL::Key::P => :P,
|
73
|
+
SDL::Key::Q => :Q,
|
74
|
+
SDL::Key::R => :R,
|
75
|
+
SDL::Key::S => :S,
|
76
|
+
SDL::Key::T => :T,
|
77
|
+
SDL::Key::U => :U,
|
78
|
+
SDL::Key::V => :V,
|
79
|
+
SDL::Key::W => :W,
|
80
|
+
SDL::Key::X => :X,
|
81
|
+
SDL::Key::Y => :Y,
|
82
|
+
SDL::Key::Z => :Z,
|
83
|
+
SDL::Key::DELETE => :DELETE,
|
84
|
+
SDL::Key::KP0 => :NP0,
|
85
|
+
SDL::Key::KP1 => :NP1,
|
86
|
+
SDL::Key::KP2 => :NP2,
|
87
|
+
SDL::Key::KP3 => :NP3,
|
88
|
+
SDL::Key::KP4 => :NP4,
|
89
|
+
SDL::Key::KP5 => :NP5,
|
90
|
+
SDL::Key::KP6 => :NP6,
|
91
|
+
SDL::Key::KP7 => :NP7,
|
92
|
+
SDL::Key::KP8 => :NP8,
|
93
|
+
SDL::Key::KP9 => :NP9,
|
94
|
+
SDL::Key::KP_PERIOD => :NP_PERIOD,
|
95
|
+
SDL::Key::KP_DIVIDE => :NP_DIVIDE,
|
96
|
+
SDL::Key::KP_MULTIPLY => :NP_MULTIPLY,
|
97
|
+
SDL::Key::KP_MINUS => :NP_MINUS,
|
98
|
+
SDL::Key::KP_PLUS => :NP_PLUS,
|
99
|
+
SDL::Key::KP_ENTER => :NP_ENTER,
|
100
|
+
SDL::Key::KP_EQUALS => :NP_EQUALS,
|
101
|
+
SDL::Key::UP => :UP,
|
102
|
+
SDL::Key::DOWN => :DOWN,
|
103
|
+
SDL::Key::RIGHT => :RIGHT,
|
104
|
+
SDL::Key::LEFT => :LEFT,
|
105
|
+
SDL::Key::INSERT => :INSERT,
|
106
|
+
SDL::Key::HOME => :HOME,
|
107
|
+
SDL::Key::END => :END,
|
108
|
+
SDL::Key::PAGEUP => :PAGE_UP,
|
109
|
+
SDL::Key::PAGEDOWN => :PAGE_DOWN,
|
110
|
+
SDL::Key::F1 => :F1,
|
111
|
+
SDL::Key::F2 => :F2,
|
112
|
+
SDL::Key::F3 => :F3,
|
113
|
+
SDL::Key::F4 => :F4,
|
114
|
+
SDL::Key::F5 => :F5,
|
115
|
+
SDL::Key::F6 => :F6,
|
116
|
+
SDL::Key::F7 => :F7,
|
117
|
+
SDL::Key::F8 => :F8,
|
118
|
+
SDL::Key::F9 => :F9,
|
119
|
+
SDL::Key::F10 => :F10,
|
120
|
+
SDL::Key::F11 => :F11,
|
121
|
+
SDL::Key::F12 => :F12,
|
122
|
+
#SDL::Key::F13 => F13, # Who
|
123
|
+
#SDL::Key::F14 => F14, # has
|
124
|
+
#SDL::Key::F15 => F15, # these?
|
125
|
+
SDL::Key::NUMLOCK => :NUM_LOCK,
|
126
|
+
SDL::Key::CAPSLOCK => :CAPS_LOCK,
|
127
|
+
SDL::Key::SCROLLOCK => :SCROLL_LOCK,
|
128
|
+
SDL::Key::RSHIFT => :R_SHIFT,
|
129
|
+
SDL::Key::LSHIFT => :L_SHIFT,
|
130
|
+
SDL::Key::RCTRL => :R_CTRL,
|
131
|
+
SDL::Key::LCTRL => :L_CTRL,
|
132
|
+
SDL::Key::RALT => :R_ALT,
|
133
|
+
SDL::Key::LALT => :L_ALT,
|
134
|
+
#SDL::Key::RMETA => right meta, # 'meta' should
|
135
|
+
#SDL::Key::LMETA => left meta, # be 'alt'?
|
136
|
+
SDL::Key::LSUPER => :L_SUPER,
|
137
|
+
SDL::Key::RSUPER => :R_SUPER,
|
138
|
+
SDL::Key::MODE => :ALT_GR,
|
139
|
+
#SDL::Key::HELP => help, # Rare enough to cause problems.
|
140
|
+
SDL::Key::PRINT => :PRINT_SCREEN,
|
141
|
+
SDL::Key::SYSREQ => :SYS_REQ,
|
142
|
+
SDL::Key::BREAK => :BREAK,
|
143
|
+
SDL::Key::MENU => :MENU,
|
144
|
+
#SDL::Key::POWER => power, # "Power Macintosh" power key
|
145
|
+
SDL::Key::EURO => :EURO, # Some European keyboards need this.
|
146
|
+
}
|
147
|
+
|
148
|
+
# Call a block for each defined Tea key constant.
|
149
|
+
def Event.each_key
|
150
|
+
@@sdl_key_table.each_value { |key_const| yield key_const }
|
151
|
+
end
|
152
|
+
|
153
|
+
protected
|
154
|
+
|
155
|
+
# Convert an SDL keysym to a Tea key constant
|
156
|
+
def sdl_keysym_to_key(sdl_keysym)
|
157
|
+
@@sdl_key_table[sdl_keysym]
|
158
|
+
end
|
159
|
+
|
160
|
+
# Convert SDL key modifier info into an array of active key modifiers.
|
161
|
+
def sdl_keymod_to_mods(sdl_keymod)
|
162
|
+
mods = {}
|
163
|
+
|
164
|
+
mods[:L_SHIFT] = (sdl_keymod & SDL::Key::MOD_LSHIFT) != 0
|
165
|
+
mods[:R_SHIFT] = (sdl_keymod & SDL::Key::MOD_RSHIFT) != 0
|
166
|
+
mods[:SHIFT] = mods[:L_SHIFT] || mods[:R_SHIFT]
|
167
|
+
|
168
|
+
mods[:L_CTRL] = (sdl_keymod & SDL::Key::MOD_LCTRL) != 0
|
169
|
+
mods[:R_CTRL] = (sdl_keymod & SDL::Key::MOD_RCTRL) != 0
|
170
|
+
mods[:CTRL] = mods[:L_CTRL] || mods[:R_CTRL]
|
171
|
+
|
172
|
+
mods[:L_ALT] = (sdl_keymod & SDL::Key::MOD_LALT) != 0
|
173
|
+
mods[:R_ALT] = (sdl_keymod & SDL::Key::MOD_RALT) != 0
|
174
|
+
mods[:ALT] = mods[:L_ALT] || mods[:R_ALT]
|
175
|
+
|
176
|
+
mods[:NUM_LOCK] = (sdl_keymod & SDL::Key::MOD_NUM) != 0
|
177
|
+
mods[:CAPS_LOCK] = (sdl_keymod & SDL::Key::MOD_CAPS) != 0
|
178
|
+
mods[:ALT_GR] = (sdl_keymod & SDL::Key::MOD_MODE) != 0
|
179
|
+
|
180
|
+
mods
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Event generated when a key is pressed down.
|
185
|
+
#
|
186
|
+
# +key+:: Physical key that was pressed, as a symbol (see key reference).
|
187
|
+
# +mods+:: Hash of the active key modifiers. Values are +true+ or
|
188
|
+
# +false+, and the keys can be: +:L_SHIFT+, +:R_SHIFT+,
|
189
|
+
# +:L_CTRL+, +:R_CTRL+, +:L_ALT+, +:R_ALT+, +:NUM_LOCK+,
|
190
|
+
# +:CAPS_LOCK+, +:ALT_GR+. Also, +:SHIFT+, +:CTRL+ and +:ALT+
|
191
|
+
# are provided for convenience, and Tea key constants with the
|
192
|
+
# same names can be used instead.
|
193
|
+
# +char+:: String character generated by that key and those modifiers.
|
194
|
+
# With Ruby >= 1.9, the encoding of this character is UTF-8,
|
195
|
+
# otherwise it varies with the running environment.
|
196
|
+
class Down < Event
|
197
|
+
attr_reader :key, :mods, :char
|
198
|
+
def initialize(sdl_event)
|
199
|
+
@key = sdl_keysym_to_key(sdl_event.sym)
|
200
|
+
@mods = sdl_keymod_to_mods(sdl_event.mod)
|
201
|
+
|
202
|
+
if sdl_event.unicode != 0
|
203
|
+
unicode_field = "%c"
|
204
|
+
# Ruby 1.9 uses UTF-8 Unicode encoding. Otherwise, who knows how
|
205
|
+
# Unicode code points are interpreted?
|
206
|
+
unicode_field = unicode_field.encode('utf-8') if RUBY_1_9
|
207
|
+
@char = unicode_field % sdl_event.unicode
|
208
|
+
else
|
209
|
+
@char = ''
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Event generated when a held-down key is released. +key+ and +mods+ are
|
215
|
+
# the same as in KeyDown.
|
216
|
+
#
|
217
|
+
# +key+:: Physical key that was pressed, as a symbol (see key reference).
|
218
|
+
# +mods+:: Hash of the active key modifiers. Values are +true+ or
|
219
|
+
# +false+, and the keys can be: +:L_SHIFT+, +:R_SHIFT+,
|
220
|
+
# +:L_CTRL+, +:R_CTRL+, +:L_ALT+, +:R_ALT+, +:NUM_LOCK+,
|
221
|
+
# +:CAPS_LOCK+, +:ALT_GR+. Also, +:SHIFT+, +:CTRL+ and +:ALT+
|
222
|
+
# are provided for convenience, and Tea key constants with the
|
223
|
+
# same names can be used instead.
|
224
|
+
class Up < Event
|
225
|
+
attr_reader :key, :mods
|
226
|
+
def initialize(sdl_event)
|
227
|
+
@key = sdl_keysym_to_key(sdl_event.sym)
|
228
|
+
@mods = sdl_keymod_to_mods(sdl_event.mod)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Defaults for Kbd.key_down?, Kbd.mod_active? and Kbd.in_app?.
|
233
|
+
@key_states = {}
|
234
|
+
Event.each_key { |key| @key_states[key] = false }
|
235
|
+
|
236
|
+
@mod_states = { :L_SHIFT => false, :R_SHIFT => false, :SHIFT => false,
|
237
|
+
:L_CTRL => false, :R_CTRL => false, :CTRL => false,
|
238
|
+
:L_ALT => false, :R_ALT => false, :ALT => false,
|
239
|
+
:NUM_LOCK => false,
|
240
|
+
:CAPS_LOCK => false,
|
241
|
+
:ALT_GR => false }
|
242
|
+
|
243
|
+
@in_app = true
|
244
|
+
|
245
|
+
# Returns +true+ if +key+ is being pressed down.
|
246
|
+
def Kbd.key_down?(key)
|
247
|
+
if (down = @key_states[key]) == nil
|
248
|
+
raise Tea::Error, "Can't find key #{key} to check", caller
|
249
|
+
end
|
250
|
+
down
|
251
|
+
end
|
252
|
+
|
253
|
+
# Returns true if the modifier is 'active'. For +:L_SHIFT+, +:R_SHIFT+,
|
254
|
+
# +:L_CTRL+, +:R_CTRL+, +:L_ALT+, and +:ALT_GR+ (and convenience modifier
|
255
|
+
# constants +:SHIFT+, +:CTRL+ and +:ALT+), this means they're being held
|
256
|
+
# down. For +:NUM_LOCK+ and +:CAPS_LOCK+, this means their toggle is on.
|
257
|
+
def Kbd.mod_active?(mod)
|
258
|
+
if (active = @mod_states[mod]) == nil
|
259
|
+
raise Tea::Error, "Can't find modifier #{mod} to check", caller
|
260
|
+
end
|
261
|
+
active
|
262
|
+
end
|
263
|
+
|
264
|
+
# Returns true if the keyboard is focused in the screen window.
|
265
|
+
def Kbd.in_app?
|
266
|
+
@in_app
|
267
|
+
end
|
268
|
+
|
269
|
+
# Update the keyboard state, so that Kbd.key_down? and Kbd.mod_active?
|
270
|
+
# provide fresh data. Called automatically by Event.get.
|
271
|
+
def Kbd.update_state(tea_event)
|
272
|
+
case tea_event
|
273
|
+
when Down, Up
|
274
|
+
@key_states[tea_event.key] = (tea_event.class == Down)
|
275
|
+
@mod_states.merge! tea_event.mods
|
276
|
+
when Lost then @in_app = false
|
277
|
+
when Gained then @in_app = true
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Define Tea key symbols as constants to avoid typo errors.
|
282
|
+
Event.each_key { |key| const_set(key, key) }
|
283
|
+
|
284
|
+
# Extra modifier constants for making modifier detection more consistent.
|
285
|
+
SHIFT = :SHIFT
|
286
|
+
CTRL = :CTRL
|
287
|
+
ALT = :ALT
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
module Event
|
292
|
+
|
293
|
+
# Convert a keyboard-related SDL::Event into a Tea event. For internal use
|
294
|
+
# only.
|
295
|
+
def Event.translate_keyboard_event(sdl_event)
|
296
|
+
out_events = []
|
297
|
+
|
298
|
+
case sdl_event
|
299
|
+
when SDL::Event::KeyDown
|
300
|
+
out_events.push Kbd::Down.new(sdl_event)
|
301
|
+
when SDL::Event::KeyUp
|
302
|
+
out_events.push Kbd::Up.new(sdl_event)
|
303
|
+
end
|
304
|
+
|
305
|
+
out_events
|
306
|
+
end
|
307
|
+
private_class_method :translate_keyboard_event
|
308
|
+
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|