tea 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/COPYING +674 -0
  2. data/COPYING.LESSER +165 -0
  3. data/README.rdoc +93 -0
  4. data/doc/example/bitmap_draw.rb +21 -0
  5. data/doc/example/bitmap_load.rb +14 -0
  6. data/doc/example/bitmap_new.rb +19 -0
  7. data/doc/example/circle.rb +24 -0
  8. data/doc/example/circle_alpha.rb +45 -0
  9. data/doc/example/circle_alpha_bitmap.rb +51 -0
  10. data/doc/example/circle_bitmap.rb +18 -0
  11. data/doc/example/clip.rb +46 -0
  12. data/doc/example/event_app.rb +45 -0
  13. data/doc/example/event_keyboard.rb +43 -0
  14. data/doc/example/event_mouse.rb +85 -0
  15. data/doc/example/font_hello.rb +22 -0
  16. data/doc/example/font_word_wrap.rb +44 -0
  17. data/doc/example/grab.rb +28 -0
  18. data/doc/example/init.rb +10 -0
  19. data/doc/example/lines.rb +49 -0
  20. data/doc/example/lines_aa.rb +44 -0
  21. data/doc/example/lines_alpha.rb +33 -0
  22. data/doc/example/point.rb +26 -0
  23. data/doc/example/rect.rb +15 -0
  24. data/doc/example/rect_alpha.rb +75 -0
  25. data/doc/example/screen_set_mode.rb +18 -0
  26. data/doc/example/screen_update.rb +14 -0
  27. data/doc/example/sfont_hello.rb +22 -0
  28. data/doc/example/smile.png +0 -0
  29. data/doc/example/smile_bounce.rb +44 -0
  30. data/doc/example/smile_move.rb +58 -0
  31. data/doc/example/smile_move_2.rb +78 -0
  32. data/doc/example/sound.rb +101 -0
  33. data/doc/example/state_app.rb +33 -0
  34. data/doc/example/state_keyboard.rb +23 -0
  35. data/doc/example/state_mouse.rb +60 -0
  36. data/doc/key_constants.textile +129 -0
  37. data/doc/key_modifiers.textile +19 -0
  38. data/doc/reference.textile +421 -0
  39. data/lib/tea.rb +34 -0
  40. data/lib/tea/c_bitmap.rb +122 -0
  41. data/lib/tea/c_error.rb +11 -0
  42. data/lib/tea/c_font.rb +302 -0
  43. data/lib/tea/c_sound.rb +144 -0
  44. data/lib/tea/m_color.rb +50 -0
  45. data/lib/tea/m_event.rb +65 -0
  46. data/lib/tea/m_event_app.rb +96 -0
  47. data/lib/tea/m_event_dispatch.rb +54 -0
  48. data/lib/tea/m_event_keyboard.rb +311 -0
  49. data/lib/tea/m_event_mouse.rb +189 -0
  50. data/lib/tea/mix_blitting.rb +31 -0
  51. data/lib/tea/mix_clipping.rb +71 -0
  52. data/lib/tea/mix_grabbing.rb +86 -0
  53. data/lib/tea/mix_image_saving.rb +70 -0
  54. data/lib/tea/mix_primitive.rb +613 -0
  55. data/lib/tea/o_screen.rb +98 -0
  56. metadata +137 -0
@@ -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
@@ -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
@@ -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