├── .dir-locals.el
├── .gitignore
├── COPYING.txt
├── Gemfile
├── README.md
├── Rakefile
├── Steepfile
├── clipboard.c
├── doc
├── DEVELOP.md
└── po
│ └── ja.po
├── event.c
├── extconf.rb
├── filesystem.c
├── gamecontroller.c.m4
├── gl.c.m4
├── hint.c
├── joystick.c.m4
├── key.c.m4
├── lib
├── sdl2.rb
└── sdl2
│ └── version.rb
├── main.c
├── messagebox.c
├── mixer.c.m4
├── mouse.c
├── rbs_collection.yaml
├── rubysdl2_internal.h
├── sample
├── chunk_destroy.rb
├── icon.bmp
├── memory_test
│ ├── m1.rb
│ ├── m2.rb
│ └── m3.rb
├── message_box.rb
├── music_player.rb
├── playwave.rb
├── primitives.rb
├── test_clipboard.rb
├── test_controller.rb
├── test_joystick.rb
├── test_keyboard.rb
├── test_mouse.rb
├── test_surface.rb
├── test_ttf.rb
├── test_video.rb
├── testgl.rb
├── testsprite.rb
├── testspriteminimal.rb
├── timer.rb
├── version.rb
└── video_info.rb
├── timer.c
├── ttf.c.m4
└── video.c.m4
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ((c-mode . ((c-default-style . "k&r")
2 | (c-basic-offset . 4))))
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.bundle
3 | *~
4 | Makefile
5 | *.so
6 | key.c
7 | video.c
8 | gl.c
9 | ttf.c
10 | mixer.c
11 | joystick.c
12 | gamecontroller.c
13 | extconf.h
14 | sdl2.rbi
15 | sdl2.rbs
16 | pkg/
17 | TAGS
18 | local/
19 | mkmf.log
20 | sample/sample.wav
21 | *.gem
22 | .yardoc/
23 | doc/doc-*
24 | doc/po/rubysdl2.pot
25 | extconf-options.txt
26 | .gem_rbs_collection/
27 |
--------------------------------------------------------------------------------
/COPYING.txt:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | group :development do
6 | gem "rake"
7 | gem "yard"
8 | gem "sord" # , path: "../sord"
9 | gem "steep" # , path: "../steep"
10 | end
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ruby/SDL2
2 |
3 | The simple ruby extension library for SDL 2.x.
4 |
5 | # Install
6 | Before installing Ruby/SDL2, you need to install:
7 |
8 | * [devkit](https://rubyinstaller.org/add-ons/devkit.html) (only for windows)
9 | * M4 (only for installing from git repository)
10 | * [SDL 2.x](http://www.libsdl.org/download-2.0.php)
11 | * [SDL_image](https://www.libsdl.org/projects/SDL_image/)
12 | * [SDL_mixer](https://www.libsdl.org/projects/SDL_mixer/)
13 | * [SDL_ttf](https://www.libsdl.org/projects/SDL_ttf/)
14 |
15 | After installing these libraries, you can install Ruby/SDL2 by gem command as follows:
16 |
17 | gem install ruby-sdl2
18 |
19 | Alternatively You can also install the master version of Ruby/SDL2 from github:
20 |
21 | git clone https://github.com/ohai/ruby-sdl2.git
22 | cd ruby-sdl2
23 | rake gem
24 | gem install pkg/ruby-sdl2-x.y.z.gem
25 |
26 | # Document
27 |
28 | * [English Reference manual](http://ohai.github.io/ruby-sdl2/doc-en/)
29 |
30 | # License
31 |
32 | LGPL v3 (see {file:COPYING.txt}).
33 |
34 | # Author
35 |
36 | Ippei Obayashi
37 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'rubygems/package_task'
3 | require 'rake/clean'
4 | require_relative "lib/sdl2/version"
5 |
6 | C_M4_FILES = Dir.glob("*.c.m4")
7 | C_FROM_M4_FILES = C_M4_FILES.map{|path| path.gsub(/\.c\.m4\Z/, ".c") }
8 | C_FILES = Dir.glob("*.c") | C_FROM_M4_FILES
9 | RB_FILES = Dir.glob("lib/**/*.rb")
10 | O_FILES = Rake::FileList[*C_FILES].pathmap('%n.o')
11 |
12 | POT_SOURCES = RB_FILES + ["main.c"] + (C_FILES - ["main.c"])
13 | YARD_DOC_FILES = ["README.md", "COPYING.txt"] + POT_SOURCES
14 | YARD_SOURCES = "-m markdown --main README.md --files COPYING.txt #{POT_SOURCES.join(" ")}"
15 | YARD_CHECKSUM_FILE = ".yardoc/checksums"
16 |
17 | WATCH_TARGETS = (C_FILES - C_FROM_M4_FILES) + C_M4_FILES + ["README.md"]
18 |
19 | CLEAN.include(O_FILES)
20 | CLOBBER.include(*C_FROM_M4_FILES)
21 |
22 | locale = ENV["YARD_LOCALE"]
23 |
24 | def extconf_options
25 | return ENV["RUBYSDL2_EXTCONF_OPTS"] if ENV["RUBYSDL2_EXTCONF_OPTS"]
26 | return ENV["EXTCONF_OPTS"] if ENV["EXTCONF_OPTS"]
27 |
28 | begin
29 | return File.readlines("extconf-options.txt").map(&:chomp).join(" ")
30 | rescue Errno::ENOENT
31 | return ""
32 | end
33 | end
34 |
35 | def gem_spec
36 | Gem::Specification.new do |spec|
37 | spec.name = "ruby-sdl2"
38 | spec.version = SDL2::VERSION
39 | spec.summary = "The simple ruby extension library for SDL 2.x"
40 | spec.description = <<-EOS
41 | Ruby/SDL2 is an extension library to use SDL 2.x
42 | (Simple DirectMedia Layer). SDL 2.x is quite refined
43 | from SDL 1.x and API is changed.
44 | This library enables you to control audio, keyboard,
45 | mouse, joystick, 3D hardware via OpenGL, and 2D video
46 | using opengl or Direct3D.
47 | Ruby/SDL is used for games and visual demos.
48 | EOS
49 | spec.license = "LGPL-3.0"
50 | spec.author = "Ippei Obayashi"
51 | spec.email = "ohai@kmc.gr.jp"
52 | spec.homepage = "https://github.com/ohai/ruby-sdl2"
53 | spec.files = `git ls-files`.split(/\n/) + C_FROM_M4_FILES
54 | spec.test_files = []
55 | spec.extensions = ["extconf.rb"]
56 | end
57 | end
58 |
59 | task "pot" => "doc/po/rubysdl2.pot"
60 | file "doc/po/rubysdl2.pot" => POT_SOURCES do |t|
61 | sh "yard i18n -o #{t.name} #{POT_SOURCES.join(" ")}"
62 | end
63 |
64 | if locale
65 | PO_FILE = "doc/po/#{locale}.po"
66 |
67 | task "init-po" do
68 | sh "rmsginit -i doc/po/rubysdl2.pot -o #{PO_FILE} -l #{locale}"
69 | end
70 |
71 | task "merge-po" do
72 | sh "rmsgmerge -o #{PO_FILE} #{PO_FILE} doc/po/rubysdl2.pot"
73 | end
74 |
75 | task "doc" => [PO_FILE] do
76 | sh "yard doc -o doc/doc-#{locale} --locale #{locale} --po-dir doc/po #{YARD_SOURCES}"
77 | end
78 | else
79 | task "doc" => YARD_DOC_FILES do
80 | sh "yard doc -o doc/doc-en #{YARD_SOURCES}"
81 | end
82 | end
83 |
84 | task "doc-all" do
85 | raise "Not yet implemented"
86 | end
87 |
88 | desc "List undocumented classes/modules/methods/constants"
89 | task "doc-stat-undocumented" => YARD_DOC_FILES do
90 | sh "yard stats --list-undoc --compact #{YARD_SOURCES}"
91 | end
92 |
93 | rule ".c" => ".c.m4" do |t|
94 | sh "m4 #{t.prerequisites[0]} > #{t.name}"
95 | end
96 |
97 | file "Makefile" => "extconf.rb" do
98 | sh "ruby extconf.rb #{extconf_options()}"
99 | end
100 |
101 | task "build" => C_FILES + ["Makefile"] do
102 | sh "make"
103 | end
104 |
105 | Gem::PackageTask.new(gem_spec) do |pkg|
106 | end
107 |
108 | rule ".gem" => C_FILES
109 |
110 | task "watch-doc" do
111 | loop do
112 | sh "inotifywait -e modify #{WATCH_TARGETS.join(" ")} && rake doc && notify-send -u low \"Ruby/SDL2 build doc OK\""
113 | end
114 | end
115 |
116 | file YARD_CHECKSUM_FILE => YARD_DOC_FILES do
117 | sh "yard doc -n #{YARD_SOURCES}"
118 | end
119 |
120 | file "sdl2.rbs" => YARD_CHECKSUM_FILE do
121 | sh "sord --no-regenerate --rbs sdl2.rbs"
122 | end
123 |
124 | file "sdl2.rbi" => YARD_CHECKSUM_FILE do
125 | sh "sord --no-regenerate --rbi sdl2.rbi"
126 | end
127 |
128 | task "clean" do
129 | sh "rm -r .yardoc"
130 | sh "rm -r doc/doc-en"
131 | sh "rm #{C_FROM_M4_FILES.join(' ')}"
132 | end
133 |
--------------------------------------------------------------------------------
/Steepfile:
--------------------------------------------------------------------------------
1 | D = Steep::Diagnostic
2 |
3 | # target :lib do
4 | # signature "sdl2.rbs"
5 | # library "rbs"
6 |
7 | # check "lib"
8 | # end
9 |
10 | target :sample_testspriteminimal do
11 | signature "sample/testspriteminimal.rbs"
12 | signature "sdl2.rbs"
13 | library "rbs"
14 |
15 | check "sample/testspriteminimal.rb"
16 |
17 |
18 | # configure_code_diagnostics(D::Ruby.strict)
19 | # configure_code_diagnostics(D::Ruby.lenient)
20 | configure_code_diagnostics do |hash|
21 | hash[D::Ruby::MethodDefinitionMissing] = :information
22 | hash[D::Ruby::UnknownConstant] = :information
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/clipboard.c:
--------------------------------------------------------------------------------
1 | #include "rubysdl2_internal.h"
2 | #include
3 |
4 | /*
5 | * Document-module: SDL2::Clipboard
6 | *
7 | * This module has clipboard manipulating functions.
8 | *
9 | */
10 |
11 | /*
12 | * Get the text from the clipboard.
13 | *
14 | * @return [String] a string in the clipboard
15 | * @return [nil] if the clipboard is empty
16 | */
17 | static VALUE Clipboard_s_text(VALUE self)
18 | {
19 | if (SDL_HasClipboardText()) {
20 | char* text = SDL_GetClipboardText();
21 | VALUE str;
22 | if (!text)
23 | SDL_ERROR();
24 | str = utf8str_new_cstr(text);
25 | SDL_free(text);
26 | return str;
27 | } else {
28 | return Qnil;
29 | }
30 | }
31 |
32 | /*
33 | * @overload text=(text)
34 | * Set the text in the clipboard.
35 | *
36 | * @param text [String] a new text
37 | * @return [String] text
38 | */
39 | static VALUE Clipboard_s_set_text(VALUE self, VALUE text)
40 | {
41 | HANDLE_ERROR(SDL_SetClipboardText(StringValueCStr(text)));
42 | return text;
43 | }
44 |
45 | /*
46 | * Return true if the clipboard has any text.
47 | *
48 | */
49 | static VALUE Clipboard_s_has_text_p(VALUE self)
50 | {
51 | return INT2BOOL(SDL_HasClipboardText());
52 | }
53 |
54 | void rubysdl2_init_clipboard(void)
55 | {
56 | VALUE mClipBoard = rb_define_module_under(mSDL2, "Clipboard");
57 |
58 | rb_define_module_function(mClipBoard, "text", Clipboard_s_text, 0);
59 | rb_define_module_function(mClipBoard, "text=", Clipboard_s_set_text, 1);
60 | rb_define_module_function(mClipBoard, "has_text?", Clipboard_s_has_text_p, 0);
61 | }
62 |
--------------------------------------------------------------------------------
/doc/DEVELOP.md:
--------------------------------------------------------------------------------
1 | # For Developer
2 |
3 |
4 | ## Check out and build a gem
5 |
6 | git clone https://github.com/ohai/ruby-sdl2.git
7 | cd ruby-sdl2
8 | bundle install
9 | bundle exec rake gem # To build a gem
10 | bundle exec rake doc # To build API documents
11 |
12 | ## Build C extension and run samples
13 |
14 | bundle exec rake build # To build sdl2_ext.so
15 | cd sample
16 | ruby -I .. -I ../lib testspriteminimal.rb
17 |
18 | ## For type checking
19 |
20 | Preparation
21 |
22 | bundle exec rbs collection install
23 |
24 | Generate ruby-sdl2's rbs file
25 |
26 | bundle exec rake sdl2.rbs
27 |
28 | Signature file validation
29 |
30 | bundle exec steep validate
31 |
32 | Type checking with Steep
33 |
34 | bundle exec steep check
35 |
36 |
--------------------------------------------------------------------------------
/extconf.rb:
--------------------------------------------------------------------------------
1 | require 'mkmf'
2 |
3 | def add_cflags(str)
4 | $CFLAGS += " " + str
5 | end
6 |
7 | def add_libs(str)
8 | $LOCAL_LIBS += " " + str
9 | end
10 |
11 | def run_config_program(*args)
12 | IO.popen([*args], "r") do |io|
13 | io.gets.chomp
14 | end
15 | end
16 |
17 | def config(pkg_config, header, libnames)
18 | if system("pkg-config", pkg_config, "--exists")
19 | puts("Use pkg-config #{pkg_config}")
20 | add_cflags(run_config_program("pkg-config", pkg_config, "--cflags"))
21 | add_libs(run_config_program("pkg-config", pkg_config, "--libs"))
22 | else
23 | libnames.each{|libname| break if have_library(libname) }
24 | end
25 |
26 | have_header(header)
27 | end
28 |
29 | def sdl2config_with_command
30 | sdl2_config = with_config('sdl2-config', 'sdl2-config')
31 | add_cflags(run_config_program(sdl2_config, "--cflags"))
32 | add_libs(run_config_program(sdl2_config, "--libs"))
33 | end
34 |
35 | def sdl2config_on_mingw
36 | have_library("mingw32")
37 | have_library("SDL2")
38 | add_libs("-mwindows")
39 | end
40 |
41 | case RbConfig::CONFIG["arch"]
42 | when /mingw/
43 | sdl2config_on_mingw
44 | else
45 | sdl2config_with_command
46 | end
47 |
48 | config("SDL2_image", "SDL_image.h", ["SDL2_image", "SDL_image"])
49 | config("SDL2_mixer", "SDL_mixer.h", ["SDL2_mixer", "SDL_mixer"])
50 | config("SDL2_ttf", "SDL_ttf.h", ["SDL2_ttf", "SDL_ttf"])
51 | have_header("SDL_filesystem.h")
52 |
53 | have_const("MIX_INIT_MODPLUG", "SDL_mixer.h")
54 | have_const("MIX_INIT_FLUIDSYNTH", "SDL_mixer.h")
55 | have_const("MIX_INIT_MID", "SDL_mixer.h")
56 | have_const("SDL_RENDERER_PRESENTVSYNC", "SDL_render.h")
57 | have_const("SDL_WINDOW_ALLOW_HIGHDPI", "SDL_video.h")
58 | have_const("SDL_WINDOW_MOUSE_CAPTURE", "SDL_video.h")
59 |
60 | create_makefile('sdl2_ext')
61 |
--------------------------------------------------------------------------------
/filesystem.c:
--------------------------------------------------------------------------------
1 | #ifdef HAVE_SDL_FILESYSTEM_H
2 | #include "rubysdl2_internal.h"
3 | #include
4 |
5 | /*
6 | * Get the directory where the application was run from.
7 | *
8 | * In almost all cases, using this method, you will get the directory where ruby executable is in.
9 | * Probably what you hope is $0 or __FILE__.
10 | *
11 | * @return [String] an absolute path in UTF-8 encoding to the application data directory
12 | * @raise [SDL2::Error] raised if your platform doesn't implement this functionality.
13 | * @note This method is available since SDL 2.0.1.
14 | */
15 | static VALUE SDL2_s_base_path(VALUE self)
16 | {
17 | char* path = SDL_GetBasePath();
18 | VALUE str;
19 | if (!path)
20 | SDL_ERROR();
21 | str = utf8str_new_cstr(path);
22 | SDL_free(path);
23 | return str;
24 | }
25 |
26 | /*
27 | * @overload preference_path(org, app)
28 | * Get the "pref dir". You can use the directory to write personal files such as
29 | * preferences and save games.
30 | *
31 | * The directory is unique per user and per application.
32 | *
33 | * @param org [String] the name of your organization
34 | * @param app [String] the name of your application
35 | * @return [String] a UTF-8 string of the user directory in platform-depnedent notation.
36 | *
37 | * @raise [SDL2::Error] raised if your platform doesn't implement this functionality.
38 | * @note This method is available since SDL 2.0.1.
39 | *
40 | * @example
41 | * SDL2.preference_path("foo", "bar") # => "/home/ohai/.local/share/foo/bar/" (on Linux)
42 | */
43 | static VALUE SDL2_s_preference_path(VALUE self, VALUE org, VALUE app)
44 | {
45 | char* path = SDL_GetPrefPath(StringValueCStr(org), StringValueCStr(app));
46 | VALUE str;
47 | if (!path)
48 | SDL_ERROR();
49 | str = utf8str_new_cstr(path);
50 | SDL_free(path);
51 | return str;
52 | }
53 |
54 | void rubysdl2_init_filesystem(void)
55 | {
56 | rb_define_module_function(mSDL2, "base_path", SDL2_s_base_path, 0);
57 | rb_define_module_function(mSDL2, "preference_path", SDL2_s_preference_path, 2);
58 | }
59 | #else
60 | void rubysdl2_init_filesystem(void)
61 | {
62 | }
63 | #endif
64 |
--------------------------------------------------------------------------------
/gamecontroller.c.m4:
--------------------------------------------------------------------------------
1 | /* -*- mode: C -*- */
2 | #include "rubysdl2_internal.h"
3 | #include
4 |
5 | static VALUE cGameController;
6 | static VALUE mAxis;
7 | static VALUE mButton;
8 |
9 | typedef struct GameController {
10 | SDL_GameController* controller;
11 | } GameController;
12 |
13 | static void GameController_free(GameController* g)
14 | {
15 | if (rubysdl2_is_active() && g->controller)
16 | SDL_GameControllerClose(g->controller);
17 | free(g);
18 | }
19 |
20 | /*
21 | * Document-class: SDL2::GameController
22 | *
23 | * This class represents a "Game Controller".
24 | *
25 | * In SDL2, there is a gamecontroller framework to
26 | * hide the details of joystick types. This framework
27 | * is built on the existing joystick API.
28 | *
29 | * The framework consists of two parts:
30 | *
31 | * * Mapping joysticks to game controllers
32 | * * Aquire input from game controllers
33 | *
34 | * Mapping information is a string, and described as:
35 | *
36 | * GUID,name,mapping
37 | *
38 | * GUID is a unique ID of a type of joystick and
39 | * given by {Joystick#GUID}. name is the human readable
40 | * string for the device, and mappings are contorller mappings
41 | * to joystick ones.
42 | *
43 | * This mappings abstract the details of joysticks, for exmaple,
44 | * the number of buttons, the number of axes, and the position
45 | * of these buttons a on pad.
46 | * You can use unified interface
47 | * with this framework theoretically.
48 | * Howerver, we need to prepare the
49 | * database of many joysticks that users will use. A database
50 | * is available at https://github.com/gabomdq/SDL_GameControllerDB,
51 | * but perhaps this is not sufficient for your usage.
52 | * In fact, Steam prepares its own database for Steam games,
53 | * so if you will create a game for Steam, this framework is
54 | * useful. Otherwise, it is a better way to use joystick API
55 | * directly.
56 | *
57 | * @!method destroy?
58 | * Return true if the gamecontroller is already closed.
59 | */
60 |
61 | static VALUE GameController_new(SDL_GameController* controller)
62 | {
63 | GameController* g = ALLOC(GameController);
64 | g->controller = controller;
65 | return Data_Wrap_Struct(cGameController, 0, GameController_free, g);
66 | }
67 |
68 | DEFINE_WRAPPER(SDL_GameController, GameController, controller, cGameController,
69 | "SDL2::GameController");
70 |
71 |
72 | /*
73 | * @overload add_mapping(string)
74 | *
75 | * Add suuport for controolers that SDL is unaware of or to cause an
76 | * existing controller to have a different binding.
77 | *
78 | * "GUID,name,mapping", where GUID is
79 | * the string value from SDL_JoystickGetGUIDString(), name is the human
80 | * readable string for the device and mappings are controller mappings to
81 | * joystick ones. Under Windows there is a reserved GUID of "xinput" that
82 | * covers all XInput devices. The mapping format for joystick is:
83 | *
84 | * * bX: a joystick button, index X
85 | * * hX.Y: hat X with value Y
86 | * * aX: axis X of the joystick
87 | *
88 | * Buttons can be used as a controller axes and vice versa.
89 | *
90 | * This string shows an example of a valid mapping for a controller:
91 | * "341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftshoulder:b4,,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7"
92 | *
93 | * @param string [String] mapping string
94 | * @return [Integer] 1 if a new mapping, 0 if an existing mapping is updated.
95 | */
96 | static VALUE GameController_s_add_mapping(VALUE self, VALUE string)
97 | {
98 | int ret = HANDLE_ERROR(SDL_GameControllerAddMapping(StringValueCStr(string)));
99 | return INT2NUM(ret);
100 | }
101 |
102 | /*
103 | * @overload axis_name_of(axis)
104 | *
105 | * Get a string representing **axis**.
106 | *
107 | * @param axis [Integer] axis constant in {Axis}
108 | * @return [String]
109 | */
110 | static VALUE GameController_s_axis_name_of(VALUE self, VALUE axis)
111 | {
112 | const char* name = SDL_GameControllerGetStringForAxis(NUM2INT(axis));
113 | if (!name) {
114 | SDL_SetError("Unknown axis %d", NUM2INT(axis));
115 | SDL_ERROR();
116 | }
117 | return utf8str_new_cstr(name);
118 | }
119 |
120 | /*
121 | * @overload button_name_of(button)
122 | *
123 | * Get a string representing **button**.
124 | *
125 | * @param button [Integer] button constant in {Button}
126 | * @return [String]
127 | */
128 | static VALUE GameController_s_button_name_of(VALUE self, VALUE button)
129 | {
130 | const char* name = SDL_GameControllerGetStringForButton(NUM2INT(button));
131 | if (!name) {
132 | SDL_SetError("Unknown axis %d", NUM2INT(button));
133 | SDL_ERROR();
134 | }
135 | return utf8str_new_cstr(name);
136 | }
137 |
138 | /*
139 | * @overload axis_from_name(name)
140 | *
141 | * Get a integer representation of the axis
142 | * whose string representation is **name**.
143 | *
144 | * The return value is the value of one of the constants in {Axis}
145 | *
146 | * @param name [String] a string representing an axis
147 | * @return [Integer]
148 | */
149 | static VALUE GameController_s_axis_from_name(VALUE self, VALUE name)
150 | {
151 | int axis = SDL_GameControllerGetAxisFromString(StringValueCStr(name));
152 | if (axis < 0) {
153 | SDL_SetError("Unknown axis name \"%s\"", StringValueCStr(name));
154 | SDL_ERROR();
155 | }
156 | return INT2FIX(axis);
157 | }
158 |
159 | /*
160 | * @overload button_from_name(name)
161 | *
162 | * Get a integer representation of the button
163 | * whose string representation is **name**.
164 | *
165 | * The return value is the value of one of the constants in {Button}
166 | *
167 | * @param name [String] a string representing a button
168 | * @return [Integer]
169 | */
170 | static VALUE GameController_s_button_from_name(VALUE self, VALUE name)
171 | {
172 | int button = SDL_GameControllerGetButtonFromString(StringValueCStr(name));
173 | if (button < 0) {
174 | SDL_SetError("Unknown button name \"%s\"", StringValueCStr(name));
175 | SDL_ERROR();
176 | }
177 | return INT2FIX(button);
178 | }
179 |
180 | /*
181 | * Get the implementation dependent name for the game controllers
182 | * connected to the machine.
183 | *
184 | * The number of elements of returning array is same as
185 | * {Joystick.num_connected_joysticks}.
186 | *
187 | * @return [Array]
188 | */
189 | static VALUE GameController_s_device_names(VALUE self)
190 | {
191 | int num_joysticks = SDL_NumJoysticks();
192 | int i;
193 | VALUE device_names = rb_ary_new2(num_joysticks);
194 | for (i=0; icontroller)
267 | SDL_GameControllerClose(g->controller);
268 | g->controller = NULL;
269 | return Qnil;
270 | }
271 |
272 | /*
273 | * Get the mapping string of **self**.
274 | *
275 | * @return [String]
276 | * @see .add_mapping
277 | * @see .mapping_for
278 | */
279 | static VALUE GameController_mapping(VALUE self)
280 | {
281 | return utf8str_new_cstr(SDL_GameControllerMapping(Get_SDL_GameController(self)));
282 | }
283 |
284 | /*
285 | * @overload axis(axis)
286 | * Get the state of an axis control.
287 | *
288 | * The state is an integer from -32768 to 32767.
289 | * The state of trigger never returns negative value (from 0 to 32767).
290 | *
291 | * @param axis [Integer] the index of an axis, one of the constants in {Axis}
292 | * @return [Integer]
293 | */
294 | static VALUE GameController_axis(VALUE self, VALUE axis)
295 | {
296 | return INT2FIX(SDL_GameControllerGetAxis(Get_SDL_GameController(self),
297 | NUM2INT(axis)));
298 | }
299 |
300 | /*
301 | * @overload button_pressed?(button)
302 | * Return true if a button is pressed.
303 | *
304 | * @param button [Integer] the index of a button, one of the constants in {Button}
305 | * @return [Boolean]
306 | */
307 | static VALUE GameController_button_pressed_p(VALUE self, VALUE button)
308 | {
309 | return INT2BOOL(SDL_GameControllerGetButton(Get_SDL_GameController(self),
310 | NUM2INT(button)));
311 | }
312 |
313 | /*
314 | * Document-module: SDL2::GameController::Axis
315 | *
316 | * This module provides constants of gamecontroller's axis indices used by
317 | * {SDL2::GameController} class.
318 | */
319 |
320 | /*
321 | * Document-module: SDL2::GameController::Button
322 | *
323 | * This module provides constants of gamecontroller's button indices used by
324 | * {SDL2::GameController} class.
325 | */
326 | void rubysdl2_init_gamecontorller(void)
327 | {
328 | cGameController = rb_define_class_under(mSDL2, "GameController", rb_cObject);
329 |
330 | rb_define_singleton_method(cGameController, "add_mapping",
331 | GameController_s_add_mapping, 1);
332 | rb_define_singleton_method(cGameController, "device_names",
333 | GameController_s_device_names, 0);
334 | rb_define_singleton_method(cGameController, "axis_name_of",
335 | GameController_s_axis_name_of, 1);
336 | rb_define_singleton_method(cGameController, "button_name_of",
337 | GameController_s_button_name_of, 1);
338 | rb_define_singleton_method(cGameController, "mapping_for",
339 | GameController_s_mapping_for, 1);
340 | rb_define_singleton_method(cGameController, "button_from_name",
341 | GameController_s_button_from_name, 1);
342 | rb_define_singleton_method(cGameController, "axis_from_name",
343 | GameController_s_axis_from_name, 1);
344 | rb_define_singleton_method(cGameController, "open", GameController_s_open, 1);
345 | rb_define_method(cGameController, "destroy?", GameController_destroy_p, 0);
346 | rb_define_method(cGameController, "destroy", GameController_destroy, 0);
347 | rb_define_method(cGameController, "name", GameController_name, 0);
348 | rb_define_method(cGameController, "attached?", GameController_attached_p, 0);
349 | rb_define_method(cGameController, "mapping", GameController_mapping, 0);
350 | rb_define_method(cGameController, "axis", GameController_axis, 1);
351 | rb_define_method(cGameController, "button_pressed?", GameController_button_pressed_p, 1);
352 |
353 | mAxis = rb_define_module_under(cGameController, "Axis");
354 | mButton = rb_define_module_under(cGameController, "Button");
355 |
356 | /* define(`DEFINE_CONTROLLER_AXIS_CONST',`rb_define_const(mAxis, "$1", INT2NUM(SDL_CONTROLLER_AXIS_$1))') */
357 | /* @return [Integer] index for invalid axis*/
358 | DEFINE_CONTROLLER_AXIS_CONST(INVALID);
359 | /* @return [Integer] index for Left X axis */
360 | DEFINE_CONTROLLER_AXIS_CONST(LEFTX);
361 | /* @return [Integer] index for Left Y axis */
362 | DEFINE_CONTROLLER_AXIS_CONST(LEFTY);
363 | /* @return [Integer] index for Right X axis */
364 | DEFINE_CONTROLLER_AXIS_CONST(RIGHTX);
365 | /* @return [Integer] index for Right Y axis */
366 | DEFINE_CONTROLLER_AXIS_CONST(RIGHTY);
367 | /* @return [Integer] index for Left trigger axis */
368 | DEFINE_CONTROLLER_AXIS_CONST(TRIGGERLEFT);
369 | /* @return [Integer] index for Right trigger axis */
370 | DEFINE_CONTROLLER_AXIS_CONST(TRIGGERRIGHT);
371 | /* @return [Integer] the max value of axis indices */
372 | DEFINE_CONTROLLER_AXIS_CONST(MAX);
373 |
374 | /* define(`DEFINE_CONTROLLER_BUTTON_CONST',`rb_define_const(mButton, "$1", INT2NUM(SDL_CONTROLLER_BUTTON_$1))') */
375 | /* @return [Integer] index for Invalid button */
376 | DEFINE_CONTROLLER_BUTTON_CONST(INVALID);
377 | /* @return [Integer] index for Button A */
378 | DEFINE_CONTROLLER_BUTTON_CONST(A);
379 | /* @return [Integer] index for Button B */
380 | DEFINE_CONTROLLER_BUTTON_CONST(B);
381 | /* @return [Integer] index for Button X */
382 | DEFINE_CONTROLLER_BUTTON_CONST(X);
383 | /* @return [Integer] index for Button Y */
384 | DEFINE_CONTROLLER_BUTTON_CONST(Y);
385 | /* @return [Integer] index for Back Button */
386 | DEFINE_CONTROLLER_BUTTON_CONST(BACK);
387 | /* @return [Integer] index for Guide Button */
388 | DEFINE_CONTROLLER_BUTTON_CONST(GUIDE);
389 | /* @return [Integer] index for Start Button */
390 | DEFINE_CONTROLLER_BUTTON_CONST(START);
391 | /* @return [Integer] index for Left stick Button */
392 | DEFINE_CONTROLLER_BUTTON_CONST(LEFTSTICK);
393 | /* @return [Integer] index for Right stick Button */
394 | DEFINE_CONTROLLER_BUTTON_CONST(RIGHTSTICK);
395 | /* @return [Integer] index for Left shoulder Button */
396 | DEFINE_CONTROLLER_BUTTON_CONST(LEFTSHOULDER);
397 | /* @return [Integer] index for Right shoulder Button */
398 | DEFINE_CONTROLLER_BUTTON_CONST(RIGHTSHOULDER);
399 | /* @return [Integer] index for D-pad UP Button */
400 | DEFINE_CONTROLLER_BUTTON_CONST(DPAD_UP);
401 | /* @return [Integer] index for D-pad DOWN Button */
402 | DEFINE_CONTROLLER_BUTTON_CONST(DPAD_DOWN);
403 | /* @return [Integer] index for D-pad LEFT Button */
404 | DEFINE_CONTROLLER_BUTTON_CONST(DPAD_LEFT);
405 | /* @return [Integer] index for D-pad RIGHT Button */
406 | DEFINE_CONTROLLER_BUTTON_CONST(DPAD_RIGHT);
407 | /* @return [Integer] The max value of button indices */
408 | DEFINE_CONTROLLER_BUTTON_CONST(MAX);
409 | }
410 |
--------------------------------------------------------------------------------
/gl.c.m4:
--------------------------------------------------------------------------------
1 | /* -*- mode: C -*- */
2 | #include "rubysdl2_internal.h"
3 | #include
4 | #include
5 |
6 | static VALUE mGL;
7 | static VALUE cGLContext;
8 |
9 | static VALUE current_context = Qnil;
10 |
11 | typedef struct GLContext {
12 | SDL_GLContext context;
13 | } GLContext;
14 |
15 | DEFINE_WRAPPER(SDL_GLContext, GLContext, context, cGLContext, "SDL2::GL::Context");
16 |
17 | static void GLContext_free(GLContext* c)
18 | {
19 | if (c->context)
20 | SDL_GL_DeleteContext(c->context);
21 | free(c);
22 | }
23 |
24 | static VALUE GLContext_new(SDL_GLContext context)
25 | {
26 | GLContext* c = ALLOC(GLContext);
27 | c->context = context;
28 | return Data_Wrap_Struct(cGLContext, 0, GLContext_free, c);
29 | }
30 |
31 | /*
32 | * Document-module: SDL2::GL
33 | *
34 | * This module provides the initialize/shutdown functions of OpenGL.
35 | *
36 | */
37 |
38 | /*
39 | * Document-class: SDL2::GL::Context
40 | *
41 | * This class represents an OpenGL context.
42 | *
43 | * You must create a new OpenGL context before using
44 | * OpenGL functions.
45 | *
46 | * @!method destroy?
47 | * Return true if the context is {#destroy destroyed}.
48 | */
49 |
50 | /*
51 | * @overload create(window)
52 | * Create an OpenGL context for use with an OpenGL window, and make it
53 | * current.
54 | *
55 | * @param window [SDL2::Window] the window associate with a new context
56 | * @return [SDL2::GL::Context]
57 | *
58 | * @see #delete
59 | *
60 | * @example
61 | *
62 | * SDL2.init(SDL2::INIT_EVERYTHING)
63 | * # You need to create a window with `OPENGL' flag
64 | * window = SDL2::Window.create("testgl", 0, 0, WINDOW_W, WINDOW_H,
65 | * SDL2::Window::Flags::OPENGL)
66 | *
67 | * # Create a OpenGL context attached to the window
68 | * context = SDL2::GL::Context.create(window)
69 | *
70 | * # You can use OpenGL functions
71 | * :
72 | *
73 | * # Delete the context after using OpenGL functions
74 | * context.destroy
75 | */
76 | static VALUE GLContext_s_create(VALUE self, VALUE window)
77 | {
78 | SDL_GLContext context = SDL_GL_CreateContext(Get_SDL_Window(window));
79 | if (!context)
80 | SDL_ERROR();
81 |
82 | return (current_context = GLContext_new(context));
83 | }
84 |
85 | /*
86 | * Delete the OpenGL context.
87 | *
88 | * @return [nil]
89 | *
90 | * @see #destroy?
91 | */
92 | static VALUE GLContext_destroy(VALUE self)
93 | {
94 | GLContext* c = Get_GLContext(self);
95 | if (c->context)
96 | SDL_GL_DeleteContext(c->context);
97 | c->context = NULL;
98 | return Qnil;
99 | }
100 |
101 | /*
102 | * @overload make_current(window)
103 | * Set the OpenGL context for rendering into an OpenGL window.
104 | *
105 | * @param window [SDL2::Window] the window to associate with the context
106 | * @return [nil]
107 | */
108 | static VALUE GLContext_make_current(VALUE self, VALUE window)
109 | {
110 | HANDLE_ERROR(SDL_GL_MakeCurrent(Get_SDL_Window(window), Get_SDL_GLContext(self)));
111 | current_context = self;
112 | return Qnil;
113 | }
114 |
115 | /*
116 | * Get the current OpenGL context.
117 | *
118 | * @return [SDL2::GL::Context,nil] the current context, nil if there is no current context
119 | *
120 | * @see #make_current
121 | */
122 | static VALUE GLContext_s_current(VALUE self)
123 | {
124 | return current_context;
125 | }
126 |
127 | /*
128 | * @overload extension_supported?(extension)
129 | * Return true if the current context supports **extension**
130 | *
131 | * @param extension [String] the name of an extension
132 | * @return [Boolean]
133 | * @example
134 | * SDL2::GL.extension_supported?("GL_EXT_framebuffer_blit")
135 | */
136 | static VALUE GL_s_extension_supported_p(VALUE self, VALUE extension)
137 | {
138 | return INT2BOOL(SDL_GL_ExtensionSupported(StringValueCStr(extension)));
139 | }
140 |
141 | /*
142 | * Get the state of swap interval of the current context.
143 | *
144 | * @return [Integer]
145 | * return 0 when vsync is not used,
146 | * return 1 when vsync is used,
147 | * and return -1 when vsync is not supported by the system (OS).
148 | *
149 | */
150 | static VALUE GL_s_swap_interval(VALUE self)
151 | {
152 | return INT2NUM(SDL_GL_GetSwapInterval());
153 | }
154 |
155 |
156 | /*
157 | * @overload swap_interval=(interval)
158 | * Set the state of swap interval of the current context.
159 | *
160 | * @param interval [Integer]
161 | * 0 if you don't want to wait for vsync,
162 | * 1 if you want to wait for vsync,
163 | * -1 if you want to use late swap tearing
164 | * @return [nil]
165 | *
166 | */
167 | static VALUE GL_s_set_swap_interval(VALUE self, VALUE interval)
168 | {
169 | HANDLE_ERROR(SDL_GL_SetSwapInterval(NUM2INT(interval)));
170 | return Qnil;
171 | }
172 |
173 | /*
174 | * @overload get_attribute(attr)
175 | * Get the acutal value for an attribute from current context.
176 | *
177 | * @param attr [Integer] the OpenGL attribute to query
178 | * @return [Integer]
179 | */
180 | static VALUE GL_s_get_attribute(VALUE self, VALUE attr)
181 | {
182 | int value;
183 | HANDLE_ERROR(SDL_GL_GetAttribute(NUM2INT(attr), &value));
184 | return INT2NUM(value);
185 | }
186 |
187 | /*
188 | * @overload set_attribute(attr, value)
189 | * Set an OpenGL window attribute before window creation.
190 | *
191 | * @param attr [Integer] the OpenGL attribute to set
192 | * @param value [Integer] the desired value for the attribute
193 | * @return [void]
194 | */
195 | static VALUE GL_s_set_attribute(VALUE self, VALUE attr, VALUE value)
196 | {
197 | HANDLE_ERROR(SDL_GL_SetAttribute(NUM2INT(attr), NUM2INT(value)));
198 | return value;
199 | }
200 |
201 | void rubysdl2_init_gl(void)
202 | {
203 | mGL = rb_define_module_under(mSDL2, "GL");
204 | cGLContext = rb_define_class_under(mGL, "Context", rb_cObject);
205 |
206 | rb_define_singleton_method(cGLContext, "create", GLContext_s_create, 1);
207 | rb_define_singleton_method(cGLContext, "current", GLContext_s_current, 0);
208 |
209 | rb_define_method(cGLContext, "destroy?", GLContext_destroy_p, 0);
210 | rb_define_method(cGLContext, "destroy", GLContext_destroy, 0);
211 | rb_define_method(cGLContext, "make_current", GLContext_make_current, 1);
212 |
213 | rb_define_module_function(mGL, "extension_supported?", GL_s_extension_supported_p, 1);
214 | rb_define_module_function(mGL, "swap_interval", GL_s_swap_interval, 0);
215 | rb_define_module_function(mGL, "swap_interval=", GL_s_set_swap_interval, 1);
216 | rb_define_module_function(mGL, "get_attribute", GL_s_get_attribute, 1);
217 | rb_define_module_function(mGL, "set_attribute", GL_s_set_attribute, 2);
218 |
219 | /* define(`DEFINE_GL_ATTR_CONST',`rb_define_const(mGL, "$1", INT2NUM(SDL_GL_$1))') */
220 | /* @return [Integer] index for OpenGL attribute - minimal bits of red channel in color buffer, default is 3 */
221 | DEFINE_GL_ATTR_CONST(RED_SIZE);
222 | /* @return [Integer] index for OpenGL attribute - minimal bits of green channel in color buffer, default is 3 */
223 | DEFINE_GL_ATTR_CONST(GREEN_SIZE);
224 | /* @return [Integer] index for OpenGL attribute - minimal bits of blue channel in color buffer, default is 2 */
225 | DEFINE_GL_ATTR_CONST(BLUE_SIZE);
226 | /* @return [Integer] index for OpenGL attribute - minimal bits of alpha channel in color buffer, default is 0 */
227 | DEFINE_GL_ATTR_CONST(ALPHA_SIZE);
228 | /* @return [Integer] index for OpenGL attribute - minimal bits of framebufer, default is 0 */
229 | DEFINE_GL_ATTR_CONST(BUFFER_SIZE);
230 | /* @return [Integer] index for OpenGL attribute - whether the single buffer (0) or double buffer (1), default is double buffer */
231 | DEFINE_GL_ATTR_CONST(DOUBLEBUFFER);
232 | /* @return [Integer] index for OpenGL attribute - bits of depth buffer, default is 16 */
233 | DEFINE_GL_ATTR_CONST(DEPTH_SIZE);
234 | /* @return [Integer] index for OpenGL attribute - bits of stencil buffer, default is 0 */
235 | DEFINE_GL_ATTR_CONST(STENCIL_SIZE);
236 | /* @return [Integer] index for OpenGL attribute - minimal bits of red channel in accumlation buffer, default is 0 */
237 | DEFINE_GL_ATTR_CONST(ACCUM_RED_SIZE);
238 | /* @return [Integer] index for OpenGL attribute - minimal bits of green channel in accumlation buffer, default is 0 */
239 | DEFINE_GL_ATTR_CONST(ACCUM_GREEN_SIZE);
240 | /* @return [Integer] index for OpenGL attribute - minimal bits of blue channel in accumlation buffer, default is 0 */
241 | DEFINE_GL_ATTR_CONST(ACCUM_BLUE_SIZE);
242 | /* @return [Integer] index for OpenGL attribute - minimal bits of alpha channel in accumlation buffer, default is 0 */
243 | DEFINE_GL_ATTR_CONST(ACCUM_ALPHA_SIZE);
244 | /* @return [Integer] index for OpenGL attribute - whether output is stereo (1) or not (0), default is 0 */
245 | DEFINE_GL_ATTR_CONST(STEREO);
246 | /* @return [Integer] index for OpenGL attribuite - the number of buffers used for multisampe anti-aliasing, default is 0 */
247 | DEFINE_GL_ATTR_CONST(MULTISAMPLEBUFFERS);
248 | /* @return [Integer] index for OpenGL attribute - the number of samples used around the current pixel use for multisample anti-aliasing, default is 0 */
249 | DEFINE_GL_ATTR_CONST(MULTISAMPLESAMPLES);
250 | /* @return [Integer] index for OpenGL attribute - 1 for requiring hardware acceleration, 0 for software rendering, default is allowing either */
251 | DEFINE_GL_ATTR_CONST(ACCELERATED_VISUAL);
252 | /* @return [Integer] index for OpenGL attribute - not used (deprecated) */
253 | DEFINE_GL_ATTR_CONST(RETAINED_BACKING);
254 | /* @return [Integer] index for OpenGL attribute - OpenGL context major version */
255 | DEFINE_GL_ATTR_CONST(CONTEXT_MAJOR_VERSION);
256 | /* @return [Integer] index for OpenGL attribute - OpenGL context minor version */
257 | DEFINE_GL_ATTR_CONST(CONTEXT_MINOR_VERSION);
258 | /*
259 | * INT2NUM(SDL_GL_CONTEXT_FLAGS):
260 | *
261 | * @return [Integer] index for OpenGL attribute - the bit combination of following constants, or 0.
262 | * default is 0
263 | *
264 | * * {SDL2::GL::CONTEXT_DEBUG_FLAG}
265 | * * {SDL2::GL::CONTEXT_FORWARD_COMPATIBLE_FLAG}
266 | * * {SDL2::GL::CONTEXT_ROBUST_ACCESS_FLAG}
267 | * * {SDL2::GL::CONTEXT_RESET_ISOLATION_FLAG}
268 | *
269 | * These flags are mapped to some OpenGL extensions. Please see
270 | * the documentation of each constant for more details.
271 | *
272 | * https://wiki.libsdl.org/SDL_GLcontextFlag
273 | */
274 | DEFINE_GL_ATTR_CONST(CONTEXT_FLAGS);
275 | /* INT2NUM(SDL_GL_CONTEXT_PROFILE_MASK):
276 | *
277 | * @return [Integer] index for OpenGL attribute - type of GL context, one of the following constants,
278 | * defaults depends on platform
279 | *
280 | * * {CONTEXT_PROFILE_CORE}
281 | * * {CONTEXT_PROFILE_COMPATIBILITY}
282 | * * {CONTEXT_PROFILE_ES}
283 | *
284 | * https://wiki.libsdl.org/SDL_GLprofile
285 | */
286 | DEFINE_GL_ATTR_CONST(CONTEXT_PROFILE_MASK);
287 | /* @return [Integer] index for OpenGL attribute - OpenGL context sharing, default is 0 */
288 | DEFINE_GL_ATTR_CONST(SHARE_WITH_CURRENT_CONTEXT);
289 | #if SDL_VERSION_ATLEAST(2,0,1)
290 | /* @return [Integer] index for OpenGL attribute - 1 for requesting sRGB capable visual, default to 0 */
291 | DEFINE_GL_ATTR_CONST(FRAMEBUFFER_SRGB_CAPABLE);
292 | #endif
293 | /* @return [Integer] index for OpenGL attribute - not used (deprecated) */
294 | DEFINE_GL_ATTR_CONST(CONTEXT_EGL);
295 |
296 | /* define(`DEFINE_GL_CONTEXT_CONST',`rb_define_const(mGL, "CONTEXT_$1", INT2NUM(SDL_GL_CONTEXT_$1))') */
297 |
298 | /* @return [Integer]
299 | * This flag maps to GLX_CONTEXT_DEBUG_BIT_ARB in
300 | * the GLX_ARB_create_context extension for X11
301 | * and WGL_CONTEXT_DEBUG_BIT_ARB in the WGL_ARB_create_context
302 | * extension for Windows.
303 | */
304 | DEFINE_GL_CONTEXT_CONST(DEBUG_FLAG);
305 | /* @return [Integer]
306 | * This flag maps to GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB in the
307 | * GLX_ARB_create_context extension for X11 and
308 | * WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB in the WGL_ARB_create_context
309 | * extension for Windows.
310 | */
311 | DEFINE_GL_CONTEXT_CONST(FORWARD_COMPATIBLE_FLAG);
312 | /* @return [Integer]
313 | * This flag maps to GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB in the
314 | * GLX_ARB_create_context_robustness extension for X11 and
315 | * WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB in the WGL_ARB_create_context_robustness
316 | * extension for Windows.
317 | */
318 | DEFINE_GL_CONTEXT_CONST(ROBUST_ACCESS_FLAG);
319 | /* @return [Integer]
320 | * This flag maps to GLX_CONTEXT_RESET_ISOLATION_BIT_ARB in the
321 | * GLX_ARB_robustness_isolation extension for X11 and
322 | * WGL_CONTEXT_RESET_ISOLATION_BIT_ARB in the WGL_ARB_create_context_robustness
323 | * extension for Windows.
324 | */
325 | DEFINE_GL_CONTEXT_CONST(RESET_ISOLATION_FLAG);
326 |
327 | /* @return [Integer]
328 | * OpenGL core profile - deprecated
329 | * functions are disabled
330 | */
331 | DEFINE_GL_CONTEXT_CONST(PROFILE_CORE);
332 | /* @return [Integer]
333 | * OpenGL compatibility profile -
334 | * deprecated functions are allowed
335 | */
336 | DEFINE_GL_CONTEXT_CONST(PROFILE_COMPATIBILITY);
337 | /* @return [Integer]
338 | * OpenGL ES profile - only a subset of the
339 | * base OpenGL functionality is available
340 | */
341 | DEFINE_GL_CONTEXT_CONST(PROFILE_ES);
342 |
343 | rb_gc_register_address(¤t_context);
344 | }
345 |
--------------------------------------------------------------------------------
/hint.c:
--------------------------------------------------------------------------------
1 | #include "rubysdl2_internal.h"
2 | #include
3 |
4 | static VALUE sym_priority;
5 |
6 | /*
7 | * Document-module: SDL2::Hints
8 | *
9 | * This module enables you to get and set configuration hints.
10 | *
11 | */
12 |
13 | /*
14 | * Clear all hints set by {.[]=}.
15 | *
16 | * @return [nil]
17 | */
18 | static VALUE Hints_s_clear(VALUE self)
19 | {
20 | SDL_ClearHints();
21 | return Qnil;
22 | }
23 |
24 | /*
25 | * @overload [](hint)
26 | * Get the value of a hint.
27 | *
28 | * @param hint [String] the name of the hint to query
29 | *
30 | * @return [String] the string value of a hint
31 | * @return [nil] if the hint isn't set
32 | */
33 | static VALUE Hints_s_aref(VALUE self, VALUE name)
34 | {
35 | const char* value = SDL_GetHint(StringValueCStr(name));
36 | if (value)
37 | return utf8str_new_cstr(value);
38 | else
39 | return Qnil;
40 | }
41 |
42 | /*
43 | * Set the value of a hint.
44 | *
45 | * @overload []=(hint, value)
46 | * Set a hint with normal priority.
47 | *
48 | * @param hint [String] the name of the hint to query
49 | * @param value [String] the value of the hint varaible
50 | *
51 | * @overload []=(hint, priority: , value)
52 | * Set a hint with given priority.
53 | *
54 | * @param hint [String] the name of the hint to query
55 | * @param priority [Integer] the priority, one of the
56 | * {DEFAULT}, {NORMAL}, or {OVERRIDE}.
57 | * @param value [String] the value of the hint varaible
58 | *
59 | * @return [Boolean] return true if the hint was set
60 | *
61 | * @example
62 | * SDL2::Hints["SDL_HINT_XINPUT_ENABLED", priority: SDL2::Hints::OVERRIDE] = "0"
63 | *
64 | */
65 | static VALUE Hints_s_aset(int argc, VALUE* argv, VALUE self)
66 | {
67 | VALUE name, pri, value;
68 | rb_scan_args(argc, argv, "21", &name, &pri, &value);
69 |
70 | if (argc == 2) {
71 | value = pri;
72 | return INT2BOOL(SDL_SetHint(StringValueCStr(name), StringValueCStr(value)));
73 | } else {
74 | Check_Type(pri, T_HASH);
75 | return INT2BOOL(SDL_SetHintWithPriority(StringValueCStr(name),
76 | StringValueCStr(value),
77 | NUM2INT(rb_hash_aref(pri, sym_priority))));
78 | }
79 |
80 | return Qnil;
81 | }
82 |
83 | void rubysdl2_init_hints(void)
84 | {
85 | VALUE mHints = rb_define_module_under(mSDL2, "Hints");
86 |
87 | rb_define_singleton_method(mHints, "clear", Hints_s_clear, 0);
88 | rb_define_singleton_method(mHints, "get", Hints_s_aref, 1);
89 | rb_define_singleton_method(mHints, "[]=", Hints_s_aset, -1);
90 |
91 | /* @return [Integer] index for low priority, used fro default values */
92 | rb_define_const(mHints, "DEFAULT", INT2NUM(SDL_HINT_DEFAULT));
93 | /* @return [Integer] index for medium priority, overrided by an environment variable */
94 | rb_define_const(mHints, "NORMAL", INT2NUM(SDL_HINT_NORMAL));
95 | /* @return [Integer] index for high priority, this priority overrides the value by environment variables */
96 | rb_define_const(mHints, "OVERRIDE", INT2NUM(SDL_HINT_OVERRIDE));
97 |
98 | sym_priority = ID2SYM(rb_intern("priority"));
99 | }
100 |
--------------------------------------------------------------------------------
/joystick.c.m4:
--------------------------------------------------------------------------------
1 | /* -*- mode: C -*- */
2 | #include "rubysdl2_internal.h"
3 | #include
4 | #include
5 |
6 | static VALUE cJoystick;
7 | static VALUE cDeviceInfo;
8 | static VALUE mHat;
9 |
10 | typedef struct Joystick {
11 | SDL_Joystick* joystick;
12 | } Joystick;
13 |
14 | static void Joystick_free(Joystick* j)
15 | {
16 | if (rubysdl2_is_active() && j->joystick)
17 | SDL_JoystickClose(j->joystick);
18 | free(j);
19 | }
20 |
21 | static VALUE Joystick_new(SDL_Joystick* joystick)
22 | {
23 | Joystick* j = ALLOC(Joystick);
24 | j->joystick = joystick;
25 | return Data_Wrap_Struct(cJoystick, 0, Joystick_free, j);
26 | }
27 |
28 | DEFINE_WRAPPER(SDL_Joystick, Joystick, joystick, cJoystick, "SDL2::Joystick");
29 |
30 | /*
31 | * Document-class: SDL2::Joystick
32 | *
33 | * This class represents a joystick connected to the machine.
34 | *
35 | * In order to use joystick subsystem, {SDL2.init} must have been called
36 | * with the SDL2::INIT_JOYSTICK flag.
37 | *
38 | * @!method destroy?
39 | * Return true if the device is alread closed.
40 | * @see #destroy
41 | */
42 |
43 | /*
44 | * Get the number of connected joysticks.
45 | *
46 | * @return [Integer]
47 | */
48 | static VALUE Joystick_s_num_connected_joysticks(VALUE self)
49 | {
50 | return INT2FIX(HANDLE_ERROR(SDL_NumJoysticks()));
51 | }
52 |
53 | static VALUE GUID_to_String(SDL_JoystickGUID guid)
54 | {
55 | char buf[128];
56 | SDL_JoystickGetGUIDString(guid, buf, sizeof(buf));
57 | return rb_usascii_str_new_cstr(buf);
58 | }
59 |
60 | /*
61 | * Get the information of connected joysticks
62 | *
63 | * @return [Array] information of connected devices
64 | */
65 | static VALUE Joystick_s_devices(VALUE self)
66 | {
67 | int num_joysticks = SDL_NumJoysticks();
68 | int i;
69 | VALUE devices = rb_ary_new2(num_joysticks);
70 | for (i=0; ijoystick)
118 | return Qfalse;
119 | return INT2BOOL(SDL_JoystickGetAttached(j->joystick));
120 | }
121 |
122 | /*
123 | * Get the joystick GUID
124 | *
125 | * @return [String] GUID string
126 | */
127 | static VALUE Joystick_GUID(VALUE self)
128 | {
129 | SDL_JoystickGUID guid;
130 | char buf[128];
131 | guid = SDL_JoystickGetGUID(Get_SDL_Joystick(self));
132 | SDL_JoystickGetGUIDString(guid, buf, sizeof(buf));
133 | return rb_usascii_str_new_cstr(buf);
134 | }
135 |
136 | /*
137 | * Get the index of a joystick
138 | *
139 | * @return [Integer] index
140 | */
141 | static VALUE Joystick_index(VALUE self)
142 | {
143 | return INT2NUM(HANDLE_ERROR(SDL_JoystickInstanceID(Get_SDL_Joystick(self))));
144 | }
145 |
146 | /*
147 | * Close a joystick device.
148 | *
149 | * @return [nil]
150 | * @see #destroy?
151 | */
152 | static VALUE Joystick_destroy(VALUE self)
153 | {
154 | Joystick* j = Get_Joystick(self);
155 | if (j->joystick)
156 | SDL_JoystickClose(j->joystick);
157 | j->joystick = NULL;
158 | return Qnil;
159 | }
160 |
161 | /*
162 | * Get the name of a joystick
163 | *
164 | * @return [String] name
165 | */
166 | static VALUE Joystick_name(VALUE self)
167 | {
168 | return utf8str_new_cstr(SDL_JoystickName(Get_SDL_Joystick(self)));
169 | }
170 |
171 | /*
172 | * Get the number of general axis controls on a joystick.
173 | * @return [Integer]
174 | * @see #axis
175 | */
176 | static VALUE Joystick_num_axes(VALUE self)
177 | {
178 | return INT2FIX(SDL_JoystickNumAxes(Get_SDL_Joystick(self)));
179 | }
180 |
181 | /*
182 | * Get the number of trackball on a joystick
183 | * @return [Integer]
184 | * @see #ball
185 | */
186 | static VALUE Joystick_num_balls(VALUE self)
187 | {
188 | return INT2FIX(SDL_JoystickNumBalls(Get_SDL_Joystick(self)));
189 | }
190 |
191 | /*
192 | * Get the number of button on a joystick
193 | * @return [Integer]
194 | * @see #button
195 | */
196 | static VALUE Joystick_num_buttons(VALUE self)
197 | {
198 | return INT2FIX(SDL_JoystickNumButtons(Get_SDL_Joystick(self)));
199 | }
200 |
201 | /*
202 | * Get the number of POV hats on a joystick
203 | * @return [Integer]
204 | * @see #hat
205 | */
206 | static VALUE Joystick_num_hats(VALUE self)
207 | {
208 | return INT2FIX(SDL_JoystickNumHats(Get_SDL_Joystick(self)));
209 | }
210 |
211 | /*
212 | * @overload axis(which)
213 | * Get the current state of an axis control on a joystick.
214 | *
215 | * @param [Integer] which an index of an axis, started at index 0
216 | * @return [Integer] state value, ranging from -32768 to 32767.
217 | * @see #num_axes
218 | */
219 | static VALUE Joystick_axis(VALUE self, VALUE which)
220 | {
221 | return INT2FIX(SDL_JoystickGetAxis(Get_SDL_Joystick(self), NUM2INT(which)));
222 | }
223 |
224 | /*
225 | * @overload ball(which)
226 | * Get the current state of a trackball on a joystick.
227 | *
228 | * @param [Integer] which an index of a trackball, started at index 0
229 | * @return [Array(Integer,Integer)] dx and dy
230 | * @see #num_balls
231 | */
232 | static VALUE Joystick_ball(VALUE self, VALUE which)
233 | {
234 | int dx, dy;
235 | HANDLE_ERROR(SDL_JoystickGetBall(Get_SDL_Joystick(self), NUM2INT(which), &dx, &dy));
236 | return rb_ary_new3(2, INT2NUM(dx), INT2NUM(dy));
237 | }
238 |
239 | /*
240 | * @overload button(which)
241 | * Get the current state of a button on a joystick.
242 | *
243 | * @param [Integer] which an index of a button, started at index 0
244 | * @return [Boolean] true if the button is pressed
245 | * @see #num_buttons
246 | */
247 | static VALUE Joystick_button(VALUE self, VALUE which)
248 | {
249 | return INT2BOOL(SDL_JoystickGetButton(Get_SDL_Joystick(self), NUM2INT(which)));
250 | }
251 |
252 | /*
253 | * @overload hat(which)
254 | * Get the current state of a POV hat on a joystick.
255 | *
256 | * @param [Integer] which an index of a hat, started at index 0
257 | * @return [Integer] hat state
258 | * @see #num_hats
259 | */
260 | static VALUE Joystick_hat(VALUE self, VALUE which)
261 | {
262 | return UINT2NUM(SDL_JoystickGetHat(Get_SDL_Joystick(self), NUM2INT(which)));
263 | }
264 |
265 | /*
266 | * Document-class: SDL2::Joystick::DeviceInfo
267 | *
268 | * This class represents joystick device information, its name and GUID.
269 | *
270 | * You can get the information with {SDL2::Joystick.devices}.
271 | */
272 |
273 | /*
274 | * Document-module: SDL2::Joystick::Hat
275 | *
276 | * This module provides constants of joysticks's hat positions used by {SDL2::Joystick} class.
277 | * The position of the hat is represented by OR'd bits of {RIGHT}, {LEFT}, {UP}, and {DOWN}.
278 | * This means the center position ({CENTERED}) is represeted by 0 and
279 | * the left up position {LEFTUP} is represeted by ({LEFT}|{UP}).
280 | */
281 |
282 | void rubysdl2_init_joystick(void)
283 | {
284 | cJoystick = rb_define_class_under(mSDL2, "Joystick", rb_cObject);
285 | cDeviceInfo = rb_define_class_under(cJoystick, "DeviceInfo", rb_cObject);
286 |
287 | rb_define_singleton_method(cJoystick, "num_connected_joysticks",
288 | Joystick_s_num_connected_joysticks, 0);
289 | rb_define_singleton_method(cJoystick, "devices", Joystick_s_devices, 0);
290 | rb_define_singleton_method(cJoystick, "open", Joystick_s_open, 1);
291 | rb_define_singleton_method(cJoystick, "game_controller?",
292 | Joystick_s_game_controller_p, 1);
293 | rb_define_method(cJoystick, "destroy?", Joystick_destroy_p, 0);
294 | rb_define_alias(cJoystick, "close?", "destroy?");
295 | rb_define_method(cJoystick, "attached?", Joystick_attached_p, 0);
296 | rb_define_method(cJoystick, "GUID", Joystick_GUID, 0);
297 | rb_define_method(cJoystick, "index", Joystick_index, 0);
298 | rb_define_method(cJoystick, "destroy", Joystick_destroy, 0);
299 | rb_define_alias(cJoystick, "close", "destroy");
300 | rb_define_method(cJoystick, "name", Joystick_name, 0);
301 | rb_define_method(cJoystick, "num_axes", Joystick_num_axes, 0);
302 | rb_define_method(cJoystick, "num_balls", Joystick_num_balls, 0);
303 | rb_define_method(cJoystick, "num_buttons", Joystick_num_buttons, 0);
304 | rb_define_method(cJoystick, "num_hats", Joystick_num_hats, 0);
305 | rb_define_method(cJoystick, "axis", Joystick_axis, 1);
306 | rb_define_method(cJoystick, "ball", Joystick_ball, 1);
307 | rb_define_method(cJoystick, "button", Joystick_button, 1);
308 | rb_define_method(cJoystick, "hat", Joystick_hat, 1);
309 |
310 | mHat = rb_define_module_under(cJoystick, "Hat");
311 |
312 | /* define(`DEFINE_JOY_HAT_CONST',`rb_define_const(mHat, "$1", INT2NUM(SDL_HAT_$1))') */
313 | /* @return [Integer] hat state\: Center position. Equal to 0. */
314 | DEFINE_JOY_HAT_CONST(CENTERED);
315 | /* @return [Integer] hat state\: Up position. */
316 | DEFINE_JOY_HAT_CONST(UP);
317 | /* @return [Integer] hat state\: Right position. */
318 | DEFINE_JOY_HAT_CONST(RIGHT);
319 | /* @return [Integer] hat state\: Down position. */
320 | DEFINE_JOY_HAT_CONST(DOWN);
321 | /* @return [Integer] hat state\: Left position. */
322 | DEFINE_JOY_HAT_CONST(LEFT);
323 | /* @return [Integer] hat state\: Right Up position. Equal to ({RIGHT} | {UP}) */
324 | DEFINE_JOY_HAT_CONST(RIGHTUP);
325 | /* @return [Integer] hat state\: Right Down position. Equal to ({RIGHT} | {DOWN}) */
326 | DEFINE_JOY_HAT_CONST(RIGHTDOWN);
327 | /* @return [Integer] hat state\: Left Up position. Equal to ({LEFT} | {UP}) */
328 | DEFINE_JOY_HAT_CONST(LEFTUP);
329 | /* @return [Integer] hat state\: Left Down position. Equal to ({LEFT} | {DOWN}) */
330 | DEFINE_JOY_HAT_CONST(LEFTDOWN);
331 |
332 | /* Device GUID
333 | * @return [String] */
334 | rb_define_attr(cDeviceInfo, "GUID", 1, 0);
335 | /* Device name
336 | * @return [String] */
337 | rb_define_attr(cDeviceInfo, "name", 1, 0);
338 |
339 |
340 | }
341 |
--------------------------------------------------------------------------------
/key.c.m4:
--------------------------------------------------------------------------------
1 | /* -*- C -*- */
2 | #include "rubysdl2_internal.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | static VALUE mKey;
9 | static VALUE mScan;
10 | static VALUE mMod;
11 | static VALUE mTextInput;
12 |
13 | /*
14 | * Document-module: SDL2::Key
15 | *
16 | * This module has "virtual" keycode constants and some
17 | * keyboard input handling functions.
18 | *
19 | */
20 |
21 | /*
22 | * @overload name_of(code)
23 | * @param [Integer] code keycode
24 | *
25 | * Get a human-readable name for a key
26 | *
27 | * @return [String] the name of given keycode
28 | * @see .keycode_from_name
29 | * @see SDL2::Key::Scan.name_of
30 | */
31 | static VALUE Key_s_name_of(VALUE self, VALUE code)
32 | {
33 | return utf8str_new_cstr(SDL_GetKeyName(NUM2INT(code)));
34 | }
35 |
36 | /*
37 | * @overload keycode_from_name(name)
38 | * @param [String] name name of a key
39 | *
40 | * Get a key code from given name
41 | * @return [Integer] keycode
42 | * @see .name_of
43 | * @see SDL2::Key::Scan.from_name
44 | */
45 | static VALUE Key_s_keycode_from_name(VALUE self, VALUE name)
46 | {
47 | return INT2NUM(SDL_GetKeyFromName(StringValueCStr(name)));
48 | }
49 |
50 | /*
51 | * @overload keycode_from_scancode(scancode)
52 | * @param [Integer] scancode scancode
53 | *
54 | * Convert a scancode to the corresponding keycode
55 | *
56 | * @return [Integer]
57 | * @see SDL2::Key::Scan.from_keycode
58 | */
59 | static VALUE Key_s_keycode_from_scancode(VALUE self, VALUE scancode)
60 | {
61 | return INT2NUM(SDL_GetKeyFromScancode(NUM2INT(scancode)));
62 | }
63 |
64 | /*
65 | * @overload pressed?(code)
66 | * @param [Integer] code scancode
67 | *
68 | * @return [Boolean] true if pressed
69 | *
70 | * Get whether the key of the given scancode is pressed or not.
71 | */
72 | static VALUE Key_s_pressed_p(VALUE self, VALUE code)
73 | {
74 | const Uint8* state = SDL_GetKeyboardState(NULL);
75 | SDL_Scancode scancode;
76 | if (!state) {
77 | SDL_PumpEvents();
78 | state = SDL_GetKeyboardState(NULL);
79 | if (!state)
80 | rb_raise(eSDL2Error, "Event subsystem is not initialized");
81 | }
82 | scancode = NUM2UINT(code);
83 | if (scancode >= SDL_NUM_SCANCODES)
84 | rb_raise(rb_eArgError, "too large scancode %d", scancode);
85 |
86 | return INT2BOOL(state[scancode]);
87 | }
88 |
89 | /*
90 | * Document-module: SDL2::Key::Scan
91 | *
92 | * This module has "physical" key scancode constants and some
93 | * scancode handling functions.
94 | */
95 |
96 | /*
97 | * @overload name_of(code)
98 | * @param [Integer] code scancode
99 | *
100 | * Get a human-readable name of the given scancode
101 | * @return [String]
102 | * @see SDL2::Key.name_of
103 | * @see .from_name
104 | */
105 | static VALUE Scan_s_name_of(VALUE self, VALUE code)
106 | {
107 | return utf8str_new_cstr(SDL_GetScancodeName(NUM2INT(code)));
108 | }
109 |
110 | /*
111 | * @overload from_name(name)
112 | * @param [String] name name of a key
113 | *
114 | * Get a scancode from the key of the given name
115 | * @return [String]
116 | * @see .name_of
117 | * @see SDL2::Key.keycode_from_name
118 | */
119 | static VALUE Scan_s_from_name(VALUE self, VALUE name)
120 | {
121 | return INT2NUM(SDL_GetScancodeFromName(StringValueCStr(name)));
122 | }
123 |
124 | /*
125 | * @overload from_keycode(keycode)
126 | * @param [Integer] keycode keycode
127 | *
128 | * Get a keycode corresponding to the given keycode
129 | * @return [Integer] keycode
130 | * @see SDL2::Key.keycode_from_scancode
131 | */
132 | static VALUE Scan_s_from_keycode(VALUE self, VALUE keycode)
133 | {
134 | return INT2NUM(SDL_GetScancodeFromKey(NUM2INT(keycode)));
135 | }
136 |
137 | /*
138 | * Document-module: SDL2::Key::Mod
139 | *
140 | * This module has key modifier bitmask constants and
141 | * some functions to handle key modifier states.
142 | */
143 |
144 | /*
145 | * Get the current key modifier state
146 | *
147 | * You can examine whether the modifier key is pressed
148 | * using bitmask constants of {SDL2::Key::Mod}.
149 | *
150 | * @return [Integer] key state
151 | */
152 | static VALUE Mod_s_state(VALUE self)
153 | {
154 | return UINT2NUM(SDL_GetModState());
155 | }
156 |
157 | /*
158 | * @overload state=(keymod)
159 | * @param [Integer] keymod key modifier flags (bits)
160 | *
161 | * Set the current key modifier state
162 | *
163 | * @note This does not change the keyboard state, only the key modifier flags.
164 | * @return [void]
165 | * @see .state
166 | */
167 | static VALUE Mod_s_set_state(VALUE self, VALUE keymod)
168 | {
169 | SDL_SetModState(NUM2UINT(keymod));
170 | return Qnil;
171 | }
172 |
173 | /*
174 | * Document-module: SDL2::TextInput
175 | *
176 | * This module provides Unicode text input support.
177 | *
178 | * Normally, you can handle key inputs from key events
179 | * and {SDL2::Key} module. This module is required to
180 | * input thousands kinds of symbols like CJK languages.
181 | * Please see {https://wiki.libsdl.org/Tutorials/TextInput}
182 | * to understand the concept of Unicode text input.
183 | */
184 |
185 | /*
186 | * Return true if Unicode text input events are enabled.
187 | *
188 | * @see .start
189 | * @see .stop
190 | */
191 | static VALUE TextInput_s_active_p(VALUE self)
192 | {
193 | return INT2BOOL(SDL_IsTextInputActive());
194 | }
195 |
196 | /*
197 | * Enable Unicode input events.
198 | *
199 | * @return [nil]
200 | * @see .stop
201 | * @see .active?
202 | */
203 | static VALUE TextInput_s_start(VALUE self)
204 | {
205 | SDL_StartTextInput(); return Qnil;
206 | }
207 |
208 | /*
209 | * Disable Unicode input events.
210 | *
211 | * @return [nil]
212 | * @see .start
213 | * @see .active?
214 | */
215 | static VALUE TextInput_s_stop(VALUE self)
216 | {
217 | SDL_StopTextInput(); return Qnil;
218 | }
219 |
220 | /*
221 | * @overload rect=(rect)
222 | * Set the rectanlgle used to type Unicode text inputs.
223 | *
224 | * @param rect [SDL2::Rect] the rectangle to receive text
225 | * @return [void]
226 | */
227 | static VALUE TextInput_s_set_rect(VALUE self, VALUE rect)
228 | {
229 | SDL_Rect *r = Get_SDL_Rect(rect);
230 | SDL_SetTextInputRect(r);
231 | return rect;
232 | }
233 |
234 | /*
235 | define(`DEFINE_SCANCODE',`ifelse(`$#',`2',`$2
236 | ',`/$8* @return [Integer] scancode for "$1" key *$8/
237 | ')rb_define_const(mScan, "$1", INT2NUM(SDL_SCANCODE_$1))')
238 |
239 | define(`DEFINE_SCANCODE_NUMBER',`/$8* @return [Integer] scancode for number key "$1" (not on keypad) *$8/
240 | rb_define_const(mScan, "K$1", INT2NUM(SDL_SCANCODE_$1))')
241 |
242 | define(`DEFINE_SCANCODE_ALPH',`/$8* @return [Integer] scancode for alphabet key "$1" *$8/
243 | rb_define_const(mScan, "$1", INT2NUM(SDL_SCANCODE_$1))')
244 |
245 | define(`DEFINE_KEYCODE', `ifelse(`$#',`2',`$2
246 | ',`/$8* @return [Integer] keycode for "$1" key *$8/
247 | ')rb_define_const(mKey, "$1", INT2NUM(SDLK_$1))')
248 |
249 | define(`DEFINE_KEYCODE_NUMBER',`/$8* @return [Integer] keycode for number key "$1" (not on keypad) *$8/
250 | rb_define_const(mKey, "K$1", INT2NUM(SDLK_$1))')
251 |
252 | define(`DEFINE_KEYCODE_ALPH',`/$8* @return [Integer] keycode for alphabet key "$1" *$8/
253 | rb_define_const(mKey, "translit($1,`a-z',`A-Z')", INT2NUM(SDLK_$1))')
254 |
255 | define(`DEFINE_KEYMOD',`rb_define_const(mMod, "$1", INT2NUM(KMOD_$1))')
256 | */
257 | void rubysdl2_init_key(void)
258 | {
259 | mKey = rb_define_module_under(mSDL2, "Key");
260 | mScan = rb_define_module_under(mKey, "Scan");
261 | mMod = rb_define_module_under(mKey, "Mod");
262 | mTextInput = rb_define_module_under(mSDL2, "TextInput");
263 |
264 | rb_define_module_function(mKey, "name_of", Key_s_name_of, 1);
265 | rb_define_module_function(mKey, "keycode_from_name", Key_s_keycode_from_name, 1);
266 | rb_define_module_function(mKey, "keycode_from_scancode", Key_s_keycode_from_scancode, 1);
267 | rb_define_module_function(mKey, "pressed?", Key_s_pressed_p, 1);
268 | rb_define_module_function(mScan, "name_of", Scan_s_name_of, 1);
269 | rb_define_module_function(mScan, "from_name", Scan_s_from_name, 1);
270 | rb_define_module_function(mScan, "from_keycode", Scan_s_from_keycode, 1);
271 | rb_define_module_function(mMod, "state", Mod_s_state, 0);
272 | rb_define_module_function(mMod, "state=", Mod_s_set_state, 1);
273 | rb_define_module_function(mTextInput, "active?", TextInput_s_active_p, 0);
274 | rb_define_module_function(mTextInput, "start", TextInput_s_start, 0);
275 | rb_define_module_function(mTextInput, "stop", TextInput_s_stop, 0);
276 | rb_define_module_function(mTextInput, "rect=", TextInput_s_set_rect, 1);
277 |
278 | DEFINE_SCANCODE(UNKNOWN,/* @return [Integer] unused scancode */);
279 | DEFINE_SCANCODE_ALPH(A);
280 | DEFINE_SCANCODE_ALPH(B);
281 | DEFINE_SCANCODE_ALPH(C);
282 | DEFINE_SCANCODE_ALPH(D);
283 | DEFINE_SCANCODE_ALPH(E);
284 | DEFINE_SCANCODE_ALPH(F);
285 | DEFINE_SCANCODE_ALPH(G);
286 | DEFINE_SCANCODE_ALPH(H);
287 | DEFINE_SCANCODE_ALPH(I);
288 | DEFINE_SCANCODE_ALPH(J);
289 | DEFINE_SCANCODE_ALPH(K);
290 | DEFINE_SCANCODE_ALPH(L);
291 | DEFINE_SCANCODE_ALPH(M);
292 | DEFINE_SCANCODE_ALPH(N);
293 | DEFINE_SCANCODE_ALPH(O);
294 | DEFINE_SCANCODE_ALPH(P);
295 | DEFINE_SCANCODE_ALPH(Q);
296 | DEFINE_SCANCODE_ALPH(R);
297 | DEFINE_SCANCODE_ALPH(S);
298 | DEFINE_SCANCODE_ALPH(T);
299 | DEFINE_SCANCODE_ALPH(U);
300 | DEFINE_SCANCODE_ALPH(V);
301 | DEFINE_SCANCODE_ALPH(W);
302 | DEFINE_SCANCODE_ALPH(X);
303 | DEFINE_SCANCODE_ALPH(Y);
304 | DEFINE_SCANCODE_ALPH(Z);
305 |
306 | DEFINE_SCANCODE_NUMBER(1);
307 | DEFINE_SCANCODE_NUMBER(2);
308 | DEFINE_SCANCODE_NUMBER(3);
309 | DEFINE_SCANCODE_NUMBER(4);
310 | DEFINE_SCANCODE_NUMBER(5);
311 | DEFINE_SCANCODE_NUMBER(6);
312 | DEFINE_SCANCODE_NUMBER(7);
313 | DEFINE_SCANCODE_NUMBER(8);
314 | DEFINE_SCANCODE_NUMBER(9);
315 | DEFINE_SCANCODE_NUMBER(0);
316 |
317 | DEFINE_SCANCODE(RETURN);
318 | DEFINE_SCANCODE(ESCAPE);
319 | DEFINE_SCANCODE(BACKSPACE);
320 | DEFINE_SCANCODE(TAB);
321 | DEFINE_SCANCODE(SPACE);
322 |
323 | DEFINE_SCANCODE(MINUS);
324 | DEFINE_SCANCODE(EQUALS);
325 | DEFINE_SCANCODE(LEFTBRACKET);
326 | DEFINE_SCANCODE(RIGHTBRACKET);
327 | DEFINE_SCANCODE(BACKSLASH);
328 |
329 | DEFINE_SCANCODE(NONUSHASH);
330 | DEFINE_SCANCODE(SEMICOLON);
331 | DEFINE_SCANCODE(APOSTROPHE);
332 | DEFINE_SCANCODE(GRAVE);
333 | DEFINE_SCANCODE(COMMA);
334 | DEFINE_SCANCODE(PERIOD);
335 | DEFINE_SCANCODE(SLASH);
336 |
337 | DEFINE_SCANCODE(CAPSLOCK);
338 |
339 | DEFINE_SCANCODE(F1);
340 | DEFINE_SCANCODE(F2);
341 | DEFINE_SCANCODE(F3);
342 | DEFINE_SCANCODE(F4);
343 | DEFINE_SCANCODE(F5);
344 | DEFINE_SCANCODE(F6);
345 | DEFINE_SCANCODE(F7);
346 | DEFINE_SCANCODE(F8);
347 | DEFINE_SCANCODE(F9);
348 | DEFINE_SCANCODE(F10);
349 | DEFINE_SCANCODE(F11);
350 | DEFINE_SCANCODE(F12);
351 |
352 | DEFINE_SCANCODE(PRINTSCREEN);
353 | DEFINE_SCANCODE(SCROLLLOCK);
354 | DEFINE_SCANCODE(PAUSE);
355 | DEFINE_SCANCODE(INSERT);
356 |
357 | DEFINE_SCANCODE(HOME);
358 | DEFINE_SCANCODE(PAGEUP);
359 | DEFINE_SCANCODE(DELETE);
360 | DEFINE_SCANCODE(END);
361 | DEFINE_SCANCODE(PAGEDOWN);
362 | DEFINE_SCANCODE(RIGHT);
363 | DEFINE_SCANCODE(LEFT);
364 | DEFINE_SCANCODE(DOWN);
365 | DEFINE_SCANCODE(UP);
366 |
367 | DEFINE_SCANCODE(NUMLOCKCLEAR);
368 |
369 | DEFINE_SCANCODE(KP_DIVIDE);
370 | DEFINE_SCANCODE(KP_MULTIPLY);
371 | DEFINE_SCANCODE(KP_MINUS);
372 | DEFINE_SCANCODE(KP_PLUS);
373 | DEFINE_SCANCODE(KP_ENTER);
374 | DEFINE_SCANCODE(KP_1);
375 | DEFINE_SCANCODE(KP_2);
376 | DEFINE_SCANCODE(KP_3);
377 | DEFINE_SCANCODE(KP_4);
378 | DEFINE_SCANCODE(KP_5);
379 | DEFINE_SCANCODE(KP_6);
380 | DEFINE_SCANCODE(KP_7);
381 | DEFINE_SCANCODE(KP_8);
382 | DEFINE_SCANCODE(KP_9);
383 | DEFINE_SCANCODE(KP_0);
384 | DEFINE_SCANCODE(KP_PERIOD);
385 |
386 | DEFINE_SCANCODE(NONUSBACKSLASH);
387 | DEFINE_SCANCODE(APPLICATION);
388 | DEFINE_SCANCODE(POWER);
389 | DEFINE_SCANCODE(KP_EQUALS);
390 | DEFINE_SCANCODE(F13);
391 | DEFINE_SCANCODE(F14);
392 | DEFINE_SCANCODE(F15);
393 | DEFINE_SCANCODE(F16);
394 | DEFINE_SCANCODE(F17);
395 | DEFINE_SCANCODE(F18);
396 | DEFINE_SCANCODE(F19);
397 | DEFINE_SCANCODE(F20);
398 | DEFINE_SCANCODE(F21);
399 | DEFINE_SCANCODE(F22);
400 | DEFINE_SCANCODE(F23);
401 | DEFINE_SCANCODE(F24);
402 | DEFINE_SCANCODE(EXECUTE);
403 | DEFINE_SCANCODE(HELP);
404 | DEFINE_SCANCODE(MENU);
405 | DEFINE_SCANCODE(SELECT);
406 | DEFINE_SCANCODE(STOP);
407 | DEFINE_SCANCODE(AGAIN);
408 | DEFINE_SCANCODE(UNDO);
409 | DEFINE_SCANCODE(CUT);
410 | DEFINE_SCANCODE(COPY);
411 | DEFINE_SCANCODE(PASTE);
412 | DEFINE_SCANCODE(FIND);
413 | DEFINE_SCANCODE(MUTE);
414 | DEFINE_SCANCODE(VOLUMEUP);
415 | DEFINE_SCANCODE(VOLUMEDOWN);
416 | /* not sure whether there's a reason to enable these */
417 | /* SDL_SCANCODE_LOCKINGCAPSLOCK = 130, */
418 | /* SDL_SCANCODE_LOCKINGNUMLOCK = 131, */
419 | /* SDL_SCANCODE_LOCKINGSCROLLLOCK = 132, */
420 | DEFINE_SCANCODE(KP_COMMA);
421 | DEFINE_SCANCODE(KP_EQUALSAS400);
422 |
423 | DEFINE_SCANCODE(INTERNATIONAL1);
424 |
425 | DEFINE_SCANCODE(INTERNATIONAL2);
426 | DEFINE_SCANCODE(INTERNATIONAL3);
427 | DEFINE_SCANCODE(INTERNATIONAL4);
428 | DEFINE_SCANCODE(INTERNATIONAL5);
429 | DEFINE_SCANCODE(INTERNATIONAL6);
430 | DEFINE_SCANCODE(INTERNATIONAL7);
431 | DEFINE_SCANCODE(INTERNATIONAL8);
432 | DEFINE_SCANCODE(INTERNATIONAL9);
433 | DEFINE_SCANCODE(LANG1);
434 | DEFINE_SCANCODE(LANG2);
435 | DEFINE_SCANCODE(LANG3);
436 | DEFINE_SCANCODE(LANG4);
437 | DEFINE_SCANCODE(LANG5);
438 | DEFINE_SCANCODE(LANG6);
439 | DEFINE_SCANCODE(LANG7);
440 | DEFINE_SCANCODE(LANG8);
441 | DEFINE_SCANCODE(LANG9);
442 |
443 | DEFINE_SCANCODE(ALTERASE);
444 | DEFINE_SCANCODE(SYSREQ);
445 | DEFINE_SCANCODE(CANCEL);
446 | DEFINE_SCANCODE(CLEAR);
447 | DEFINE_SCANCODE(PRIOR);
448 | DEFINE_SCANCODE(RETURN2);
449 | DEFINE_SCANCODE(SEPARATOR);
450 | DEFINE_SCANCODE(OUT);
451 | DEFINE_SCANCODE(OPER);
452 | DEFINE_SCANCODE(CLEARAGAIN);
453 | DEFINE_SCANCODE(CRSEL);
454 | DEFINE_SCANCODE(EXSEL);
455 |
456 | DEFINE_SCANCODE(KP_00);
457 | DEFINE_SCANCODE(KP_000);
458 | DEFINE_SCANCODE(THOUSANDSSEPARATOR);
459 | DEFINE_SCANCODE(DECIMALSEPARATOR);
460 | DEFINE_SCANCODE(CURRENCYUNIT);
461 | DEFINE_SCANCODE(CURRENCYSUBUNIT);
462 | DEFINE_SCANCODE(KP_LEFTPAREN);
463 | DEFINE_SCANCODE(KP_RIGHTPAREN);
464 | DEFINE_SCANCODE(KP_LEFTBRACE);
465 | DEFINE_SCANCODE(KP_RIGHTBRACE);
466 | DEFINE_SCANCODE(KP_TAB);
467 | DEFINE_SCANCODE(KP_BACKSPACE);
468 | DEFINE_SCANCODE(KP_A);
469 | DEFINE_SCANCODE(KP_B);
470 | DEFINE_SCANCODE(KP_C);
471 | DEFINE_SCANCODE(KP_D);
472 | DEFINE_SCANCODE(KP_E);
473 | DEFINE_SCANCODE(KP_F);
474 | DEFINE_SCANCODE(KP_XOR);
475 | DEFINE_SCANCODE(KP_POWER);
476 | DEFINE_SCANCODE(KP_PERCENT);
477 | DEFINE_SCANCODE(KP_LESS);
478 | DEFINE_SCANCODE(KP_GREATER);
479 | DEFINE_SCANCODE(KP_AMPERSAND);
480 | DEFINE_SCANCODE(KP_DBLAMPERSAND);
481 | DEFINE_SCANCODE(KP_VERTICALBAR);
482 | DEFINE_SCANCODE(KP_DBLVERTICALBAR);
483 | DEFINE_SCANCODE(KP_COLON);
484 | DEFINE_SCANCODE(KP_HASH);
485 | DEFINE_SCANCODE(KP_SPACE);
486 | DEFINE_SCANCODE(KP_AT);
487 | DEFINE_SCANCODE(KP_EXCLAM);
488 | DEFINE_SCANCODE(KP_MEMSTORE);
489 | DEFINE_SCANCODE(KP_MEMRECALL);
490 | DEFINE_SCANCODE(KP_MEMCLEAR);
491 | DEFINE_SCANCODE(KP_MEMADD);
492 | DEFINE_SCANCODE(KP_MEMSUBTRACT);
493 | DEFINE_SCANCODE(KP_MEMMULTIPLY);
494 | DEFINE_SCANCODE(KP_MEMDIVIDE);
495 | DEFINE_SCANCODE(KP_PLUSMINUS);
496 | DEFINE_SCANCODE(KP_CLEAR);
497 | DEFINE_SCANCODE(KP_CLEARENTRY);
498 | DEFINE_SCANCODE(KP_BINARY);
499 | DEFINE_SCANCODE(KP_OCTAL);
500 | DEFINE_SCANCODE(KP_DECIMAL);
501 | DEFINE_SCANCODE(KP_HEXADECIMAL);
502 |
503 | DEFINE_SCANCODE(LCTRL);
504 | DEFINE_SCANCODE(LSHIFT);
505 | DEFINE_SCANCODE(LALT);
506 | DEFINE_SCANCODE(LGUI);
507 | DEFINE_SCANCODE(RCTRL);
508 | DEFINE_SCANCODE(RSHIFT);
509 | DEFINE_SCANCODE(RALT);
510 | DEFINE_SCANCODE(RGUI);
511 |
512 | DEFINE_SCANCODE(MODE);
513 |
514 | DEFINE_SCANCODE(AUDIONEXT);
515 | DEFINE_SCANCODE(AUDIOPREV);
516 | DEFINE_SCANCODE(AUDIOSTOP);
517 | DEFINE_SCANCODE(AUDIOPLAY);
518 | DEFINE_SCANCODE(AUDIOMUTE);
519 | DEFINE_SCANCODE(MEDIASELECT);
520 | DEFINE_SCANCODE(WWW);
521 | DEFINE_SCANCODE(MAIL);
522 | DEFINE_SCANCODE(CALCULATOR);
523 | DEFINE_SCANCODE(COMPUTER);
524 | DEFINE_SCANCODE(AC_SEARCH);
525 | DEFINE_SCANCODE(AC_HOME);
526 | DEFINE_SCANCODE(AC_BACK);
527 | DEFINE_SCANCODE(AC_FORWARD);
528 | DEFINE_SCANCODE(AC_STOP);
529 | DEFINE_SCANCODE(AC_REFRESH);
530 | DEFINE_SCANCODE(AC_BOOKMARKS);
531 |
532 | DEFINE_SCANCODE(BRIGHTNESSDOWN);
533 | DEFINE_SCANCODE(BRIGHTNESSUP);
534 | DEFINE_SCANCODE(DISPLAYSWITCH);
535 |
536 | DEFINE_SCANCODE(KBDILLUMTOGGLE);
537 | DEFINE_SCANCODE(KBDILLUMDOWN);
538 | DEFINE_SCANCODE(KBDILLUMUP);
539 | DEFINE_SCANCODE(EJECT);
540 | DEFINE_SCANCODE(SLEEP);
541 |
542 | DEFINE_SCANCODE(APP1);
543 | DEFINE_SCANCODE(APP2);
544 |
545 | DEFINE_KEYCODE(UNKNOWN,/* @return [Integer] unused keycode */);
546 | DEFINE_KEYCODE(RETURN);
547 | DEFINE_KEYCODE(ESCAPE);
548 | DEFINE_KEYCODE(BACKSPACE);
549 | DEFINE_KEYCODE(TAB);
550 | DEFINE_KEYCODE(SPACE);
551 | DEFINE_KEYCODE(EXCLAIM);
552 | DEFINE_KEYCODE(QUOTEDBL);
553 | DEFINE_KEYCODE(HASH);
554 | DEFINE_KEYCODE(PERCENT);
555 | DEFINE_KEYCODE(DOLLAR);
556 | DEFINE_KEYCODE(AMPERSAND);
557 | DEFINE_KEYCODE(QUOTE);
558 | DEFINE_KEYCODE(LEFTPAREN);
559 | DEFINE_KEYCODE(RIGHTPAREN);
560 | DEFINE_KEYCODE(ASTERISK);
561 | DEFINE_KEYCODE(PLUS);
562 | DEFINE_KEYCODE(COMMA);
563 | DEFINE_KEYCODE(MINUS);
564 | DEFINE_KEYCODE(PERIOD);
565 | DEFINE_KEYCODE(SLASH);
566 | DEFINE_KEYCODE_NUMBER(0);
567 | DEFINE_KEYCODE_NUMBER(1);
568 | DEFINE_KEYCODE_NUMBER(2);
569 | DEFINE_KEYCODE_NUMBER(3);
570 | DEFINE_KEYCODE_NUMBER(4);
571 | DEFINE_KEYCODE_NUMBER(5);
572 | DEFINE_KEYCODE_NUMBER(6);
573 | DEFINE_KEYCODE_NUMBER(7);
574 | DEFINE_KEYCODE_NUMBER(8);
575 | DEFINE_KEYCODE_NUMBER(9);
576 | DEFINE_KEYCODE(COLON);
577 | DEFINE_KEYCODE(SEMICOLON);
578 | DEFINE_KEYCODE(LESS);
579 | DEFINE_KEYCODE(EQUALS);
580 | DEFINE_KEYCODE(GREATER);
581 | DEFINE_KEYCODE(QUESTION);
582 | DEFINE_KEYCODE(AT);
583 | /*
584 | Skip uppercase letters
585 | */
586 | DEFINE_KEYCODE(LEFTBRACKET);
587 | DEFINE_KEYCODE(BACKSLASH);
588 | DEFINE_KEYCODE(RIGHTBRACKET);
589 | DEFINE_KEYCODE(CARET);
590 | DEFINE_KEYCODE(UNDERSCORE);
591 | DEFINE_KEYCODE(BACKQUOTE);
592 |
593 | DEFINE_KEYCODE_ALPH(a);
594 | DEFINE_KEYCODE_ALPH(b);
595 | DEFINE_KEYCODE_ALPH(c);
596 | DEFINE_KEYCODE_ALPH(d);
597 | DEFINE_KEYCODE_ALPH(e);
598 | DEFINE_KEYCODE_ALPH(f);
599 | DEFINE_KEYCODE_ALPH(g);
600 | DEFINE_KEYCODE_ALPH(h);
601 | DEFINE_KEYCODE_ALPH(i);
602 | DEFINE_KEYCODE_ALPH(j);
603 | DEFINE_KEYCODE_ALPH(k);
604 | DEFINE_KEYCODE_ALPH(l);
605 | DEFINE_KEYCODE_ALPH(m);
606 | DEFINE_KEYCODE_ALPH(n);
607 | DEFINE_KEYCODE_ALPH(o);
608 | DEFINE_KEYCODE_ALPH(p);
609 | DEFINE_KEYCODE_ALPH(q);
610 | DEFINE_KEYCODE_ALPH(r);
611 | DEFINE_KEYCODE_ALPH(s);
612 | DEFINE_KEYCODE_ALPH(t);
613 | DEFINE_KEYCODE_ALPH(u);
614 | DEFINE_KEYCODE_ALPH(v);
615 | DEFINE_KEYCODE_ALPH(w);
616 | DEFINE_KEYCODE_ALPH(x);
617 | DEFINE_KEYCODE_ALPH(y);
618 | DEFINE_KEYCODE_ALPH(z);
619 |
620 | DEFINE_KEYCODE(CAPSLOCK);
621 |
622 | DEFINE_KEYCODE(F1);
623 | DEFINE_KEYCODE(F2);
624 | DEFINE_KEYCODE(F3);
625 | DEFINE_KEYCODE(F4);
626 | DEFINE_KEYCODE(F5);
627 | DEFINE_KEYCODE(F6);
628 | DEFINE_KEYCODE(F7);
629 | DEFINE_KEYCODE(F8);
630 | DEFINE_KEYCODE(F9);
631 | DEFINE_KEYCODE(F10);
632 | DEFINE_KEYCODE(F11);
633 | DEFINE_KEYCODE(F12);
634 |
635 | DEFINE_KEYCODE(PRINTSCREEN);
636 | DEFINE_KEYCODE(SCROLLLOCK);
637 | DEFINE_KEYCODE(PAUSE);
638 | DEFINE_KEYCODE(INSERT);
639 | DEFINE_KEYCODE(HOME);
640 | DEFINE_KEYCODE(PAGEUP);
641 | DEFINE_KEYCODE(DELETE);
642 | DEFINE_KEYCODE(END);
643 | DEFINE_KEYCODE(PAGEDOWN);
644 | DEFINE_KEYCODE(RIGHT);
645 | DEFINE_KEYCODE(LEFT);
646 | DEFINE_KEYCODE(DOWN);
647 | DEFINE_KEYCODE(UP);
648 |
649 | DEFINE_KEYCODE(NUMLOCKCLEAR);
650 | DEFINE_KEYCODE(KP_DIVIDE);
651 | DEFINE_KEYCODE(KP_MULTIPLY);
652 | DEFINE_KEYCODE(KP_MINUS);
653 | DEFINE_KEYCODE(KP_PLUS);
654 | DEFINE_KEYCODE(KP_ENTER);
655 | DEFINE_KEYCODE(KP_1);
656 | DEFINE_KEYCODE(KP_2);
657 | DEFINE_KEYCODE(KP_3);
658 | DEFINE_KEYCODE(KP_4);
659 | DEFINE_KEYCODE(KP_5);
660 | DEFINE_KEYCODE(KP_6);
661 | DEFINE_KEYCODE(KP_7);
662 | DEFINE_KEYCODE(KP_8);
663 | DEFINE_KEYCODE(KP_9);
664 | DEFINE_KEYCODE(KP_0);
665 | DEFINE_KEYCODE(KP_PERIOD);
666 |
667 | DEFINE_KEYCODE(APPLICATION);
668 | DEFINE_KEYCODE(POWER);
669 | DEFINE_KEYCODE(KP_EQUALS);
670 | DEFINE_KEYCODE(F13);
671 | DEFINE_KEYCODE(F14);
672 | DEFINE_KEYCODE(F15);
673 | DEFINE_KEYCODE(F16);
674 | DEFINE_KEYCODE(F17);
675 | DEFINE_KEYCODE(F18);
676 | DEFINE_KEYCODE(F19);
677 | DEFINE_KEYCODE(F20);
678 | DEFINE_KEYCODE(F21);
679 | DEFINE_KEYCODE(F22);
680 | DEFINE_KEYCODE(F23);
681 | DEFINE_KEYCODE(F24);
682 | DEFINE_KEYCODE(EXECUTE);
683 | DEFINE_KEYCODE(HELP);
684 | DEFINE_KEYCODE(MENU);
685 | DEFINE_KEYCODE(SELECT);
686 | DEFINE_KEYCODE(STOP);
687 | DEFINE_KEYCODE(AGAIN);
688 | DEFINE_KEYCODE(UNDO);
689 | DEFINE_KEYCODE(CUT);
690 | DEFINE_KEYCODE(COPY);
691 | DEFINE_KEYCODE(PASTE);
692 | DEFINE_KEYCODE(FIND);
693 | DEFINE_KEYCODE(MUTE);
694 | DEFINE_KEYCODE(VOLUMEUP);
695 | DEFINE_KEYCODE(VOLUMEDOWN);
696 | DEFINE_KEYCODE(KP_COMMA);
697 | DEFINE_KEYCODE(KP_EQUALSAS400);
698 |
699 | DEFINE_KEYCODE(ALTERASE);
700 | DEFINE_KEYCODE(SYSREQ);
701 | DEFINE_KEYCODE(CANCEL);
702 | DEFINE_KEYCODE(CLEAR);
703 | DEFINE_KEYCODE(PRIOR);
704 | DEFINE_KEYCODE(RETURN2);
705 | DEFINE_KEYCODE(SEPARATOR);
706 | DEFINE_KEYCODE(OUT);
707 | DEFINE_KEYCODE(OPER);
708 | DEFINE_KEYCODE(CLEARAGAIN);
709 | DEFINE_KEYCODE(CRSEL);
710 | DEFINE_KEYCODE(EXSEL);
711 |
712 | DEFINE_KEYCODE(KP_00);
713 | DEFINE_KEYCODE(KP_000);
714 | DEFINE_KEYCODE(THOUSANDSSEPARATOR);
715 |
716 | DEFINE_KEYCODE(DECIMALSEPARATOR);
717 |
718 | DEFINE_KEYCODE(CURRENCYUNIT);
719 | DEFINE_KEYCODE(CURRENCYSUBUNIT);
720 | DEFINE_KEYCODE(KP_LEFTPAREN);
721 | DEFINE_KEYCODE(KP_RIGHTPAREN);
722 | DEFINE_KEYCODE(KP_LEFTBRACE);
723 | DEFINE_KEYCODE(KP_RIGHTBRACE);
724 | DEFINE_KEYCODE(KP_TAB);
725 | DEFINE_KEYCODE(KP_BACKSPACE);
726 | DEFINE_KEYCODE(KP_A);
727 | DEFINE_KEYCODE(KP_B);
728 | DEFINE_KEYCODE(KP_C);
729 | DEFINE_KEYCODE(KP_D);
730 | DEFINE_KEYCODE(KP_E);
731 | DEFINE_KEYCODE(KP_F);
732 | DEFINE_KEYCODE(KP_XOR);
733 | DEFINE_KEYCODE(KP_POWER);
734 | DEFINE_KEYCODE(KP_PERCENT);
735 | DEFINE_KEYCODE(KP_LESS);
736 | DEFINE_KEYCODE(KP_GREATER);
737 | DEFINE_KEYCODE(KP_AMPERSAND);
738 | DEFINE_KEYCODE(KP_DBLAMPERSAND);
739 | DEFINE_KEYCODE(KP_VERTICALBAR);
740 | DEFINE_KEYCODE(KP_DBLVERTICALBAR);
741 | DEFINE_KEYCODE(KP_COLON);
742 | DEFINE_KEYCODE(KP_HASH);
743 | DEFINE_KEYCODE(KP_SPACE);
744 | DEFINE_KEYCODE(KP_AT);
745 | DEFINE_KEYCODE(KP_EXCLAM);
746 | DEFINE_KEYCODE(KP_MEMSTORE);
747 | DEFINE_KEYCODE(KP_MEMRECALL);
748 | DEFINE_KEYCODE(KP_MEMCLEAR);
749 | DEFINE_KEYCODE(KP_MEMADD);
750 | DEFINE_KEYCODE(KP_MEMSUBTRACT);
751 | DEFINE_KEYCODE(KP_MEMMULTIPLY);
752 | DEFINE_KEYCODE(KP_MEMDIVIDE);
753 | DEFINE_KEYCODE(KP_PLUSMINUS);
754 | DEFINE_KEYCODE(KP_CLEAR);
755 | DEFINE_KEYCODE(KP_CLEARENTRY);
756 | DEFINE_KEYCODE(KP_BINARY);
757 | DEFINE_KEYCODE(KP_OCTAL);
758 | DEFINE_KEYCODE(KP_DECIMAL);
759 | DEFINE_KEYCODE(KP_HEXADECIMAL);
760 |
761 | DEFINE_KEYCODE(LCTRL);
762 | DEFINE_KEYCODE(LSHIFT);
763 | DEFINE_KEYCODE(LALT);
764 | DEFINE_KEYCODE(LGUI);
765 | DEFINE_KEYCODE(RCTRL);
766 | DEFINE_KEYCODE(RSHIFT);
767 | DEFINE_KEYCODE(RALT);
768 | DEFINE_KEYCODE(RGUI);
769 |
770 | DEFINE_KEYCODE(MODE);
771 |
772 | DEFINE_KEYCODE(AUDIONEXT);
773 | DEFINE_KEYCODE(AUDIOPREV);
774 | DEFINE_KEYCODE(AUDIOSTOP);
775 | DEFINE_KEYCODE(AUDIOPLAY);
776 | DEFINE_KEYCODE(AUDIOMUTE);
777 | DEFINE_KEYCODE(MEDIASELECT);
778 | DEFINE_KEYCODE(WWW);
779 | DEFINE_KEYCODE(MAIL);
780 | DEFINE_KEYCODE(CALCULATOR);
781 | DEFINE_KEYCODE(COMPUTER);
782 | DEFINE_KEYCODE(AC_SEARCH);
783 | DEFINE_KEYCODE(AC_HOME);
784 | DEFINE_KEYCODE(AC_BACK);
785 | DEFINE_KEYCODE(AC_FORWARD);
786 | DEFINE_KEYCODE(AC_STOP);
787 | DEFINE_KEYCODE(AC_REFRESH);
788 | DEFINE_KEYCODE(AC_BOOKMARKS);
789 |
790 | DEFINE_KEYCODE(BRIGHTNESSDOWN);
791 |
792 | DEFINE_KEYCODE(BRIGHTNESSUP);
793 | DEFINE_KEYCODE(DISPLAYSWITCH);
794 | DEFINE_KEYCODE(KBDILLUMTOGGLE);
795 |
796 | DEFINE_KEYCODE(KBDILLUMDOWN);
797 | DEFINE_KEYCODE(KBDILLUMUP);
798 | DEFINE_KEYCODE(EJECT);
799 | DEFINE_KEYCODE(SLEEP);
800 |
801 | /* @return [Integer] 0 (no modifier is applicable) */
802 | DEFINE_KEYMOD(NONE);
803 | /* @return [Integer] the modifier key bit mask for the left shift key */
804 | DEFINE_KEYMOD(LSHIFT);
805 | /* @return [Integer] the modifier key bit mask for the right shift key */
806 | DEFINE_KEYMOD(RSHIFT);
807 | /* @return [Integer] the modifier key bit mask for the left control key */
808 | DEFINE_KEYMOD(LCTRL);
809 | /* @return [Integer] the modifier key bit mask for the right control key */
810 | DEFINE_KEYMOD(RCTRL);
811 | /* @return [Integer] the modifier key bit mask for the left alt key */
812 | DEFINE_KEYMOD(LALT);
813 | /* @return [Integer] the modifier key bit mask for the right alt key */
814 | DEFINE_KEYMOD(RALT);
815 | /* @return [Integer] the modifier key bit mask for the left GUI key (often the window key) */
816 | DEFINE_KEYMOD(LGUI);
817 | /* @return [Integer] the modifier key bit mask for the right GUI key (often the window key) */
818 | DEFINE_KEYMOD(RGUI);
819 | /* @return [Integer] the modifier key bit mask for the numlock key */
820 | DEFINE_KEYMOD(NUM);
821 | /* @return [Integer] the modifier key bit mask for the capslock key */
822 | DEFINE_KEYMOD(CAPS);
823 | /* @return [Integer] the modifier key bit mask for the mode key (AltGr) */
824 | DEFINE_KEYMOD(MODE);
825 | /* @return [Integer] the modifier key bit mask for the left and right control key */
826 | DEFINE_KEYMOD(CTRL);
827 | /* @return [Integer] the modifier key bit mask for the left and right shift key */
828 | DEFINE_KEYMOD(SHIFT);
829 | /* @return [Integer] the modifier key bit mask for the left and right alt key */
830 | DEFINE_KEYMOD(ALT);
831 | /* @return [Integer] the modifier key bit mask for the left and right GUI key */
832 | DEFINE_KEYMOD(GUI);
833 | /* @return [Integer] reserved bit mask for future use */
834 | DEFINE_KEYMOD(RESERVED);
835 | }
836 |
--------------------------------------------------------------------------------
/lib/sdl2.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2_ext'
2 | require 'sdl2/version'
3 |
4 |
--------------------------------------------------------------------------------
/lib/sdl2/version.rb:
--------------------------------------------------------------------------------
1 | module SDL2
2 | # @return [String] Version string of Ruby/SDL2
3 | VERSION = "0.3.6"
4 | # @return [Array] Version of Ruby/SDL2, [major, minor, patch level]
5 | VERSION_NUMBER = VERSION.split(".").map(&:to_i)
6 | end
7 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | #define SDL2_EXTERN
2 | #include "rubysdl2_internal.h"
3 | #include
4 | #include
5 | #include
6 | #ifdef HAVE_SDL_IMAGE_H
7 | #include
8 | #endif
9 | #ifdef HAVE_SDL_MIXER_H
10 | #include
11 | #endif
12 | #ifdef HAVE_SDL_TTF_H
13 | #include
14 | #endif
15 | #include
16 | #include
17 |
18 | int rubysdl2_handle_error(int code, const char* cfunc)
19 | {
20 | VALUE err;
21 | char err_msg[1024];
22 |
23 | if (code >= 0)
24 | return code;
25 |
26 | snprintf(err_msg, sizeof(err_msg), "%s (cfunc=%s)", SDL_GetError(), cfunc);
27 | err = rb_exc_new2(eSDL2Error, err_msg);
28 | rb_iv_set(eSDL2Error, "@error_code", INT2NUM(code));
29 |
30 | rb_exc_raise(err);
31 | }
32 |
33 | void rubysdl2_define_attr_readers(VALUE klass, ...)
34 | {
35 | va_list ap;
36 |
37 | va_start(ap, klass);
38 | for (;;) {
39 | const char* field_name = va_arg(ap, const char*);
40 | if (field_name == NULL)
41 | break;
42 | rb_define_attr(klass, field_name, 1, 0);
43 | }
44 | va_end(ap);
45 | }
46 |
47 | VALUE utf8str_new_cstr(const char* str)
48 | {
49 | return rb_enc_str_new(str, strlen(str), rb_utf8_encoding());
50 | }
51 |
52 | VALUE SDL_version_to_String(const SDL_version* ver)
53 | {
54 | return rb_sprintf("%d.%d.%d", ver->major, ver->minor, ver->patch);
55 | }
56 |
57 | VALUE SDL_version_to_Array(const SDL_version* ver)
58 | {
59 | return rb_ary_new3(3, INT2FIX(ver->major), INT2FIX(ver->minor), INT2FIX(ver->patch));
60 | }
61 |
62 | const char* INT2BOOLCSTR(int n)
63 | {
64 | return n ? "true" : "false";
65 | }
66 |
67 | typedef enum {
68 | NOT_INITIALIZED, INITIALIZDED, FINALIZED
69 | } sdl2_state;
70 |
71 | static sdl2_state state = NOT_INITIALIZED;
72 |
73 | static void quit(VALUE unused)
74 | {
75 | if (state != INITIALIZDED)
76 | return;
77 |
78 | #ifdef HAVE_SDL_IMAGE_H
79 | IMG_Quit();
80 | #endif
81 | #ifdef HAVE_SDL_MIXER_H
82 | Mix_Quit();
83 | #endif
84 | #ifdef HAVE_SDL_TTF_H
85 | TTF_Quit();
86 | #endif
87 | SDL_VideoQuit();
88 | SDL_Quit();
89 | state = FINALIZED;
90 | }
91 |
92 | /*
93 | * Initialize SDL.
94 | * You must call this function before using any other Ruby/SDL2 methods.
95 | *
96 | * You can specify initialized subsystem by flags which is
97 | * bitwise OR of the following constants:
98 | *
99 | * * SDL2::INIT_TIMER - timer subsystem
100 | * * SDL2::INIT_AUDIO - audio subsystem
101 | * * SDL2::INIT_VIDEO - video subsystem
102 | * * SDL2::INIT_JOYSTICK - joystick subsystem
103 | * * SDL2::INIT_HAPTIC - haptic (force feedback) subsystem
104 | * (interface is not implemented yet)
105 | * * SDL2::INIT_GAMECONTROLLER - controller subsystem
106 | * * SDL2::INIT_EVENTS - events subsystem
107 | * * SDL2::INIT_EVERYTHING - all of the above flags
108 | * * SDL2::INIT_NOPARACHUTE - this flag is ignored; for compatibility
109 | *
110 | * @overload init(flags)
111 | *
112 | * @param [Integer] flags initializing subsystems
113 | *
114 | * @return [nil]
115 | */
116 | static VALUE SDL2_s_init(VALUE self, VALUE flags)
117 | {
118 | SDL_SetMainReady();
119 | HANDLE_ERROR(SDL_Init(NUM2UINT(flags)));
120 | state = INITIALIZDED;
121 | return Qnil;
122 | }
123 |
124 | int rubysdl2_is_active(void)
125 | {
126 | return state == INITIALIZDED;
127 | }
128 |
129 | static VALUE libsdl_version(void)
130 | {
131 | SDL_version version;
132 | SDL_GetVersion(&version);
133 | return SDL_version_to_String(&version);
134 | }
135 |
136 | static VALUE libsdl_version_number(void)
137 | {
138 | SDL_version version;
139 | SDL_GetVersion(&version);
140 | return SDL_version_to_Array(&version);
141 | }
142 |
143 | static VALUE libsdl_revision(void)
144 | {
145 | return rb_usascii_str_new_cstr(SDL_GetRevision());
146 | }
147 |
148 | /*
149 | * Document-module: SDL2
150 | *
151 | * Namespace module for Ruby/SDL2.
152 | *
153 | */
154 |
155 | /*
156 | * Document-class: SDL2::Error
157 | *
158 | * An exception class for all Ruby/SDL2 specific errors.
159 | *
160 | * @attribute [r] error_code
161 | * @return [Integer] error code sent from SDL library
162 | */
163 | void Init_sdl2_ext(void)
164 | {
165 | mSDL2 = rb_define_module("SDL2");
166 |
167 | rb_define_module_function(mSDL2, "init", SDL2_s_init, 1);
168 | #define DEFINE_SDL_INIT_CONST(type) \
169 | rb_define_const(mSDL2, "INIT_" #type, UINT2NUM(SDL_INIT_##type))
170 |
171 | DEFINE_SDL_INIT_CONST(TIMER);
172 | DEFINE_SDL_INIT_CONST(AUDIO);
173 | DEFINE_SDL_INIT_CONST(VIDEO);
174 | DEFINE_SDL_INIT_CONST(JOYSTICK);
175 | DEFINE_SDL_INIT_CONST(HAPTIC);
176 | DEFINE_SDL_INIT_CONST(GAMECONTROLLER);
177 | DEFINE_SDL_INIT_CONST(EVENTS);
178 | DEFINE_SDL_INIT_CONST(EVERYTHING);
179 | DEFINE_SDL_INIT_CONST(NOPARACHUTE);
180 |
181 | /* @return [String] SDL's version string */
182 | rb_define_const(mSDL2, "LIBSDL_VERSION", libsdl_version());
183 | /* @return [Array(Integer, Integer, Integer)] SDL's version array of numbers */
184 | rb_define_const(mSDL2, "LIBSDL_VERSION_NUMBER", libsdl_version_number());
185 | /* @return [String] SDL's revision (from VCS) string */
186 | rb_define_const(mSDL2, "LIBSDL_REVISION", libsdl_revision());
187 | /* @return [Integer] always 0
188 | * @deprecated
189 | */
190 | rb_define_const(mSDL2, "LIBSDL_REVISION_NUMBER", INT2NUM(0));
191 |
192 | #ifdef HAVE_SDL_IMAGE_H
193 | {
194 | const SDL_version* version = IMG_Linked_Version();
195 | /* @return [String] SDL_image's version string, only available if SDL_image is linked */
196 | rb_define_const(mSDL2, "LIBSDL_IMAGE_VERSION", SDL_version_to_String(version));
197 | /* @return [Array(Integer, Integer, Integer)] SDL_image's version array of numbers */
198 | rb_define_const(mSDL2, "LIBSDL_IMAGE_VERSION_NUMBER", SDL_version_to_Array(version));
199 | }
200 | #endif
201 | #ifdef HAVE_SDL_TTF_H
202 | {
203 | const SDL_version* version = TTF_Linked_Version();
204 | /* @return [String] SDL_ttf's version string, only available if SDL_ttf is linked */
205 | rb_define_const(mSDL2, "LIBSDL_TTF_VERSION", SDL_version_to_String(version));
206 | /* @return [Array(Integer, Integer, Integer)] SDL_ttf's version array of numbers */
207 | rb_define_const(mSDL2, "LIBSDL_TTF_VERSION_NUMBER", SDL_version_to_Array(version));
208 | }
209 | #endif
210 | #ifdef HAVE_SDL_MIXER_H
211 | {
212 | const SDL_version* version = Mix_Linked_Version();
213 | /* @return [Integer] SDL_mixer's version string , only available if SDL_mixer is linked */
214 | rb_define_const(mSDL2, "LIBSDL_MIXER_VERSION", SDL_version_to_String(version));
215 | /* @return [Array(Integer, Integer, Integer)] SDL_mixer's version array of numbers */
216 | rb_define_const(mSDL2, "LIBSDL_MIXER_VERSION_NUMBER", SDL_version_to_Array(version));
217 | }
218 | #endif
219 |
220 | eSDL2Error = rb_define_class_under(mSDL2, "Error", rb_eStandardError);
221 | rb_define_attr(eSDL2Error, "error_code", 1, 0);
222 |
223 | rubysdl2_init_hints();
224 | rubysdl2_init_video();
225 | rubysdl2_init_gl();
226 | rubysdl2_init_messagebox();
227 | rubysdl2_init_event();
228 | rubysdl2_init_key();
229 | rubysdl2_init_mouse();
230 | rubysdl2_init_joystick();
231 | rubysdl2_init_gamecontorller();
232 | rubysdl2_init_timer();
233 | rubysdl2_init_image();
234 | rubysdl2_init_mixer();
235 | rubysdl2_init_ttf();
236 | rubysdl2_init_filesystem();
237 | rubysdl2_init_clipboard();
238 |
239 | rb_set_end_proc(quit, 0);
240 | return;
241 | }
242 |
--------------------------------------------------------------------------------
/messagebox.c:
--------------------------------------------------------------------------------
1 | #include "rubysdl2_internal.h"
2 | #include
3 |
4 | static VALUE sym_flags, sym_window, sym_title, sym_message, sym_buttons, sym_color_scheme,
5 | sym_id, sym_text, sym_bg, sym_button_border, sym_button_bg, sym_button_selected;
6 |
7 | static VALUE mMessageBox;
8 |
9 | static inline SDL_Window* Get_SDL_Window_or_NULL(VALUE win)
10 | {
11 | if (win == Qnil)
12 | return NULL;
13 | else
14 | return Get_SDL_Window(win);
15 | }
16 |
17 | /*
18 | * Document-module: SDL2::MessageBox
19 | *
20 | * This module provides functions to show a modal message box.
21 | */
22 |
23 | /*
24 | * @overload show_simple_box(flag, title, message, parent)
25 | * Create a simple modal message box.
26 | *
27 | * This function pauses all ruby's threads and
28 | * the threads are resumed when modal dialog is closed.
29 | *
30 | * You can create a message box before calling {SDL2.init}.
31 | *
32 | * You specify one of the following constants as flag
33 | *
34 | * * {SDL2::MessageBox::ERROR}
35 | * * {SDL2::MessageBox::WARNING}
36 | * * {SDL2::MessageBox::INFORMATION}
37 | *
38 | * @example show warning dialog
39 | *
40 | * SDL2.show_simple_message_box(SDL2::MessageBox::WARNING, "warning!",
41 | * "Somewhat special warning message!!", nil)
42 | *
43 | * @param [Integer] flag one of the above flags
44 | * @param [String] title title text
45 | * @param [String] message message text
46 | * @param [SDL2::Window,nil] parent the parent window, or nil for no parent
47 | * @return [nil]
48 | *
49 | * @see .show
50 | */
51 | static VALUE MessageBox_s_show_simple_box(VALUE self, VALUE flag, VALUE title,
52 | VALUE message, VALUE parent)
53 | {
54 | title = rb_str_export_to_utf8(title);
55 | message = rb_str_export_to_utf8(message);
56 | HANDLE_ERROR(SDL_ShowSimpleMessageBox(NUM2UINT(flag),
57 | StringValueCStr(title),
58 | StringValueCStr(message),
59 | Get_SDL_Window_or_NULL(parent)));
60 | return Qnil;
61 | }
62 |
63 | static void set_color_scheme(VALUE colors, VALUE sym, SDL_MessageBoxColor* color)
64 | {
65 | VALUE c = rb_hash_aref(colors, sym);
66 | Check_Type(c, T_ARRAY);
67 | color->r = NUM2UCHAR(rb_ary_entry(c, 0));
68 | color->g = NUM2UCHAR(rb_ary_entry(c, 1));
69 | color->b = NUM2UCHAR(rb_ary_entry(c, 2));
70 | }
71 |
72 | /*
73 | * @overload show(flags:, window: nil, title:, message:, buttons:, color_scheme: nil)
74 | * Create a model message box.
75 | *
76 | * You specify one of the following constants as flag
77 | *
78 | * * {SDL2::MessageBox::ERROR}
79 | * * {SDL2::MessageBox::WARNING}
80 | * * {SDL2::MessageBox::INFORMATION}
81 | *
82 | * One button in the dialog represents a hash with folloing elements.
83 | *
84 | * { flags: 0, SDL2::MessageBox::BUTTON_ESCAPEKEY_DEFAULT, or
85 | * SDL2::MessageBox::BUTTON_RETURNKEY_DEFAULT,
86 | * you can ignore it for 0,
87 | * text: text of a button,
88 | * id: index of the button
89 | * }
90 | *
91 | * and buttons is an array of above button hashes.
92 | *
93 | * You can specify the color of message box by color_scheme.
94 | * color_scheme is an hash whose keys are :bg, :text, :button_border, :button_bg,
95 | * and :button_selected and values are array of three integers representing
96 | * color.
97 | * You can also use default color scheme by giving nil.
98 | *
99 | *
100 | * This function pauses all ruby's threads until
101 | * the modal dialog is closed.
102 | *
103 | * You can create a message box before calling {SDL2.init}.
104 | *
105 | * @example show a dialog with 3 buttons
106 | * button = SDL2::MessageBox.show(flags: SDL2::MessageBox::WARNING,
107 | * window: nil,
108 | * title: "Warning window",
109 | * message: "Here is the warning message",
110 | * buttons: [ { # flags is ignored
111 | * id: 0,
112 | * text: "No",
113 | * },
114 | * {flags: SDL2::MessageBox::BUTTON_RETURNKEY_DEFAULT,
115 | * id: 1,
116 | * text: "Yes",
117 | * },
118 | * {flags: SDL2::MessageBox::BUTTON_ESCAPEKEY_DEFAULT,
119 | * id: 2,
120 | * text: "Cancel",
121 | * },
122 | * ],
123 | * color_scheme: {
124 | * bg: [255, 0, 0],
125 | * text: [0, 255, 0],
126 | * button_border: [255, 0, 0],
127 | * button_bg: [0, 0, 255],
128 | * button_selected: [255, 0, 0]
129 | * }
130 | * )
131 | *
132 | * @param [Integer] flags message box type flag
133 | * @param [SDL2::Window,nil] window the parent window, or nil for no parent
134 | * @param [String] title the title text
135 | * @param [String] message the message text
136 | * @param [Array Object>>] buttons array of buttons
137 | * @param [HashArray(Integer,Integer,Integer)>, nil] color_scheme
138 | * color scheme, or nil for the default color scheme
139 | * @return [Integer] pressed button id
140 | *
141 | * @see .show_simple_box
142 | */
143 | static VALUE MessageBox_s_show(VALUE self, VALUE params)
144 | {
145 | SDL_MessageBoxData mb_data;
146 | VALUE title, message, texts, buttons, color_scheme;
147 | long num_buttons;
148 | int i;
149 | SDL_MessageBoxButtonData* button_data;
150 | SDL_MessageBoxColorScheme scheme;
151 | int buttonid;
152 |
153 | Check_Type(params, T_HASH);
154 | mb_data.flags = NUM2INT(rb_hash_aref(params, sym_flags));
155 | mb_data.window = Get_SDL_Window_or_NULL(rb_hash_aref(params, sym_window));
156 | title = rb_str_export_to_utf8(rb_hash_aref(params, sym_title));
157 | mb_data.title = StringValueCStr(title);
158 | message = rb_str_export_to_utf8(rb_hash_aref(params, sym_message));
159 | mb_data.message = StringValueCStr(message);
160 |
161 | buttons = rb_hash_aref(params, sym_buttons);
162 | Check_Type(buttons, T_ARRAY);
163 | mb_data.numbuttons = num_buttons = RARRAY_LEN(buttons);
164 | button_data = ALLOCA_N(SDL_MessageBoxButtonData, num_buttons);
165 | mb_data.buttons = button_data;
166 | texts = rb_ary_new2(num_buttons);
167 |
168 | for (i=0; i
5 |
6 | static VALUE mMixer;
7 | static VALUE cChunk;
8 | static VALUE cMusic;
9 | static VALUE mChannels;
10 | static VALUE cGroup;
11 | static VALUE mMusicChannel;
12 |
13 | static VALUE playing_chunks = Qnil;
14 | static VALUE playing_music = Qnil;
15 |
16 | #define MIX_ERROR() do { HANDLE_ERROR(SDL_SetError("%s", Mix_GetError())); } while(0)
17 | #define HANDLE_MIX_ERROR(code) \
18 | do { if ((code) < 0) { MIX_ERROR(); } } while (0)
19 |
20 | typedef struct Chunk {
21 | Mix_Chunk* chunk;
22 | } Chunk;
23 |
24 | typedef struct Music {
25 | Mix_Music* music;
26 | } Music;
27 |
28 | static void Chunk_free(Chunk* c)
29 | {
30 | if (rubysdl2_is_active() && c->chunk)
31 | Mix_FreeChunk(c->chunk);
32 | free(c);
33 | }
34 |
35 | static VALUE Chunk_new(Mix_Chunk* chunk)
36 | {
37 | Chunk* c = ALLOC(Chunk);
38 | c->chunk = chunk;
39 | return Data_Wrap_Struct(cChunk, 0, Chunk_free, c);
40 | }
41 |
42 | DEFINE_WRAPPER(Mix_Chunk, Chunk, chunk, cChunk, "SDL2::Mixer::Chunk");
43 |
44 | static void Music_free(Music* m)
45 | {
46 | if (rubysdl2_is_active() && m->music)
47 | Mix_FreeMusic(m->music);
48 | free(m);
49 | }
50 |
51 | static VALUE Music_new(Mix_Music* music)
52 | {
53 | Music* c = ALLOC(Music);
54 | c->music = music;
55 | return Data_Wrap_Struct(cMusic, 0, Music_free, c);
56 | }
57 |
58 | DEFINE_WRAPPER(Mix_Music, Music, music, cMusic, "SDL2::Mixer::Music");
59 |
60 | /*
61 | * Document-module: SDL2::Mixer
62 | *
63 | * Sound mixing module.
64 | *
65 | * With this module, you can play many kinds of sound files such as:
66 | *
67 | * * WAVE/RIFF (.wav)
68 | * * AIFF (.aiff)
69 | * * VOC (.voc)
70 | * * MOD (.mod .xm .s3m .669 .it .med etc.)
71 | * * MIDI (.mid)
72 | * * OggVorbis (.ogg)
73 | * * MP3 (.mp3)
74 | * * FLAC (.flac)
75 | *
76 | * Before playing sounds,
77 | * you need to initialize this module by {.init} and
78 | * open a sound device by {.open}.
79 | *
80 | * This module mixes multiple sound sources in parallel.
81 | * To play a sound source, you assign the source to a "channel"
82 | * and this module mixes all sound sources assigned to the channels.
83 | *
84 | * In this module, there are two types of sound sources:
85 | * {SDL2::Mixer::Chunk} and {SDL2::Mixer::Music}.
86 | * And there are two corresponding types of channels:
87 | * {SDL2::Mixer::Channels} and {SDL2::Mixer::MusicChannel}.
88 | *
89 | * {SDL2::Mixer::Channels} module plays {SDL2::Mixer::Chunk} objects,
90 | * through multiple (default eight) channels. This module is suitable
91 | * for the sound effects.
92 | * The number of channels is variable with {SDL2::Mixer::Channels.allocate}.
93 | *
94 | * {SDL2::Mixer::MusicChannel} module plays {SDL2::Mixer::Music} objects.
95 | * This module has only one playing channel, and you cannot play
96 | * multiple music in parallel. However an {SDL2::Mixer::Music} object
97 | * is more efficient for memory, and this module supports more file formats
98 | * than {SDL2::Mixer::Channels}.
99 | * This module is suitable for playing "BGMs" of your application.
100 | *
101 | */
102 |
103 | /*
104 | * @overload init(flags)
105 | * Initialize the mixer library.
106 | *
107 | * This module function load dynamically-linked libraries for sound file
108 | * formats such as ogg and flac.
109 | *
110 | * You can give the initialized libraries (file formats) with OR'd bits of the
111 | * following constants:
112 | *
113 | * * SDL2::Mixer::INIT_FLAC
114 | * * SDL2::Mixer::INIT_MOD
115 | * * SDL2::Mixer::INIT_MODPLUG
116 | * * SDL2::Mixer::INIT_MP3
117 | * * SDL2::Mixer::INIT_OGG
118 | * * SDL2::Mixer::INIT_FLUIDSYNTH
119 | *
120 | * @param flags [Integer] intialized sublibraries
121 | * @return [nil]
122 | *
123 | */
124 | static VALUE Mixer_s_init(VALUE self, VALUE f)
125 | {
126 | int flags = NUM2INT(f);
127 | if ((Mix_Init(flags) & flags) != flags)
128 | rb_raise(eSDL2Error, "Couldn't initialize SDL_mixer");
129 |
130 | return Qnil;
131 | }
132 |
133 | static void check_channel(VALUE ch, int allow_minus_1)
134 | {
135 | int channel = NUM2INT(ch);
136 | if (channel >= Mix_AllocateChannels(-1))
137 | rb_raise(rb_eArgError, "too large number of channel (%d)", channel);
138 | if ((channel == -1 && !allow_minus_1) || channel < -1)
139 | rb_raise(rb_eArgError, "negative number of channel is not allowed");
140 | }
141 |
142 | /*
143 | * @overload open(freq=22050, format=SDL2::Mixer::DEFAULT_FORMAT, channels=2, chunksize=1024)
144 | * Open a sound device.
145 | *
146 | * Before calling loading/playing methods in the mixer module,
147 | * this method must be called.
148 | * Before calling this method,
149 | * {SDL2.init} must be called with SDL2::INIT_AUDIO.
150 | *
151 | * @param freq [Integer] output sampling frequency in Hz.
152 | * Normally 22050 or 44100 is used.
153 | * 44100 is CD audio rate. SDL2::Mixer::DEFAULT_FREQUENCY(22050) is best for
154 | * many kinds of game because 44100 requires too much CPU power on older computers.
155 | * @param format [Integer] output sample format
156 | * @param channels 1 is for mono, and 2 is for stereo.
157 | * @param chunksize bytes used per output sample
158 | *
159 | * @return [nil]
160 | *
161 | * @raise [SDL2::Error] raised when a device cannot be opened
162 | *
163 | * @see .init
164 | * @see .close
165 | * @see .query
166 | */
167 | static VALUE Mixer_s_open(int argc, VALUE* argv, VALUE self)
168 | {
169 | VALUE freq, format, channels, chunksize;
170 | rb_scan_args(argc, argv, "04", &freq, &format, &channels, &chunksize);
171 | HANDLE_MIX_ERROR(Mix_OpenAudio((freq == Qnil) ? MIX_DEFAULT_FREQUENCY : NUM2INT(freq),
172 | (format == Qnil) ? MIX_DEFAULT_FORMAT : NUM2UINT(format),
173 | (channels == Qnil) ? 2 : NUM2INT(channels),
174 | (chunksize == Qnil) ? 1024 : NUM2INT(chunksize)));
175 | playing_chunks = rb_ary_new();
176 | return Qnil;
177 | }
178 |
179 | /*
180 | * Close the audio device.
181 | *
182 | * @return [nil]
183 | */
184 | static VALUE Mixer_s_close(VALUE self)
185 | {
186 | Mix_CloseAudio();
187 | return Qnil;
188 | }
189 |
190 |
191 | /*
192 | * Query a sound device spec.
193 | *
194 | * This method returns the most suitable setting for {.open} the device.
195 | *
196 | * @return [Array(Integer, Integer, Integer, Integer)]
197 | * the suitable frequency in Hz, the suitable format,
198 | * the suitable number of channels (1 for mono, 2 for stereo),
199 | * and the number of call of {.open}.
200 | *
201 | */
202 | static VALUE Mixer_s_query(VALUE self)
203 | {
204 | int frequency = 0, channels = 0, num_opened;
205 | Uint16 format = 0;
206 |
207 | num_opened = Mix_QuerySpec(&frequency, &format, &channels);
208 | return rb_ary_new3(4, INT2NUM(frequency), UINT2NUM(format),
209 | INT2NUM(channels), INT2NUM(num_opened));
210 | }
211 |
212 | /*
213 | * Document-module: SDL2::Mixer::Channels
214 | *
215 | * This module plays {SDL2::Mixer::Chunk} objects in parallel.
216 | *
217 | * Each virtual sound output device is called channel, and
218 | * the number of channels determines the f
219 | */
220 |
221 | /*
222 | * @overload allocate(num_channels)
223 | * Set the number of channels being mixed.
224 | *
225 | * @param num_channels [Integer] Number of channels prepared for mixing.
226 | *
227 | * @return [Integer] the number of prepared channels.
228 | */
229 | static VALUE Channels_s_allocate(VALUE self, VALUE num_channels)
230 | {
231 | return INT2NUM(Mix_AllocateChannels(NUM2INT(num_channels)));
232 | }
233 |
234 | /*
235 | * @overload reserve(num)
236 | * Reserve channel from 0 to num-1 and reserved channels are not used by
237 | * {Channels.play} and {Channels.fade_in} with **channels**==-1.
238 | *
239 | * @param num [Integer]
240 | * @return [Integer]
241 | */
242 | static VALUE Channels_s_reserve(VALUE self, VALUE num)
243 | {
244 | return INT2NUM(Mix_ReserveChannels(NUM2INT(num)));
245 | }
246 |
247 | /*
248 | * @overload volume(channel)
249 | * Get the volume of specified channel.
250 | *
251 | * @param channel [Integer] the channel to get volume for.
252 | * If the specified channel is -1, this method returns
253 | * the average volume of all channels.
254 | * @return [Integer] the volume, 0-128
255 | *
256 | * @see .set_volume
257 | */
258 | static VALUE Channels_s_volume(VALUE self, VALUE channel)
259 | {
260 | return INT2NUM(Mix_Volume(NUM2INT(channel), -1));
261 | }
262 |
263 | /*
264 | * @overload set_volume(channel, volume)
265 | * Set the volume of specified channel.
266 | *
267 | * The volume should be from 0 to {SDL2::Mixer::MAX_VOLUME}(128).
268 | * If the specified channel is -1, set volume for all channels.
269 | *
270 | * @param channel [Integer] the channel to set volume for.
271 | * @param volume [Integer] the volume to use
272 | * @return [void]
273 | *
274 | * @see .volume
275 | */
276 | static VALUE Channels_s_set_volume(VALUE self, VALUE channel, VALUE volume)
277 | {
278 | return INT2NUM(Mix_Volume(NUM2INT(channel), NUM2INT(volume)));
279 | }
280 |
281 | static void protect_playing_chunk_from_gc(int channel, VALUE chunk)
282 | {
283 | rb_ary_store(playing_chunks, channel, chunk);
284 | }
285 |
286 | /*
287 | * @overload play(channel, chunk, loops, ticks = -1)
288 | * Play a {SDL2::Mixer::Chunk} on **channel**.
289 | *
290 | * @param channel [Integer] the channel to play, or -1 for the first free unreserved
291 | * channel
292 | * @param chunk [SDL2::Mixer::Chunk] the chunk to play
293 | * @param loops [Integer] the number of loops, or -1 for infite loops.
294 | * passing 1 plays the sample twice (1 loop).
295 | * @param ticks [Integer] milliseconds limit to play, at most.
296 | * If the chunk is long enough and **loops** is large enough,
297 | * the play will stop after **ticks** milliseconds.
298 | * Otherwise, the play will stop when the loop ends.
299 | * -1 means infinity.
300 | * @return [Integer] the channel that plays the chunk.
301 | *
302 | * @raise [SDL2::Error] raised on a playing error. For example,
303 | * **channel** is out of the allocated channels, or
304 | * there is no free channels when **channel** is -1.
305 | *
306 | * @see .fade_in
307 | */
308 | static VALUE Channels_s_play(int argc, VALUE* argv, VALUE self)
309 | {
310 | VALUE channel, chunk, loops, ticks;
311 | int ch;
312 | rb_scan_args(argc, argv, "31", &channel, &chunk, &loops, &ticks);
313 | if (ticks == Qnil)
314 | ticks = INT2FIX(-1);
315 | check_channel(channel, 1);
316 | ch = Mix_PlayChannelTimed(NUM2INT(channel), Get_Mix_Chunk(chunk),
317 | NUM2INT(loops), NUM2INT(ticks));
318 | HANDLE_MIX_ERROR(ch);
319 | protect_playing_chunk_from_gc(ch, chunk);
320 | return INT2FIX(ch);
321 | }
322 |
323 | /*
324 | * @overload fade_in(channel, chunk, loops, ms, ticks = -1)
325 | * Play a {SDL2::Mixer::Chunk} on **channel** with fading in.
326 | *
327 | * @param channel [Integer] the channel to play, or -1 for the first free unreserved
328 | * channel
329 | * @param chunk [SDL2::Mixer::Chunk] the chunk to play
330 | * @param loops [Integer] the number of loops, or -1 for infite loops.
331 | * passing 1 plays the sample twice (1 loop).
332 | * @param ms [Integer] milliseconds of time of fade-in effect.
333 | * @param ticks [Integer] milliseconds limit to play, at most.
334 | * If the chunk is long enough and **loops** is large enough,
335 | * the play will stop after **ticks** milliseconds.
336 | * Otherwise, the play will stop when the loop ends.
337 | * -1 means infinity.
338 | * @return [Integer] the channel that plays the chunk.
339 | *
340 | * @raise [SDL2::Error] raised on a playing error. For example,
341 | * **channel** is out of the allocated channels, or
342 | * there is no free channels when **channel** is -1.
343 | *
344 | * @see .play
345 | * @see .fade_out
346 | */
347 | static VALUE Channels_s_fade_in(int argc, VALUE* argv, VALUE self)
348 | {
349 | VALUE channel, chunk, loops, ms, ticks;
350 | int ch;
351 | rb_scan_args(argc, argv, "41", &channel, &chunk, &loops, &ms, &ticks);
352 | if (ticks == Qnil)
353 | ticks = INT2FIX(-1);
354 | check_channel(channel, 1);
355 | ch = Mix_FadeInChannelTimed(NUM2INT(channel), Get_Mix_Chunk(chunk),
356 | NUM2INT(loops), NUM2INT(ms), NUM2INT(ticks));
357 | HANDLE_MIX_ERROR(ch);
358 | protect_playing_chunk_from_gc(ch, chunk);
359 | return INT2FIX(ch);
360 | }
361 |
362 | /*
363 | * @overload pause(channel)
364 | * Pause a specified channel.
365 | *
366 | * @param channel [Integer] the channel to pause, or -1 for all channels.
367 | * @return [nil]
368 | *
369 | * @see .resume
370 | * @see .pause?
371 | */
372 | static VALUE Channels_s_pause(VALUE self, VALUE channel)
373 | {
374 | check_channel(channel, 1);
375 | Mix_Pause(NUM2INT(channel));
376 | return Qnil;
377 | }
378 |
379 | /*
380 | * @overload resume(channel)
381 | * Resume a specified channel that already pauses.
382 | *
383 | * @note This method has no effect to unpaused channels.
384 | * @param channel [Integer] the channel to be resumed, or -1 for all channels.
385 | * @return [nil]
386 | *
387 | * @see .pause
388 | * @see .pause?
389 | */
390 | static VALUE Channels_s_resume(VALUE self, VALUE channel)
391 | {
392 | check_channel(channel, 1);
393 | Mix_Resume(NUM2INT(channel));
394 | return Qnil;
395 | }
396 |
397 | /*
398 | * @overload halt(channel)
399 | * Halt playing of a specified channel.
400 | *
401 | * @param channel [Integer] the channel to be halted, or -1 for all channels.
402 | * @return [nil]
403 | *
404 | * @see .expire
405 | * @see .fade_out
406 | * @see .play?
407 | */
408 | static VALUE Channels_s_halt(VALUE self, VALUE channel)
409 | {
410 | check_channel(channel, 1);
411 | Mix_HaltChannel(NUM2INT(channel));
412 | return Qnil;
413 | }
414 |
415 | /*
416 | * @overload expire(channel, ticks)
417 | * Halt playing of a specified channel after **ticks** milliseconds.
418 | *
419 | * @param channel [Integer] the channel to be halted, or -1 for all channels.
420 | * @param ticks [Integer] milliseconds untils the channel halts playback.
421 | * @return [nil]
422 | *
423 | * @see .halt
424 | * @see .fade_out
425 | * @see .play?
426 | */
427 | static VALUE Channels_s_expire(VALUE self, VALUE channel, VALUE ticks)
428 | {
429 | check_channel(channel, 1);
430 | Mix_ExpireChannel(NUM2INT(channel), NUM2INT(ticks));
431 | return Qnil;
432 | }
433 |
434 | /*
435 | * @overload fade_out(channel, ms)
436 | * Halt playing of a specified channel with fade-out effect.
437 | *
438 | * @param channel [Integer] the channel to be halted, or -1 for all channels.
439 | * @param ms [Integer] milliseconds of fade-out effect
440 | * @return [nil]
441 | *
442 | * @see .halt
443 | * @see .expire
444 | * @see .play?
445 | * @see .fade_in
446 | */
447 | static VALUE Channels_s_fade_out(VALUE self, VALUE channel, VALUE ms)
448 | {
449 | check_channel(channel, 1);
450 | Mix_FadeOutChannel(NUM2INT(channel), NUM2INT(ms));
451 | return Qnil;
452 | }
453 |
454 | /*
455 | * @overload play?(channel)
456 | * Return true if a specified channel is playing.
457 | *
458 | * @param channel [Integer] channel to test
459 | * @return [Boolean]
460 | *
461 | * @see .pause?
462 | * @see .fading
463 | */
464 | static VALUE Channels_s_play_p(VALUE self, VALUE channel)
465 | {
466 | check_channel(channel, 0);
467 | return INT2BOOL(Mix_Playing(NUM2INT(channel)));
468 | }
469 |
470 | /*
471 | * @overload pause?(channel)
472 | * Return true if a specified channel is paused.
473 | *
474 | * @note This method returns true if a paused channel is halted by {.halt}, or any
475 | * other halting methods.
476 | *
477 | * @param channel [Integer] channel to test
478 | * @return [Boolean]
479 | *
480 | * @see .play?
481 | * @see .fading
482 | */
483 | static VALUE Channels_s_pause_p(VALUE self, VALUE channel)
484 | {
485 | check_channel(channel, 0);
486 | return INT2BOOL(Mix_Paused(NUM2INT(channel)));
487 | }
488 |
489 | /*
490 | * @overload fading(channel)
491 | * Return the fading state of a specified channel.
492 | *
493 | * The return value is one of the following:
494 | *
495 | * * {SDL2::Mixer::NO_FADING} - **channel** is not fading in, and fading out
496 | * * {SDL2::Mixer::FADING_IN} - **channel** is fading in
497 | * * {SDL2::Mixer::FADING_OUT} - **channel** is fading out
498 | *
499 | * @param channel [Integer] channel to test
500 | *
501 | * @return [Integer]
502 | *
503 | * @see .play?
504 | * @see .pause?
505 | * @see .fade_in
506 | * @see .fade_out
507 | */
508 | static VALUE Channels_s_fading(VALUE self, VALUE which)
509 | {
510 | check_channel(which, 0);
511 | return INT2FIX(Mix_FadingChannel(NUM2INT(which)));
512 | }
513 |
514 | /*
515 | * @overload playing_chunk(channel)
516 | * Get the {SDL2::Mixer::Chunk} object most recently playing on **channel**.
517 | *
518 | * If **channel** is out of allocated channels, or
519 | * no chunk is played yet on **channel**, this method returns nil.
520 | *
521 | * @param channel [Integer] the channel to get the chunk object
522 | * @return [SDL2::Mixer::Chunk,nil]
523 | */
524 | static VALUE Channels_s_playing_chunk(VALUE self, VALUE channel)
525 | {
526 | check_channel(channel, 0);
527 | return rb_ary_entry(playing_chunks, NUM2INT(channel));
528 | }
529 |
530 | /*
531 | * Document-class: SDL2::Mixer::Channels::Group
532 | *
533 | * This class represents a channel group. A channel group is
534 | * a set of channels and you can stop playing and fade out playing
535 | * channels of an group at the same time.
536 | *
537 | * Each channel group is identified by an integer called tag.
538 | */
539 |
540 | /*
541 | * Initialize the channel with given **tag**.
542 | *
543 | * @param tag [Integer] channel indentifier
544 | *
545 | * Groups with a common tag are identified.
546 | */
547 | static VALUE Group_initialize(VALUE self, VALUE tag)
548 | {
549 | rb_iv_set(self, "@tag", tag);
550 | return Qnil;
551 | }
552 |
553 | /*
554 | * Get the default channel group.
555 | *
556 | * The default channel group refers all channels in the mixer system.
557 | *
558 | * @return [SDL2::Mixer::Channels::Group]
559 | */
560 | static VALUE Group_s_default(VALUE self)
561 | {
562 | VALUE tag = INT2FIX(-1);
563 | return rb_class_new_instance(1, &tag, self);
564 | }
565 |
566 | /*
567 | * Get the tag of the group.
568 | *
569 | * @return [Integer]
570 | */
571 | inline static int Group_tag(VALUE group)
572 | {
573 | return NUM2INT(rb_iv_get(group, "@tag"));
574 | }
575 |
576 | /*
577 | * @overload ==(other)
578 | * Return true if **self** and **other** are same.
579 | *
580 | * **self** and **other** are considered to be same
581 | * if they have the same tag.
582 | *
583 | * @param other [SDL2::Mixer::Channels::Group] a compared object
584 | * @return [Boolean]
585 | */
586 | static VALUE Group_eq(VALUE self, VALUE other)
587 | {
588 | return INT2BOOL(rb_obj_is_instance_of(other, cGroup) &&
589 | Group_tag(self) == Group_tag(other));
590 | }
591 |
592 | /*
593 | * @overload add(which)
594 | * Add a channel to the group.
595 | *
596 | * @param which [Integer] a channel id
597 | * @return [nil]
598 | */
599 | static VALUE Group_add(VALUE self, VALUE which)
600 | {
601 | if (!Mix_GroupChannel(NUM2INT(which), Group_tag(self))) {
602 | SDL_SetError("Cannot add channel %d", NUM2INT(which));
603 | SDL_ERROR();
604 | }
605 | return Qnil;
606 | }
607 |
608 | /*
609 | * Get the number of channels belong to the group.
610 | *
611 | * @return [Integer]
612 | */
613 | static VALUE Group_count(VALUE self)
614 | {
615 | return INT2NUM(Mix_GroupCount(Group_tag(self)));
616 | }
617 |
618 | /*
619 | * Return the first available channel in the group.
620 | *
621 | * Return -1 if no channel is available.
622 | *
623 | * @return [Integer]
624 | */
625 | static VALUE Group_available(VALUE self)
626 | {
627 | return INT2NUM(Mix_GroupAvailable(Group_tag(self)));
628 | }
629 |
630 | /*
631 | * Return the oldest cahnnel in the group.
632 | *
633 | * Return -1 if no channel is available.
634 | *
635 | * @return [Integer]
636 | */
637 | static VALUE Group_oldest(VALUE self)
638 | {
639 | return INT2NUM(Mix_GroupOldest(Group_tag(self)));
640 | }
641 |
642 | /*
643 | * Return the newer cahnnel in the group.
644 | *
645 | * Return -1 if no channel is available.
646 | *
647 | * @return [Integer]
648 | */
649 | static VALUE Group_newer(VALUE self)
650 | {
651 | return INT2NUM(Mix_GroupNewer(Group_tag(self)));
652 | }
653 |
654 | /*
655 | * @overload fade_out(ms)
656 | * Halt playing of all channels in the group with fade-out effect.
657 | *
658 | * @param ms [Integer] milliseconds of fade-out effect
659 | * @return [Integer] the number of channels affected by this method
660 | * @see Channels.fade_out
661 | * @see .halt
662 | */
663 | static VALUE Group_fade_out(VALUE self, VALUE ms)
664 | {
665 | return INT2NUM(Mix_FadeOutGroup(Group_tag(self), NUM2INT(ms)));
666 | }
667 |
668 | /*
669 | * Halt playing of all channels in the group.
670 | *
671 | * @return [nil]
672 | * @see Channels.halt
673 | * @see .fade_out
674 | */
675 | static VALUE Group_halt(VALUE self)
676 | {
677 | Mix_HaltGroup(Group_tag(self));
678 | return Qnil;
679 | }
680 |
681 | /*
682 | * Document-module: SDL2::Mixer::MusicChannel
683 | *
684 | * This module provides the functions to play {SDL2::Mixer::Music}.
685 | */
686 |
687 | /*
688 | * @overload play(music, loops)
689 | * Play **music** **loops** times.
690 | *
691 | * @note the meaning of **loop** is different from {SDL2::Mixer::Channels.play}.
692 | *
693 | * @param music [SDL2::Mixer::Music] music to play
694 | * @param loops [Integer] number of times to play the music.
695 | * 0 plays the music zero times.
696 | * -1 plays the music forever.
697 | *
698 | * @return [nil]
699 | *
700 | * @see .fade_in
701 | *
702 | */
703 | static VALUE MusicChannel_s_play(VALUE self, VALUE music, VALUE loops)
704 | {
705 | HANDLE_MIX_ERROR(Mix_PlayMusic(Get_Mix_Music(music), NUM2INT(loops)));
706 | playing_music = music;
707 | return Qnil;
708 | }
709 |
710 | /*
711 | * @overload fade_in(music, loops, ms, pos=0)
712 | * Play **music** **loops** times with fade-in effect.
713 | *
714 | * @note the meaning of **loop** is different from {SDL2::Mixer::Channels.play}.
715 | *
716 | * @param music [SDL2::Mixer::Music] music to play
717 | * @param loops [Integer] number of times to play the music.
718 | * 0 plays the music zero times.
719 | * -1 plays the music forever.
720 | * @param ms [Integer] milliseconds for the fade-in effect
721 | * @param pos [Float] the position to play from.
722 | * The meaning of "position" is different for the type of music sources.
723 | *
724 | * @return [nil]
725 | *
726 | * @see .play
727 | */
728 | static VALUE MusicChannel_s_fade_in(int argc, VALUE* argv, VALUE self)
729 | {
730 | VALUE music, loops, fade_in_ms, pos;
731 | rb_scan_args(argc, argv, "31", &music, &loops, &fade_in_ms, &pos);
732 | HANDLE_MIX_ERROR(Mix_FadeInMusicPos(Get_Mix_Music(music), NUM2INT(loops),
733 | NUM2INT(fade_in_ms),
734 | pos == Qnil ? 0 : NUM2DBL(pos)));
735 | playing_music = music;
736 | return Qnil;
737 | }
738 |
739 | /*
740 | * Get the volume of the music channel.
741 | *
742 | * @return [Integer]
743 | *
744 | * @see .volume=
745 | */
746 | static VALUE MusicChannel_s_volume(VALUE self)
747 | {
748 | return INT2FIX(Mix_VolumeMusic(-1));
749 | }
750 |
751 | /*
752 | * @overload volume=(vol)
753 | * Set the volume of the music channel.
754 | *
755 | * @param vol [Integer] the volume for mixing,
756 | * from 0 to {SDL2::Mixer::MAX_VOLUME}(128).
757 | * @return [void]
758 | *
759 | * @see .volume
760 | */
761 | static VALUE MusicChannel_s_set_volume(VALUE self, VALUE volume)
762 | {
763 | Mix_VolumeMusic(NUM2INT(volume));
764 | return volume;
765 | }
766 |
767 | /*
768 | * Pause the playback of the music channel.
769 | *
770 | * @return [nil]
771 | *
772 | * @see .resume
773 | * @see .pause?
774 | */
775 | static VALUE MusicChannel_s_pause(VALUE self)
776 | {
777 | Mix_PauseMusic(); return Qnil;
778 | }
779 |
780 | /*
781 | * Resume the playback of the music channel.
782 | *
783 | * @return [nil]
784 | *
785 | * @see .pause
786 | * @see .pause?
787 | */
788 | static VALUE MusicChannel_s_resume(VALUE self)
789 | {
790 | Mix_ResumeMusic(); return Qnil;
791 | }
792 |
793 | /*
794 | * Rewind the music to the start.
795 | *
796 | * @return [nil]
797 | */
798 | static VALUE MusicChannel_s_rewind(VALUE self)
799 | {
800 | Mix_RewindMusic(); return Qnil;
801 | }
802 |
803 | /*
804 | * @overload set_position(position)
805 | * Set the position of the currently playing music.
806 | *
807 | * @param position [Float] the position to play from.
808 | * @return [nil]
809 | */
810 | static VALUE MusicChannel_s_set_position(VALUE self, VALUE position)
811 | {
812 | HANDLE_MIX_ERROR(Mix_SetMusicPosition(NUM2DBL(position)));
813 | return Qnil;
814 | }
815 |
816 | /*
817 | * Halt the music playback.
818 | *
819 | * @return [nil]
820 | */
821 | static VALUE MusicChannel_s_halt(VALUE self)
822 | {
823 | Mix_HaltMusic(); return Qnil;
824 | }
825 |
826 | /*
827 | * @overload fade_out(ms)
828 | * Halt the music playback with fade-out effect.
829 | *
830 | * @param ms [Integer] milliseconds of fade-out effect
831 | * @return [nil]
832 | */
833 | static VALUE MusicChannel_s_fade_out(VALUE self, VALUE fade_out_ms)
834 | {
835 | Mix_FadeOutMusic(NUM2INT(fade_out_ms)); return Qnil;
836 | }
837 |
838 | /*
839 | * Return true if a music is playing.
840 | *
841 | * @return [Boolean]
842 | */
843 | static VALUE MusicChannel_s_play_p(VALUE self)
844 | {
845 | return INT2BOOL(Mix_PlayingMusic());
846 | }
847 |
848 | /*
849 | * Return true if a music playback is paused.
850 | *
851 | * @return [Boolean]
852 | */
853 | static VALUE MusicChannel_s_pause_p(VALUE self)
854 | {
855 | return INT2BOOL(Mix_PausedMusic());
856 | }
857 |
858 | /*
859 | * Get the fading state of the music playback.
860 | *
861 | * The return value is one of the following:
862 | *
863 | * * {SDL2::Mixer::NO_FADING} - not fading in, and fading out
864 | * * {SDL2::Mixer::FADING_IN} - fading in
865 | * * {SDL2::Mixer::FADING_OUT} - fading out
866 | *
867 | * @return [Integer]
868 | *
869 | * @see .fade_in
870 | * @see .fade_out
871 | *
872 | */
873 | static VALUE MusicChannel_s_fading(VALUE self)
874 | {
875 | return INT2NUM(Mix_FadingMusic());
876 | }
877 |
878 | /*
879 | * Get the {SDL2::Mixer::Music} object that most recently played.
880 | *
881 | * Return nil if no music object is played yet.
882 | *
883 | * @return [SDL2::Mixer::Music,nil]
884 | */
885 | static VALUE MusicChannel_s_playing_music(VALUE self)
886 | {
887 | return playing_music;
888 | }
889 |
890 | /*
891 | * Document-class: SDL2::Mixer::Chunk
892 | *
893 | * This class represents a sound sample, a kind of sound sources.
894 | *
895 | * Chunk objects is playable on {SDL2::Mixer::Channels}.
896 | *
897 | * @!method destroy?
898 | * Return true if the memory is deallocated by {#destroy}.
899 | */
900 |
901 | /*
902 | * @overload load(path)
903 | * Load a sample from file.
904 | *
905 | * This can load WAVE, AIFF, RIFF, OGG, and VOC files.
906 | *
907 | * @note {SDL2::Mixer.open} must be called before calling this method.
908 | *
909 | * @param path [String] the fine name
910 | * @return [SDL2::Mixer::Chunk]
911 | *
912 | * @raise [SDL2::Error] raised when failing to load
913 | */
914 | static VALUE Chunk_s_load(VALUE self, VALUE fname)
915 | {
916 | Mix_Chunk* chunk = Mix_LoadWAV(StringValueCStr(fname));
917 | VALUE c;
918 | if (!chunk)
919 | MIX_ERROR();
920 | c = Chunk_new(chunk);
921 | rb_iv_set(c, "@filename", fname);
922 | return c;
923 | }
924 |
925 | /*
926 | * Get the names of the sample decoders.
927 | *
928 | * @return [Array] the names of decoders, such as: "WAVE", "OGG", etc.
929 | */
930 | static VALUE Chunk_s_decoders(VALUE self)
931 | {
932 | int i;
933 | int num_decoders = Mix_GetNumChunkDecoders();
934 | VALUE ary = rb_ary_new();
935 | for (i=0; i < num_decoders; ++i)
936 | rb_ary_push(ary, rb_usascii_str_new_cstr(Mix_GetChunkDecoder(i)));
937 | return ary;
938 | }
939 |
940 | /*
941 | * Deallocate the sample memory.
942 | *
943 | * Normally, the memory is deallocated by ruby's GC, but
944 | * you can surely deallocate the memory with this method at any time.
945 | *
946 | * @return [nil]
947 | */
948 | static VALUE Chunk_destroy(VALUE self)
949 | {
950 | Chunk* c = Get_Chunk(self);
951 | if (c->chunk) Mix_FreeChunk(c->chunk);
952 | c->chunk = NULL;
953 | return Qnil;
954 | }
955 |
956 | /*
957 | * Get the volume of the sample.
958 | *
959 | * @return [Integer] the volume from 0 to {SDL2::Mixer::MAX_VOLUME}.
960 | *
961 | * @see #volume=
962 | */
963 | static VALUE Chunk_volume(VALUE self)
964 | {
965 | return INT2NUM(Mix_VolumeChunk(Get_Mix_Chunk(self), -1));
966 | }
967 |
968 | /*
969 | * @overload volume=(vol)
970 | * Set the volume of the sample.
971 | *
972 | * @param vol [Integer] the new volume
973 | * @return [void]
974 | *
975 | * @see #volume
976 | */
977 | static VALUE Chunk_set_volume(VALUE self, VALUE vol)
978 | {
979 | return INT2NUM(Mix_VolumeChunk(Get_Mix_Chunk(self), NUM2INT(vol)));
980 | }
981 |
982 | /* @return [String] inspection string */
983 | static VALUE Chunk_inspect(VALUE self)
984 | {
985 | VALUE filename = rb_iv_get(self, "@filename");
986 | if (RTEST(Chunk_destroy_p(self)))
987 | return rb_sprintf("<%s: destroyed>", rb_obj_classname(self));
988 |
989 | return rb_sprintf("<%s: filename=\"%s\" volume=%d>",
990 | rb_obj_classname(self),
991 | StringValueCStr(filename),
992 | Mix_VolumeChunk(Get_Mix_Chunk(self), -1));
993 | }
994 |
995 | /*
996 | * Document-class: SDL2::Mixer::Music
997 | *
998 | * This class represents music, a kind of sound sources.
999 | *
1000 | * Music is playable on {SDL2::Mixer::MusicChannel}, not on {SDL2::Mixer::Channels}.
1001 | *
1002 | * @!method destroy?
1003 | * Return true if the memory is deallocated by {#destroy}.
1004 | */
1005 |
1006 | /*
1007 | * Get the names of music decoders.
1008 | *
1009 | * @return [Array] the names of decorders (supported sound formats),
1010 | * such as: "OGG", "WAVE", "MP3"
1011 | */
1012 | static VALUE Music_s_decoders(VALUE self)
1013 | {
1014 | int num_decoders = Mix_GetNumMusicDecoders();
1015 | int i;
1016 | VALUE decoders = rb_ary_new2(num_decoders);
1017 | for (i=0; imusic);
1053 | c->music = NULL;
1054 | return Qnil;
1055 | }
1056 |
1057 | /* @return [String] inspection string */
1058 | static VALUE Music_inspect(VALUE self)
1059 | {
1060 | VALUE filename = rb_iv_get(self, "@filename");
1061 | if (RTEST(Music_destroy_p(self)))
1062 | return rb_sprintf("<%s: destroyed>", rb_obj_classname(self));
1063 |
1064 | return rb_sprintf("<%s: filename=\"%s\" type=%d>",
1065 | rb_obj_classname(self), StringValueCStr(filename),
1066 | Mix_GetMusicType(Get_Mix_Music(self)));
1067 | }
1068 |
1069 |
1070 | void rubysdl2_init_mixer(void)
1071 | {
1072 | mMixer = rb_define_module_under(mSDL2, "Mixer");
1073 |
1074 | rb_define_module_function(mMixer, "init", Mixer_s_init, 1);
1075 | rb_define_module_function(mMixer, "open", Mixer_s_open, -1);
1076 | rb_define_module_function(mMixer, "close", Mixer_s_close, 0);
1077 | rb_define_module_function(mMixer, "query", Mixer_s_query, 0);
1078 |
1079 | /* define(`DEFINE_MIX_INIT',`rb_define_const(mMixer, "INIT_$1", UINT2NUM(MIX_INIT_$1))') */
1080 | /* @return [Integer] bitmask which means initialization of Ogg flac loader */
1081 | DEFINE_MIX_INIT(FLAC);
1082 | /* @return [Integer] bitmask which means initialization of MOD loader */
1083 | DEFINE_MIX_INIT(MOD);
1084 | /* @return [Integer] bitmask which means initialization of MP3 loader */
1085 | DEFINE_MIX_INIT(MP3);
1086 | /* @return [Integer] bitmask which means initialization of Ogg vorbis loader */
1087 | DEFINE_MIX_INIT(OGG);
1088 |
1089 | #ifdef HAVE_CONST_MIX_INIT_MODPLUG
1090 | /* @return [Integer] bitmask which means initialization of libmodplug */
1091 | DEFINE_MIX_INIT(MODPLUG);
1092 | #endif
1093 | #ifdef HAVE_CONST_MIX_INIT_FLUIDSYNTH
1094 | /* @return [Integer] bitmask which means initialization of fluidsynth */
1095 | DEFINE_MIX_INIT(FLUIDSYNTH);
1096 | #endif
1097 | #ifdef HAVE_CONST_MIX_INIT_MID
1098 | /* @return [Integer] bitmask which means initialization of mid */
1099 | DEFINE_MIX_INIT(MID);
1100 | #endif
1101 |
1102 | /* define(`DEFINE_MIX_FORMAT',`rb_define_const(mMixer, "FORMAT_$1", UINT2NUM(AUDIO_$1))') */
1103 | /* @return [Integer] the value representing Unsiged 8-bit sample format. Used by {Mixer.open} */
1104 | DEFINE_MIX_FORMAT(U8);
1105 | /* @return [Integer] the value representing Siged 8-bit sample format. Used by {Mixer.open} */
1106 | DEFINE_MIX_FORMAT(S8);
1107 | /* @return [Integer] the value representing Unsiged 16-bit little-endian sample format. Used by {Mixer.open} */
1108 | DEFINE_MIX_FORMAT(U16LSB);
1109 | /* @return [Integer] the value representing Siged 16-bit little-endian sample format. Used by {Mixer.open} */
1110 | DEFINE_MIX_FORMAT(S16LSB);
1111 | /* @return [Integer] the value representing Unsiged 16-bit big-endian sample format. Used by {Mixer.open} */
1112 | DEFINE_MIX_FORMAT(U16MSB);
1113 | /* @return [Integer] the value representing Unsiged 16-bit big-endian sample format. Used by {Mixer.open} */
1114 | DEFINE_MIX_FORMAT(S16MSB);
1115 | /* @return [Integer] the value representing Unsiged 16-bit sample format. Endian is same as system byte order. Used by {Mixer.open} */
1116 | DEFINE_MIX_FORMAT(U16SYS);
1117 | /* @return [Integer] the value representing Siged 16-bit sample format. Endian is same as system byte order. Used by {Mixer.open} */
1118 | DEFINE_MIX_FORMAT(S16SYS);
1119 | /* @return [Integer] Default frequency. 22050 (Hz) */
1120 | rb_define_const(mMixer, "DEFAULT_FREQUENCY", UINT2NUM(MIX_DEFAULT_FREQUENCY));
1121 | /* @return [Integer] Default sample format. Same as {Mixer\:\:FORMAT_S16SYS}. */
1122 | rb_define_const(mMixer, "DEFAULT_FORMAT", UINT2NUM(MIX_DEFAULT_FORMAT));
1123 | /* @return [Integer] Default number of channels. 2. */
1124 | rb_define_const(mMixer, "DEFAULT_CHANNELS", INT2FIX(MIX_DEFAULT_CHANNELS));
1125 | /* @return [Integer] Max volume value. 128. */
1126 | rb_define_const(mMixer, "MAX_VOLUME", INT2FIX(MIX_MAX_VOLUME));
1127 | /* @return [Integer] the value represents that the channel is not fading in and fading out. */
1128 | rb_define_const(mMixer, "NO_FADING", INT2FIX(MIX_NO_FADING));
1129 | /* @return [Integer] the value represents that the channel is fading out. */
1130 | rb_define_const(mMixer, "FADING_OUT", INT2FIX(MIX_FADING_OUT));
1131 | /* @return [Integer] the value represents that the channel is fading in. */
1132 | rb_define_const(mMixer, "FADING_IN", INT2FIX(MIX_FADING_IN));
1133 |
1134 |
1135 | cChunk = rb_define_class_under(mMixer, "Chunk", rb_cObject);
1136 | rb_undef_alloc_func(cChunk);
1137 | rb_define_singleton_method(cChunk, "load", Chunk_s_load, 1);
1138 | rb_define_singleton_method(cChunk, "decoders", Chunk_s_decoders, 0);
1139 | rb_define_method(cChunk, "destroy", Chunk_destroy, 0);
1140 | rb_define_method(cChunk, "destroy?", Chunk_destroy_p, 0);
1141 | rb_define_method(cChunk, "volume", Chunk_volume, 0);
1142 | rb_define_method(cChunk, "volume=", Chunk_set_volume, 1);
1143 | rb_define_method(cChunk, "inspect", Chunk_inspect, 0);
1144 | /* @return [String] The file name of the file from which the sound is loaded. */
1145 | rb_define_attr(cChunk, "filename", 1, 0);
1146 |
1147 |
1148 | cMusic = rb_define_class_under(mMixer, "Music", rb_cObject);
1149 | rb_undef_alloc_func(cMusic);
1150 | rb_define_singleton_method(cMusic, "decoders", Music_s_decoders, 0);
1151 | rb_define_singleton_method(cMusic, "load", Music_s_load, 1);
1152 | rb_define_method(cMusic, "destroy", Music_destroy, 0);
1153 | rb_define_method(cMusic, "destroy?", Music_destroy_p, 0);
1154 | rb_define_method(cMusic, "inspect", Music_inspect, 0);
1155 |
1156 |
1157 | mChannels = rb_define_module_under(mMixer, "Channels");
1158 | rb_define_module_function(mChannels, "allocate", Channels_s_allocate, 1);
1159 | rb_define_module_function(mChannels, "reserve", Channels_s_reserve, 1);
1160 | rb_define_module_function(mChannels, "volume", Channels_s_volume, 1);
1161 | rb_define_module_function(mChannels, "set_volume", Channels_s_set_volume, 2);
1162 | rb_define_module_function(mChannels, "play", Channels_s_play, -1);
1163 | rb_define_module_function(mChannels, "fade_in", Channels_s_fade_in, -1);
1164 | rb_define_module_function(mChannels, "pause", Channels_s_pause, 1);
1165 | rb_define_module_function(mChannels, "resume", Channels_s_resume, 1);
1166 | rb_define_module_function(mChannels, "halt", Channels_s_halt, 1);
1167 | rb_define_module_function(mChannels, "expire", Channels_s_expire, 2);
1168 | rb_define_module_function(mChannels, "fade_out", Channels_s_fade_out, 2);
1169 | rb_define_module_function(mChannels, "play?", Channels_s_play_p, 1);
1170 | rb_define_module_function(mChannels, "pause?", Channels_s_pause_p, 1);
1171 | rb_define_module_function(mChannels, "fading", Channels_s_fading, 1);
1172 | rb_define_module_function(mChannels, "playing_chunk", Channels_s_playing_chunk, 1);
1173 |
1174 |
1175 | cGroup = rb_define_class_under(mChannels, "Group", rb_cObject);
1176 | rb_define_method(cGroup, "initialize", Group_initialize, 1);
1177 | rb_define_singleton_method(cGroup, "default", Group_s_default, 0);
1178 | /* @return [Integer] tag id */
1179 | rb_define_attr(cGroup, "tag", 1, 0);
1180 | rb_define_method(cGroup, "==", Group_eq, 1);
1181 | rb_define_method(cGroup, "add", Group_add, 1);
1182 | rb_define_method(cGroup, "count", Group_count, 0);
1183 | rb_define_method(cGroup, "available", Group_available, 0);
1184 | rb_define_method(cGroup, "newer", Group_newer, 0);
1185 | rb_define_method(cGroup, "oldest", Group_oldest, 0);
1186 | rb_define_method(cGroup, "fade_out", Group_fade_out, 1);
1187 | rb_define_method(cGroup, "halt", Group_halt, 0);
1188 |
1189 |
1190 | mMusicChannel = rb_define_module_under(mMixer, "MusicChannel");
1191 | rb_define_module_function(mMusicChannel, "play", MusicChannel_s_play, 2);
1192 | rb_define_module_function(mMusicChannel, "fade_in", MusicChannel_s_fade_in, -1);
1193 | rb_define_module_function(mMusicChannel, "volume", MusicChannel_s_volume, 0);
1194 | rb_define_module_function(mMusicChannel, "volume=", MusicChannel_s_set_volume, 1);
1195 | rb_define_module_function(mMusicChannel, "pause", MusicChannel_s_pause, 0);
1196 | rb_define_module_function(mMusicChannel, "resume", MusicChannel_s_resume, 0);
1197 | rb_define_module_function(mMusicChannel, "rewind", MusicChannel_s_rewind, 0);
1198 | rb_define_module_function(mMusicChannel, "set_position", MusicChannel_s_set_position, 1);
1199 | rb_define_module_function(mMusicChannel, "halt", MusicChannel_s_halt, 0);
1200 | rb_define_module_function(mMusicChannel, "fade_out", MusicChannel_s_fade_out, 1);
1201 | rb_define_module_function(mMusicChannel, "play?", MusicChannel_s_play_p, 0);
1202 | rb_define_module_function(mMusicChannel, "pause?", MusicChannel_s_pause_p, 0);
1203 | rb_define_module_function(mMusicChannel, "fading", MusicChannel_s_fading, 0);
1204 | rb_define_module_function(mMusicChannel, "playing_music", MusicChannel_s_playing_music, 0);
1205 |
1206 |
1207 | rb_gc_register_address(&playing_chunks);
1208 | rb_gc_register_address(&playing_music);
1209 | }
1210 |
1211 | #else /* HAVE_SDL_MIXER_H */
1212 | void rubysdl2_init_mixer(void)
1213 | {
1214 | }
1215 | #endif
1216 |
--------------------------------------------------------------------------------
/mouse.c:
--------------------------------------------------------------------------------
1 | #include "rubysdl2_internal.h"
2 | #include
3 | #include
4 |
5 | static VALUE mMouse;
6 | static VALUE cCursor;
7 | static VALUE cState;
8 |
9 | /*
10 | * Document-module: SDL2::Mouse
11 | *
12 | * This module has mouse input handling functions.
13 | *
14 | */
15 |
16 | /*
17 | * Document-class: SDL2::Mouse::Cursor
18 | *
19 | * This class represents mouse cursor shape, and
20 | * have some class-methods for a mouse cursor.
21 | */
22 |
23 | /*
24 | * Document-class: SDL2::Mouse::State
25 | *
26 | * This class represents mouse stetes.
27 | *
28 | * You can get a mouse state with {SDL2::Mouse.state}.
29 | *
30 | * @!attribute [r] x
31 | * the x coordinate of the mouse cursor.
32 | *
33 | * For {SDL2::Mouse.state}, this attribute means the x coordinate
34 | * relative to the window.
35 | * For {SDL2::Mouse.relative_state}, this attribute means the x coordinate
36 | * relative to the previous call of {SDL2::Mouse.relative_state}.
37 | *
38 | * @return [Integer]
39 | *
40 | * @!attribute [r] y
41 | * the y coordinate of the mouse cursor
42 | *
43 | * For {SDL2::Mouse.state}, this attribute means the y coordinate
44 | * relative to the window.
45 | * For {SDL2::Mouse.relative_state}, this attribute means the y coordinate
46 | * relative to the previous call of {SDL2::Mouse.relative_state}.
47 | *
48 | * @return [Integer]
49 | *
50 | * @!attribute [r] button_bits
51 | * button bits
52 | * @return [Integer]
53 | * @see #pressed?
54 | */
55 |
56 | static VALUE State_new(int x, int y, Uint32 buttons)
57 | {
58 | VALUE state = rb_obj_alloc(cState);
59 | rb_iv_set(state, "@x", INT2FIX(x));
60 | rb_iv_set(state, "@y", INT2FIX(y));
61 | rb_iv_set(state, "@button_bits", UINT2NUM(buttons));
62 | return state;
63 | }
64 |
65 | static VALUE mouse_state(Uint32 (*func)(int*,int*))
66 | {
67 | int x, y;
68 | Uint32 state;
69 | state = func(&x, &y);
70 | return State_new(x, y, state);
71 | }
72 |
73 | #if SDL_VERSION_ATLEAST(2,0,4)
74 | /*
75 | * Get the current mouse state in relation to the desktop.
76 | *
77 | * The return value contains the x and y coordinates of the cursor
78 | * relative to the desktop and the state of mouse buttons.
79 | *
80 | * @note This module function is available since SDL 2.0.4.
81 | * @return [SDL2::Mouse::State] current state
82 | */
83 | static VALUE Mouse_s_global_state(VALUE self)
84 | {
85 | return mouse_state(SDL_GetGlobalMouseState);
86 | }
87 | #endif
88 |
89 | /*
90 | * Get the current state of the mouse.
91 | *
92 | * The return value contains the x and y coordinates of the cursor and
93 | * the state of mouse buttons.
94 | *
95 | * @return [SDL2::Mouse::State] current state
96 | */
97 | static VALUE Mouse_s_state(VALUE self)
98 | {
99 | return mouse_state(SDL_GetMouseState);
100 | }
101 |
102 | /*
103 | * Return true if relative mouse mode is enabled.
104 | *
105 | * @see .relative_mode=
106 | */
107 | static VALUE Mouse_s_relative_mode_p(VALUE self)
108 | {
109 | return INT2BOOL(SDL_GetRelativeMouseMode());
110 | }
111 |
112 | /*
113 | * @overload relative_mode=(bool)
114 | * @param [Boolean] bool true if you want to enable relative mouse mode
115 | *
116 | * Set relative mouse mode.
117 | *
118 | * While the mouse is in relative mode, the cursor is hidden, and the
119 | * driver will try to report continuous motion in the current window.
120 | * Only relative motion events will be delivered, the mouse position
121 | * will not change.
122 | *
123 | * @note This function will flush any pending mouse motion.
124 | *
125 | * @return [Boolean]
126 | * @see .relative_mode?
127 | */
128 | static VALUE Mouse_s_set_relative_mode(VALUE self, VALUE enabled)
129 | {
130 | HANDLE_ERROR(SDL_SetRelativeMouseMode(RTEST(enabled)));
131 | return enabled;
132 | }
133 |
134 | /*
135 | * Get the relative state of the mouse.
136 | *
137 | * The button state is same as {.state} and
138 | * x and y of the returned object are set to the
139 | * mouse deltas since the last call to this method.
140 | *
141 | * @return [SDL2::Mouse::State]
142 | */
143 | static VALUE Mouse_s_relative_state(VALUE self)
144 | {
145 | return mouse_state(SDL_GetRelativeMouseState);
146 | }
147 |
148 | /*
149 | * Get the window which has mouse focus.
150 | *
151 | * @return [SDL2::Window] the window which has mouse focus
152 | * @return [nil] if no window governed by SDL has mouse focus
153 | */
154 | static VALUE Mouse_s_focused_window(VALUE self)
155 | {
156 | SDL_Window* window = SDL_GetMouseFocus();
157 | if (!window)
158 | return Qnil;
159 | else
160 | return find_window_by_id(SDL_GetWindowID(window));
161 | }
162 |
163 | /*
164 | * Show the mouse cursor.
165 | * @return [nil]
166 | */
167 | static VALUE Cursor_s_show(VALUE self)
168 | {
169 | HANDLE_ERROR(SDL_ShowCursor(SDL_ENABLE));
170 | return Qnil;
171 | }
172 |
173 | /*
174 | * Hide the mouse cursor.
175 | * @return [nil]
176 | */
177 | static VALUE Cursor_s_hide(VALUE self)
178 | {
179 | HANDLE_ERROR(SDL_ShowCursor(SDL_DISABLE));
180 | return Qnil;
181 | }
182 |
183 | /*
184 | * Return true if the mouse cursor is shown.
185 | */
186 | static VALUE Cursor_s_shown_p(VALUE self)
187 | {
188 | return INT2BOOL(HANDLE_ERROR(SDL_ShowCursor(SDL_QUERY)));
189 | }
190 |
191 | /*
192 | * @overload warp(window, x, y)
193 | * Move the mouse cursor to the given position within the window.
194 | *
195 | * @param [SDL2::Window] window the window to move the mouse cursor into
196 | * @param [Integer] x the x coordinate within the window
197 | * @param [Integer] y the y coordinate within the window
198 | * @return [nil]
199 | */
200 | static VALUE Cursor_s_warp(VALUE self, VALUE window, VALUE x, VALUE y)
201 | {
202 | SDL_WarpMouseInWindow(Get_SDL_Window(window), NUM2INT(x), NUM2INT(y));
203 | return Qnil;
204 | }
205 |
206 | #if SDL_VERSION_ATLEAST(2,0,4)
207 | /*
208 | * @overload warp_globally(x, y)
209 | * Move the mouse cursor to the given position within the desktop.
210 | *
211 | * @note This class module function is available since SDL 2.0.4.
212 | * @param [Integer] x the x coordinate within the desktop
213 | * @param [Integer] y the y coordinate within the desktop
214 | * @return [nil]
215 | */
216 | static VALUE Cursor_s_warp_globally(VALUE self, VALUE x, VALUE y)
217 | {
218 | SDL_WarpMouseGlobal(NUM2INT(x), NUM2INT(y));
219 | return Qnil;
220 | }
221 | #endif
222 |
223 | /*
224 | * @overload pressed?(index)
225 | * Return true a mouse button is pressed.
226 | *
227 | * @param [Integer] index the index of a mouse button, start at index 0
228 | * @return [Boolean]
229 | */
230 | static VALUE State_pressed_p(VALUE self, VALUE index)
231 | {
232 | int idx = NUM2INT(index);
233 | if (idx < 0 || idx >= 32)
234 | rb_raise(rb_eArgError, "button index out of range (%d for 1..32)", idx);
235 | return INT2BOOL(NUM2UINT(rb_iv_get(self, "@button_bits")) & SDL_BUTTON(idx));
236 | }
237 |
238 | /* @return [String] inspection string */
239 | static VALUE State_inspect(VALUE self)
240 | {
241 | int i;
242 | Uint32 buttons = NUM2UINT(rb_iv_get(self, "@button_bits"));
243 | VALUE pressed = rb_ary_new();
244 | VALUE string_for_pressed;
245 | for (i=1; i<=32; ++i)
246 | if (buttons & SDL_BUTTON(i))
247 | rb_ary_push(pressed, rb_sprintf("%d", i));
248 |
249 | string_for_pressed = rb_ary_join(pressed, rb_str_new2(" "));
250 | return rb_sprintf("<%s:%p x=%d y=%d pressed=[%s]>",
251 | rb_obj_classname(self), (void*)self,
252 | NUM2INT(rb_iv_get(self, "@x")), NUM2INT(rb_iv_get(self, "@y")),
253 | StringValueCStr(string_for_pressed));
254 | }
255 |
256 | void rubysdl2_init_mouse(void)
257 | {
258 | mMouse = rb_define_module_under(mSDL2, "Mouse");
259 |
260 | #if SDL_VERSION_ATLEAST(2,0,4)
261 | rb_define_module_function(mMouse, "global_state", Mouse_s_global_state, 0);
262 | #endif
263 | rb_define_module_function(mMouse, "state", Mouse_s_state, 0);
264 | rb_define_module_function(mMouse, "relative_mode?", Mouse_s_relative_mode_p, 0);
265 | rb_define_module_function(mMouse, "relative_mode=", Mouse_s_set_relative_mode, 1);
266 | rb_define_module_function(mMouse, "relative_state", Mouse_s_relative_state, 0);
267 | rb_define_module_function(mMouse, "focused_window", Mouse_s_focused_window, 0);
268 |
269 | cCursor = rb_define_class_under(mMouse, "Cursor", rb_cObject);
270 |
271 | rb_define_singleton_method(cCursor, "show", Cursor_s_show, 0);
272 | rb_define_singleton_method(cCursor, "hide", Cursor_s_hide, 0);
273 | rb_define_singleton_method(cCursor, "shown?", Cursor_s_shown_p, 0);
274 | rb_define_singleton_method(cCursor, "warp", Cursor_s_warp, 3);
275 | #if SDL_VERSION_ATLEAST(2,0,4)
276 | rb_define_singleton_method(cCursor, "warp_globally", Cursor_s_warp_globally, 2);
277 | #endif
278 |
279 |
280 | cState = rb_define_class_under(mMouse, "State", rb_cObject);
281 |
282 | rb_undef_method(rb_singleton_class(cState), "new");
283 | define_attr_readers(cState, "x", "y", "button_bits", NULL);
284 | rb_define_method(cState, "pressed?", State_pressed_p, 1);
285 | rb_define_method(cState, "inspect", State_inspect, 0);
286 | }
287 |
--------------------------------------------------------------------------------
/rbs_collection.yaml:
--------------------------------------------------------------------------------
1 | # Download sources
2 | sources:
3 | - type: git
4 | name: ruby/gem_rbs_collection
5 | remote: https://github.com/ruby/gem_rbs_collection.git
6 | revision: main
7 | repo_dir: gems
8 |
9 | # You can specify local directories as sources also.
10 | # - type: local
11 | # path: path/to/your/local/repository
12 |
13 | # A directory to install the downloaded RBSs
14 | path: .gem_rbs_collection
15 |
16 | gems:
17 | # Skip loading rbs gem's RBS.
18 | # It's unnecessary if you don't use rbs as a library.
19 | - name: rbs
20 | ignore: true
21 | - name: yard
22 | ignore: true
23 | - name: rake
24 | ignore: true
25 | - name: sord
26 | ignore: true
27 | - name: ast
28 | ignore: true
29 | - name: rainbow
30 | ignore: true
31 | - name: steep
32 | ignore: true
33 |
--------------------------------------------------------------------------------
/rubysdl2_internal.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Ruby/SDL2 Ruby extensiion library for SDL 2.x
3 | *
4 | * Copyright (C) 2014 Ippei Obayashi
5 | *
6 | * This file is part of Ruby/SDL2.
7 | *
8 | * Ruby/SDL2 is free software: you can redistribute it and/or modify
9 | * it under the terms of the GNU Lesser General Public License as published by
10 | * the Free Software Foundation, either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Ruby/SDL2 is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU Lesser General Public License for more details.
17 | *
18 | * You should have received a copy of the GNU Lesser General Public License
19 | * along with Ruby/SDL2. If not, see .
20 | */
21 | #ifndef RUBYSDL2_INTERNAL_H
22 | #define RUBYSDL2_INTERNAL_H
23 |
24 | #include
25 | #include
26 | #define SDL_MAIN_HANDLED
27 | #include
28 | #include
29 | #include
30 |
31 | #ifndef SDL2_EXTERN
32 | #define SDL2_EXTERN extern
33 | #endif
34 |
35 | /** utility functions */
36 | int rubysdl2_handle_error(int code, const char* cfunc);
37 | int rubysdl2_is_active(void);
38 | void rubysdl2_define_attr_readers(VALUE klass, ...);
39 | VALUE rubysdl2_utf8str_new_cstr(const char* str);
40 | VALUE rubysdl2_Surface_new(SDL_Surface* surface);
41 | SDL_Color rubysdl2_Array_to_SDL_Color(VALUE ary);
42 | VALUE rubysdl2_SDL_version_to_String(const SDL_version* ver);
43 | VALUE rubysdl2_SDL_version_to_Array(const SDL_version* ver);
44 | VALUE rubysdl2_find_window_by_id(Uint32 id);
45 | SDL_Rect* rubysdl2_Get_SDL_Rect(VALUE);
46 | SDL_Window* rubysdl2_Get_SDL_Window(VALUE);
47 | const char* rubysdl2_INT2BOOLCSTR(int);
48 |
49 | /** initialize interfaces */
50 | void rubysdl2_init_hints(void);
51 | void rubysdl2_init_video(void);
52 | void rubysdl2_init_gl(void);
53 | void rubysdl2_init_messagebox(void);
54 | void rubysdl2_init_event(void);
55 | void rubysdl2_init_key(void);
56 | void rubysdl2_init_mouse(void);
57 | void rubysdl2_init_joystick(void);
58 | void rubysdl2_init_timer(void);
59 | void rubysdl2_init_image(void);
60 | void rubysdl2_init_mixer(void);
61 | void rubysdl2_init_ttf(void);
62 | void rubysdl2_init_filesystem(void);
63 | void rubysdl2_init_clipboard(void);
64 | void rubysdl2_init_gamecontorller(void);
65 |
66 | /** macros */
67 | #define HANDLE_ERROR(c) (rubysdl2_handle_error((c), __func__))
68 | #define SDL_ERROR() (HANDLE_ERROR(-1))
69 | #define INT2BOOL(x) ((x)?Qtrue:Qfalse)
70 | #define NUM2UCHAR NUM2UINT
71 | #define UCHAR2NUM UINT2NUM
72 | #define rb_str_export_to_utf8(str) rb_str_export_to_enc((str), rb_utf8_encoding())
73 |
74 | #define DEFINE_GETTER(scope, ctype, var_class, classname) \
75 | scope ctype* Get_##ctype(VALUE obj) \
76 | { \
77 | ctype* s; \
78 | if (!rb_obj_is_kind_of(obj, var_class)) \
79 | rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)", \
80 | rb_obj_classname(obj), classname); \
81 | Data_Get_Struct(obj, ctype, s); \
82 | \
83 | return s; \
84 | }
85 |
86 | #define DEFINE_WRAP_GETTER(scope, SDL_typename, struct_name, field, classname) \
87 | scope SDL_typename* Get_##SDL_typename(VALUE obj) \
88 | { \
89 | struct_name* s = Get_##struct_name(obj); \
90 | if (s->field == NULL) \
91 | HANDLE_ERROR(SDL_SetError(classname " is already destroyed")); \
92 | \
93 | return s->field; \
94 | }
95 |
96 | #define DEFINE_DESTROY_P(scope, struct_name, field) \
97 | scope VALUE struct_name##_destroy_p(VALUE self) \
98 | { \
99 | return INT2BOOL(Get_##struct_name(self)->field == NULL); \
100 | }
101 |
102 | #define DEFINE_WRAPPER(SDL_typename, struct_name, field, var_class, classname) \
103 | DEFINE_GETTER(static, struct_name, var_class, classname); \
104 | DEFINE_WRAP_GETTER(static ,SDL_typename, struct_name, field, classname); \
105 | DEFINE_DESTROY_P(static, struct_name, field);
106 |
107 | /** classes and modules */
108 | SDL2_EXTERN VALUE rubysdl2_mSDL2;
109 | SDL2_EXTERN VALUE rubysdl2_eSDL2Error;
110 |
111 | /** prefix macros */
112 | #define define_attr_readers rubysdl2_define_attr_readers
113 | #define utf8str_new_cstr rubysdl2_utf8str_new_cstr
114 | #define Surface_new rubysdl2_Surface_new
115 | #define Get_SDL_Rect rubysdl2_Get_SDL_Rect
116 | #define Get_SDL_Window rubysdl2_Get_SDL_Window
117 | #define Array_to_SDL_Color rubysdl2_Array_to_SDL_Color
118 | #define mSDL2 rubysdl2_mSDL2
119 | #define eSDL2Error rubysdl2_eSDL2Error
120 | #define SDL_version_to_String rubysdl2_SDL_version_to_String
121 | #define SDL_version_to_Array rubysdl2_SDL_version_to_Array
122 | #define INT2BOOLCSTR rubysdl2_INT2BOOLCSTR
123 | #define find_window_by_id rubysdl2_find_window_by_id
124 |
125 | #endif
126 |
--------------------------------------------------------------------------------
/sample/chunk_destroy.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | SDL2::init(SDL2::INIT_AUDIO)
4 |
5 | SDL2::Mixer.init(SDL2::Mixer::INIT_FLAC|SDL2::Mixer::INIT_MP3|SDL2::Mixer::INIT_OGG)
6 |
7 |
8 | SDL2::Mixer.open(22050, SDL2::Mixer::DEFAULT_FORMAT, 2, 512)
9 |
10 | chunk = SDL2::Mixer::Chunk.load(ARGV[0])
11 | p chunk; chunk.destroy; p chunk; p chunk.destroy?
12 |
13 | mus = SDL2::Mixer::Music.load(ARGV[0])
14 | p mus; mus.destroy; p mus; p mus.destroy?
15 |
16 | GC.start
17 |
--------------------------------------------------------------------------------
/sample/icon.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ohai/ruby-sdl2/6ebccec7072bb8286d8020c49d2e099142402c6c/sample/icon.bmp
--------------------------------------------------------------------------------
/sample/memory_test/m1.rb:
--------------------------------------------------------------------------------
1 | require "sdl2"
2 |
3 | SDL2.init(SDL2::INIT_VIDEO|SDL2::INIT_EVENTS)
4 |
5 | window = SDL2::Window.create("testsprite",
6 | SDL2::Window::POS_CENTERED, SDL2::Window::POS_CENTERED,
7 | 640, 480, 0)
8 |
9 | def f2(renderer)
10 | surface = SDL2::Surface.load_bmp("icon.bmp")
11 | 10.times { renderer.create_texture_from(surface) }
12 | p renderer.debug_info
13 | end
14 |
15 | def f1(window)
16 | renderer = window.create_renderer(-1, 0)
17 | p window.debug_info
18 | p renderer.debug_info
19 | f2(renderer)
20 | GC.start
21 | p renderer.debug_info
22 | end
23 |
24 | f1(window)
25 | GC.start
26 | p window.debug_info
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sample/memory_test/m2.rb:
--------------------------------------------------------------------------------
1 | require "sdl2"
2 |
3 | SDL2.init(SDL2::INIT_VIDEO|SDL2::INIT_EVENTS)
4 |
5 | $window = SDL2::Window.create("testsprite", 0, 0, 128, 128, 0)
6 |
7 | def f
8 | renderer = $window.create_renderer(-1, 0)
9 | surface = SDL2::Surface.load_bmp("icon.bmp")
10 | a = Array.new(10) { renderer.create_texture_from(surface) }
11 | p a.map{|tex| tex.debug_info }
12 | p renderer.debug_info
13 | a
14 | end
15 |
16 | a = f
17 | GC.start
18 | p a.map{|tex| tex.debug_info }
19 |
--------------------------------------------------------------------------------
/sample/memory_test/m3.rb:
--------------------------------------------------------------------------------
1 | require "sdl2"
2 |
3 | SDL2.init(SDL2::INIT_VIDEO|SDL2::INIT_EVENTS)
4 |
5 | window = SDL2::Window.create("testsprite",
6 | SDL2::Window::POS_CENTERED, SDL2::Window::POS_CENTERED,
7 | 640, 480, 0)
8 | renderer = window.create_renderer(-1, 0)
9 | surface = SDL2::Surface.load_bmp("icon.bmp")
10 | 10.times { renderer.create_texture_from(surface) }
11 |
12 | window.destroy
13 |
--------------------------------------------------------------------------------
/sample/message_box.rb:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | require 'sdl2'
3 |
4 | SDL2::MessageBox.show_simple_box(SDL2::MessageBox::ERROR, "Error",
5 | "This is the error message box", nil)
6 |
7 | button = SDL2::MessageBox.show(flags: SDL2::MessageBox::WARNING,
8 | window: nil,
9 | title: "警告ウインドウ",
10 | message: "ここに警告文が出ます",
11 | buttons: [ { # flags is ignored
12 | id: 0,
13 | text: "いいえ",
14 | },
15 | {flags: SDL2::MessageBox::BUTTON_RETURNKEY_DEFAULT,
16 | id: 1,
17 | text: "はい",
18 | },
19 | {flags: SDL2::MessageBox::BUTTON_ESCAPEKEY_DEFAULT,
20 | id: 2,
21 | text: "キャンセル",
22 | },
23 | ],
24 | color_scheme: {
25 | bg: [255, 0, 0],
26 | text: [0, 255, 0],
27 | button_border: [255, 0, 0],
28 | button_bg: [0, 0, 255],
29 | button_selected: [255, 0, 0]
30 | }
31 | )
32 | p button
33 |
34 |
--------------------------------------------------------------------------------
/sample/music_player.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | class TextureForText
4 | def initialize(renderer, font)
5 | @renderer = renderer
6 | @text_surfaces = {}
7 | @font = font
8 | @texture = nil
9 | end
10 |
11 | def add_text(text)
12 | @text_surfaces[text] = @font.render_blended(text, [0xff, 0xff, 0xff])
13 | end
14 |
15 | def creat_texture!
16 | @text_rects = Hash.new
17 | h = @text_surfaces.inject(0){|y, (text, surface)|
18 | @text_rects[text] = SDL2::Rect[0, y, surface.w, surface.h]
19 | y + surface.h
20 | }
21 | w = @text_rects.values.map(&:w).max
22 |
23 | tmp_surface = SDL2::Surface.new(w, h, 32)
24 | @text_surfaces.each do |text, surface|
25 | SDL2::Surface.blit(surface, nil, tmp_surface, @text_rects[text])
26 | end
27 | @texture = @renderer.create_texture_from(tmp_surface)
28 | tmp_surface.destroy
29 |
30 | @text_surfaces = nil
31 | end
32 |
33 | def box_for(text)
34 | @text_rects[text]
35 | end
36 |
37 | def render(text, dst)
38 | srcrect = @text_rects[text]
39 | dst = SDL2::Rect[dst.x, dst.y, srcrect.w, srcrect.h] if dst.is_a?(SDL2::Point)
40 | @renderer.copy(@texture, srcrect, dst)
41 | end
42 |
43 | attr_reader :texture
44 | end
45 |
46 | class MusicPlayer
47 | def run(argv)
48 | file = argv.shift
49 |
50 | SDL2.init(SDL2::INIT_EVERYTHING)
51 | SDL2::Mixer.init(SDL2::Mixer::INIT_FLAC|SDL2::Mixer::INIT_MODPLUG|
52 | SDL2::Mixer::INIT_MP3|SDL2::Mixer::INIT_OGG)
53 | SDL2::Mixer.open(44100, SDL2::Mixer::DEFAULT_FORMAT, 2, 512)
54 | SDL2::TTF.init
55 |
56 | @window = SDL2::Window.create("music player sample by Ruby/SDL2", 0, 0, 640, 480, 0)
57 | @renderer = @window.create_renderer(-1, 0)
58 | @music = SDL2::Mixer::Music.load(file)
59 | font = SDL2::TTF.open("font.ttf", 24)
60 |
61 | @texts = TextureForText.new(@renderer, font)
62 | @texts.add_text(file)
63 | @texts.add_text("playing")
64 | @texts.add_text("playing (fade in)")
65 | @texts.add_text("stopping")
66 | @texts.add_text("fade out")
67 | @texts.add_text("pause")
68 | @texts.creat_texture!
69 |
70 | loop do
71 | while event = SDL2::Event.poll
72 | handle_event(event)
73 | end
74 |
75 | @renderer.draw_color = [0, 0, 0]
76 | @renderer.clear
77 |
78 | @texts.render(file, SDL2::Point[20, 20])
79 | @texts.render(playing_state, SDL2::Point[20, 120])
80 |
81 | @renderer.present
82 | sleep 0.01
83 | end
84 | end
85 |
86 | def handle_event(event)
87 | case event
88 | when SDL2::Event::Quit
89 | exit
90 | when SDL2::Event::KeyDown
91 | keydown(event)
92 | end
93 | end
94 |
95 | def keydown(event)
96 | match_keydown("ESCAPE", event){ exit }
97 | match_keydown("p", event) { SDL2::Mixer::MusicChannel.play(@music, 1) }
98 | match_keydown("h", event) { SDL2::Mixer::MusicChannel.halt }
99 | match_keydown("r", event) { SDL2::Mixer::MusicChannel.rewind }
100 | match_keydown("o", event) { SDL2::Mixer::MusicChannel.fade_out(1000) }
101 | match_keydown("i", event) { SDL2::Mixer::MusicChannel.fade_in(@music, 1, 1000) }
102 | match_keydown("SPACE", event) {
103 | if SDL2::Mixer::MusicChannel.pause?
104 | SDL2::Mixer::MusicChannel.resume
105 | else
106 | SDL2::Mixer::MusicChannel.pause
107 | end
108 | }
109 | end
110 |
111 | def match_keydown(key, event)
112 | sym = SDL2::Key.keycode_from_name(key)
113 | yield if event.sym == sym
114 | end
115 |
116 | def playing_state
117 | return "stopping" if !SDL2::Mixer::MusicChannel.play?
118 | return "pause" if SDL2::Mixer::MusicChannel.pause?
119 | case SDL2::Mixer::MusicChannel.fading
120 | when SDL2::Mixer::NO_FADING
121 | return "playing"
122 | when SDL2::Mixer::FADING_IN
123 | return "playing (fade in)"
124 | when SDL2::Mixer::FADING_OUT
125 | return "fade out"
126 | end
127 | end
128 | end
129 |
130 |
131 | MusicPlayer.new.run(ARGV)
132 |
133 |
--------------------------------------------------------------------------------
/sample/playwave.rb:
--------------------------------------------------------------------------------
1 | # ruby playwave.rb sample.wav
2 | require 'sdl2'
3 |
4 | SDL2::init(SDL2::INIT_AUDIO)
5 |
6 | SDL2::Mixer.init(SDL2::Mixer::INIT_FLAC|SDL2::Mixer::INIT_MP3|SDL2::Mixer::INIT_OGG)
7 | SDL2::Mixer.open(22050, SDL2::Mixer::DEFAULT_FORMAT, 2, 512)
8 |
9 | p SDL2::Mixer::Chunk.decoders
10 | p SDL2::Mixer::Music.decoders
11 | wave = SDL2::Mixer::Chunk.load(ARGV[0])
12 |
13 | SDL2::Mixer::Channels.fade_in(0, wave, 0, 600)
14 |
15 | while SDL2::Mixer::Channels.play?(0)
16 | sleep 1
17 | end
18 |
19 |
20 |
--------------------------------------------------------------------------------
/sample/primitives.rb:
--------------------------------------------------------------------------------
1 | require "sdl2"
2 |
3 | SDL2.init(SDL2::INIT_VIDEO|SDL2::INIT_EVENTS)
4 |
5 | window = SDL2::Window.create("testsprite",
6 | SDL2::Window::POS_CENTERED, SDL2::Window::POS_CENTERED,
7 | 640, 480, 0)
8 | renderer = window.create_renderer(-1, 0)
9 |
10 | renderer.draw_color = [255, 0, 0, 255]
11 | renderer.draw_line(0,0,640,480)
12 | renderer.draw_color = [255, 255, 0]
13 | renderer.draw_point(200, 200)
14 | renderer.draw_color = [0, 255, 255]
15 | renderer.draw_rect(SDL2::Rect.new(500, 20, 40, 60))
16 | renderer.fill_rect(SDL2::Rect.new(20, 400, 60, 40))
17 | renderer.draw_blend_mode = SDL2::BlendMode::ADD
18 | renderer.draw_color = [255, 0, 0]
19 | renderer.draw_rect(SDL2::Rect.new(40, 420, 60, 40))
20 |
21 | renderer.present
22 |
23 | loop do
24 | while ev = SDL2::Event.poll
25 | if SDL2::Event::KeyDown === ev && ev.scancode == SDL2::Key::Scan::ESCAPE
26 | exit
27 | end
28 | end
29 |
30 | renderer.present
31 | sleep 0.1
32 | end
33 |
--------------------------------------------------------------------------------
/sample/test_clipboard.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | SDL2.init(SDL2::INIT_EVERYTHING)
4 | window = SDL2::Window.create("clipboard", 0, 0, 640, 480, 0)
5 |
6 | # you can call clipboard functions after creating window (on X11)
7 | p SDL2::Clipboard.has_text?
8 | p SDL2::Clipboard.text
9 | # text= is not reliable on X11 environment
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sample/test_controller.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | SDL2.init(SDL2::INIT_EVERYTHING)
4 |
5 |
6 | p SDL2::GameController.axis_name_of(SDL2::GameController::Axis::LEFTY)
7 | p SDL2::GameController.axis_name_of(129) rescue p $!
8 | p SDL2::GameController.axis_from_name("rightx")
9 | p SDL2::GameController.button_name_of(SDL2::GameController::Button::Y)
10 | p SDL2::GameController.button_from_name("x")
11 | p SDL2::GameController.button_from_name("rightx") rescue p $!
12 |
13 | if SDL2::Joystick.num_connected_joysticks == 0
14 | puts "You need to connect at least one joystick"
15 | exit
16 | end
17 |
18 | joystick = SDL2::Joystick.open(0)
19 | guid = joystick.GUID
20 | joystick.close
21 | SDL2::GameController.add_mapping([guid, "No1",
22 | "leftx:a0,lefty:a1",
23 | "a:b3,b:b2,x:b1,y:b0"
24 | ].join(","))
25 |
26 |
27 | p SDL2::Joystick.game_controller?(0)
28 | p SDL2::GameController.device_names
29 | controller = SDL2::GameController.open(0)
30 | p controller.name
31 | p controller.attached?
32 | p controller.mapping
33 |
34 | $window = SDL2::Window.create("test_controller", 0,0,640,480,0)
35 |
36 | loop do
37 | while event = SDL2::Event.poll
38 | case event
39 | when SDL2::Event::Quit
40 | exit
41 | when SDL2::Event::KeyDown
42 | exit if event.sym == SDL2::Key::ESCAPE
43 | when SDL2::Event::ControllerAxisMotion
44 | p event
45 | p controller.axis(SDL2::GameController::Axis::LEFTX)
46 | p controller.axis(SDL2::GameController::Axis::LEFTY)
47 | when SDL2::Event::ControllerButton
48 | p event
49 | p controller.button_pressed?(SDL2::GameController::Button::A)
50 | p controller.button_pressed?(SDL2::GameController::Button::B)
51 | p controller.button_pressed?(SDL2::GameController::Button::X)
52 | p controller.button_pressed?(SDL2::GameController::Button::Y)
53 | when SDL2::Event::ControllerDevice
54 | p event
55 | end
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/sample/test_joystick.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | SDL2.init(SDL2::INIT_EVERYTHING)
4 | SDL2::TTF.init
5 |
6 | p SDL2::Joystick.num_connected_joysticks
7 | p SDL2::Joystick.devices
8 |
9 | if SDL2::Joystick.num_connected_joysticks > 0
10 | $joystick = SDL2::Joystick.open(0)
11 | p $joystick.name
12 | p $joystick.num_axes
13 | p $joystick.num_hats
14 | p $joystick.num_buttons
15 | p $joystick.num_balls
16 | p $joystick.GUID
17 | p $joystick.attached?
18 | p $joystick.index
19 | end
20 |
21 | window = SDL2::Window.create("testsprite",0, 0, 640, 480, 0)
22 |
23 | renderer = window.create_renderer(-1, 0)
24 |
25 |
26 | loop do
27 | while ev = SDL2::Event.poll
28 | case ev
29 | when SDL2::Event::JoyButton, SDL2::Event::JoyAxisMotion
30 | p ev
31 | when SDL2::Event::JoyDeviceAdded
32 | p ev
33 | $joystick = SDL2::Joystick.open(ev.which)
34 | when SDL2::Event::JoyDeviceRemoved
35 | p ev
36 | p $joystick.name
37 | when SDL2::Event::KeyDown
38 | if ev.scancode == SDL2::Key::Scan::ESCAPE
39 | exit
40 | end
41 | when SDL2::Event::Quit
42 | exit
43 | end
44 | end
45 |
46 | renderer.present
47 | sleep 0.1
48 | end
49 |
--------------------------------------------------------------------------------
/sample/test_keyboard.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | SDL2.init(SDL2::INIT_EVERYTHING)
4 |
5 | window = SDL2::Window.create("testsprite", 0, 0, 640, 480, 0)
6 | renderer = window.create_renderer(-1, 0)
7 |
8 | puts "scancode of \"X\": #{SDL2::Key::Scan.from_name("X")}"
9 | puts "scancode of SDL2::Key::X: #{SDL2::Key::Scan.from_keycode(SDL2::Key::X)}"
10 | puts "keycode of \"X\": #{SDL2::Key.keycode_from_name("X")}"
11 | puts "keycode of SDL2::Key::Scan::X: #{SDL2::Key.keycode_from_scancode(SDL2::Key::Scan::X)}"
12 | SDL2::TextInput.rect = SDL2::Rect[20, 20, 200, 20]
13 |
14 |
15 | def toggle_text_input
16 | if SDL2::TextInput.active?
17 | SDL2::TextInput.stop
18 | else
19 | SDL2::TextInput.start
20 | end
21 | p SDL2::TextInput.active?
22 | end
23 |
24 | loop do
25 | while ev = SDL2::Event.poll
26 | case ev
27 | when SDL2::Event::Quit
28 | exit
29 | when SDL2::Event::TextInput
30 | p ev
31 | p ev.text
32 | when SDL2::Event::TextEditing
33 | p ev
34 | when SDL2::Event::KeyDown
35 | puts "scancode: #{ev.scancode}(#{SDL2::Key::Scan.name_of(ev.scancode)})"
36 | puts "keycode: #{ev.sym}(#{SDL2::Key.name_of(ev.sym)})"
37 | puts "mod: #{ev.mod}"
38 | puts "mod(SDL2::Key::Mod.state): #{SDL2::Key::Mod.state}"
39 | if ev.sym == SDL2::Key::RETURN
40 | toggle_text_input
41 | end
42 | end
43 | end
44 |
45 | renderer.present
46 | sleep 0.01
47 | end
48 |
--------------------------------------------------------------------------------
/sample/test_mouse.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | SDL2.init(SDL2::INIT_EVERYTHING)
4 | window = SDL2::Window.create("testsprite",0, 0, 640, 480, 0)
5 | renderer = window.create_renderer(-1, SDL2::Renderer::Flags::ACCELERATED)
6 | SDL2::TextInput.stop
7 |
8 | loop do
9 | while ev = SDL2::Event.poll
10 | case ev
11 | when SDL2::Event::Quit
12 | exit
13 | when SDL2::Event::KeyDown
14 | case ev.sym
15 | when SDL2::Key::ESCAPE
16 | exit
17 | when SDL2::Key::S
18 | state = SDL2::Mouse.state
19 | p state
20 | p [state.x, state.y, state.button_bits,
21 | state.pressed?(1), state.pressed?(2), state.pressed?(3)]
22 | when SDL2::Key::R
23 | p SDL2::Mouse.relative_state
24 | when SDL2::Key::SPACE
25 | SDL2::Mouse.relative_mode = ! SDL2::Mouse.relative_mode?
26 | when SDL2::Key::T
27 | if SDL2::Mouse::Cursor.shown?
28 | SDL2::Mouse::Cursor.hide
29 | else
30 | SDL2::Mouse::Cursor.show
31 | end
32 | when SDL2::Key::F
33 | p SDL2::Mouse.focused_window
34 | when SDL2::Key::W
35 | SDL2::Mouse::Cursor.warp(window, 100, 20)
36 | end
37 | when SDL2::Event::MouseButtonDown
38 | p ev
39 | end
40 | end
41 |
42 | renderer.present
43 | sleep 0.01
44 | end
45 |
--------------------------------------------------------------------------------
/sample/test_surface.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | s = SDL2::Surface.load("icon.bmp")
4 | p s.w
5 | p s.h
6 | p s.pitch
7 | p s.bits_per_pixel
8 | s2 = SDL2::Surface.from_string(s.pixels, 32, 32, 24)
9 |
--------------------------------------------------------------------------------
/sample/test_ttf.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | SDL2.init(SDL2::INIT_EVERYTHING)
4 | SDL2::TTF.init
5 |
6 |
7 | window = SDL2::Window.create("testsprite",
8 | SDL2::Window::POS_CENTERED, SDL2::Window::POS_CENTERED,
9 | 640, 480, 0)
10 | renderer = window.create_renderer(-1, 0)
11 |
12 | font = SDL2::TTF.open("font.ttf", 40)
13 |
14 | def draw_three_types(renderer, font, x, ybase)
15 | renderer.copy(renderer.create_texture_from(font.render_solid("Foo", [255, 255, 255])),
16 | nil, SDL2::Rect.new(x, ybase, 100, 30))
17 |
18 | renderer.copy(renderer.create_texture_from(font.render_shaded("Foo", [255, 255, 255], [0,0,0])),
19 | nil, SDL2::Rect.new(x, ybase+40, 100, 30))
20 |
21 | renderer.copy(renderer.create_texture_from(font.render_blended("Foo", [255, 255, 255])),
22 | nil, SDL2::Rect.new(x, ybase+80, 100, 30))
23 | end
24 |
25 |
26 | p font.style
27 | p font.outline
28 | p font.hinting
29 | p font.kerning
30 | p font.height
31 | p font.ascent
32 | p font.descent
33 | p font.line_skip
34 | p font.num_faces
35 | p font.face_is_fixed_width?
36 | p font.face_family_name
37 | p font.face_style_name
38 | p font.size_text("Foo")
39 |
40 | renderer.draw_color = [255,0,0]
41 | renderer.fill_rect(SDL2::Rect.new(0,0,640,480))
42 |
43 | draw_three_types(renderer, font, 20, 50)
44 |
45 | font.outline = 1
46 | draw_three_types(renderer, font, 150, 50)
47 |
48 | font.outline = 0
49 | font.style = SDL2::TTF::Style::BOLD
50 | draw_three_types(renderer, font, 280, 50)
51 |
52 | font.style = 0
53 | font.hinting = SDL2::TTF::Hinting::MONO
54 | draw_three_types(renderer, font, 410, 50)
55 |
56 | font.style = 0
57 | font.hinting = SDL2::TTF::Hinting::NORMAL
58 | font.kerning = false
59 | draw_three_types(renderer, font, 540, 50)
60 |
61 |
62 | loop do
63 | while ev = SDL2::Event.poll
64 | case ev
65 | when SDL2::Event::KeyDown
66 | if ev.scancode == SDL2::Key::Scan::ESCAPE
67 | exit
68 | end
69 | when SDL2::Event::Quit
70 | exit
71 | end
72 | end
73 |
74 | renderer.present
75 | #GC.start
76 | sleep 0.1
77 | end
78 |
--------------------------------------------------------------------------------
/sample/test_video.rb:
--------------------------------------------------------------------------------
1 | require "sdl2"
2 |
3 | SDL2.init(SDL2::INIT_EVERYTHING)
4 |
5 | window = SDL2::Window.create("testsprite",
6 | SDL2::Window::POS_CENTERED, SDL2::Window::POS_CENTERED,
7 | 640, 480, 0)
8 |
9 | renderer = window.create_renderer(-1,
10 | SDL2::Renderer::Flags::ACCELERATED|SDL2::Renderer::Flags::TARGETTEXTURE)
11 | texture = renderer.load_texture("icon.bmp")
12 |
13 | rect = SDL2::Rect.new(48, 128, 32, 32)
14 | renderer.copy(texture, nil, rect)
15 | renderer.copy_ex(texture, nil, SDL2::Rect[128, 256, 48, 48], 45, nil,
16 | SDL2::Renderer::FLIP_NONE)
17 | texture.blend_mode = SDL2::BlendMode::ADD
18 | renderer.copy(texture, nil, SDL2::Rect[128, 294, 48, 48])
19 |
20 | texture.blend_mode = SDL2::BlendMode::NONE
21 | texture.color_mod = [128, 128, 255]
22 | renderer.copy(texture, nil, SDL2::Rect[300, 420, 48, 48])
23 |
24 | texture.blend_mode = SDL2::BlendMode::NONE
25 | texture.color_mod = [255, 255, 255]
26 |
27 | if renderer.support_render_target?
28 | empty_texture = renderer.create_texture(renderer.info.texture_formats.first,
29 | SDL2::Texture::ACCESS_TARGET, 128, 128)
30 | renderer.render_target = empty_texture
31 | renderer.draw_color = [255, 0, 255]
32 | renderer.draw_line(0, 0, 128, 128)
33 | renderer.reset_render_target
34 | renderer.copy(empty_texture, SDL2::Rect[0, 0, 128, 128], SDL2::Rect[420, 20, 128, 128])
35 | end
36 |
37 | loop do
38 | while ev = SDL2::Event.poll
39 | p ev
40 | case ev
41 | when SDL2::Event::KeyDown
42 | if ev.scancode == SDL2::Key::Scan::ESCAPE
43 | exit
44 | else
45 | p ev.scancode
46 | end
47 | end
48 | end
49 |
50 | renderer.present
51 | sleep 0.1
52 | end
53 |
54 |
--------------------------------------------------------------------------------
/sample/testgl.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 | require 'gl'
3 |
4 | include Gl
5 |
6 | WINDOW_W = 640
7 | WINDOW_H = 480
8 |
9 | shadedCube = true
10 |
11 | SDL2.init(SDL2::INIT_EVERYTHING)
12 | SDL2::GL.set_attribute(SDL2::GL::RED_SIZE, 8)
13 | SDL2::GL.set_attribute(SDL2::GL::GREEN_SIZE, 8)
14 | SDL2::GL.set_attribute(SDL2::GL::BLUE_SIZE, 8)
15 | SDL2::GL.set_attribute(SDL2::GL::ALPHA_SIZE, 8)
16 | SDL2::GL.set_attribute(SDL2::GL::DOUBLEBUFFER, 1)
17 |
18 | window = SDL2::Window.create("testgl", 0, 0, WINDOW_W, WINDOW_H, SDL2::Window::Flags::OPENGL)
19 | context = SDL2::GL::Context.create(window)
20 |
21 | printf("OpenGL version %d.%d\n",
22 | SDL2::GL.get_attribute(SDL2::GL::CONTEXT_MAJOR_VERSION),
23 | SDL2::GL.get_attribute(SDL2::GL::CONTEXT_MINOR_VERSION))
24 |
25 | glViewport( 0, 0, 640, 400 )
26 | glMatrixMode( GL_PROJECTION )
27 | glLoadIdentity( )
28 |
29 | glMatrixMode( GL_MODELVIEW )
30 | glLoadIdentity( )
31 |
32 | glEnable(GL_DEPTH_TEST)
33 |
34 | glDepthFunc(GL_LESS)
35 |
36 | glShadeModel(GL_SMOOTH)
37 |
38 | color =
39 | [[ 1.0, 1.0, 0.0],
40 | [ 1.0, 0.0, 0.0],
41 | [ 0.0, 0.0, 0.0],
42 | [ 0.0, 1.0, 0.0],
43 | [ 0.0, 1.0, 1.0],
44 | [ 1.0, 1.0, 1.0],
45 | [ 1.0, 0.0, 1.0],
46 | [ 0.0, 0.0, 1.0]]
47 |
48 | cube =
49 | [[ 0.5, 0.5, -0.5],
50 | [ 0.5, -0.5, -0.5],
51 | [-0.5, -0.5, -0.5],
52 | [-0.5, 0.5, -0.5],
53 | [-0.5, 0.5, 0.5],
54 | [ 0.5, 0.5, 0.5],
55 | [ 0.5, -0.5, 0.5],
56 | [-0.5, -0.5, 0.5]]
57 |
58 |
59 | loop do
60 |
61 | while event = SDL2::Event.poll
62 | case event
63 | when SDL2::Event::Quit, SDL2::Event::KeyDown
64 | exit
65 | end
66 | end
67 |
68 | glClearColor(0.0, 0.0, 0.0, 1.0);
69 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
70 |
71 |
72 | glBegin(GL_QUADS)
73 |
74 | if shadedCube then
75 | glColor(color[0])
76 | glVertex(cube[0])
77 | glColor(color[1])
78 | glVertex(cube[1])
79 | glColor(color[2])
80 | glVertex(cube[2])
81 | glColor(color[3])
82 | glVertex(cube[3])
83 |
84 | glColor(color[3])
85 | glVertex(cube[3])
86 | glColor(color[4])
87 | glVertex(cube[4])
88 | glColor(color[7])
89 | glVertex(cube[7])
90 | glColor(color[2])
91 | glVertex(cube[2])
92 |
93 | glColor(color[0])
94 | glVertex(cube[0])
95 | glColor(color[5])
96 | glVertex(cube[5])
97 | glColor(color[6])
98 | glVertex(cube[6])
99 | glColor(color[1])
100 | glVertex(cube[1])
101 |
102 | glColor(color[5])
103 | glVertex(cube[5])
104 | glColor(color[4])
105 | glVertex(cube[4])
106 | glColor(color[7])
107 | glVertex(cube[7])
108 | glColor(color[6])
109 | glVertex(cube[6])
110 |
111 | glColor(color[5])
112 | glVertex(cube[5])
113 | glColor(color[0])
114 | glVertex(cube[0])
115 | glColor(color[3])
116 | glVertex(cube[3])
117 | glColor(color[4])
118 | glVertex(cube[4])
119 |
120 | glColor(color[6])
121 | glVertex(cube[6])
122 | glColor(color[1])
123 | glVertex(cube[1])
124 | glColor(color[2])
125 | glVertex(cube[2])
126 | glColor(color[7])
127 | glVertex(cube[7])
128 |
129 | else
130 | glColor(1.0, 0.0, 0.0)
131 | glVertex(cube[0])
132 | glVertex(cube[1])
133 | glVertex(cube[2])
134 | glVertex(cube[3])
135 |
136 | glColor(0.0, 1.0, 0.0)
137 | glVertex(cube[3])
138 | glVertex(cube[4])
139 | glVertex(cube[7])
140 | glVertex(cube[2])
141 |
142 | glColor(0.0, 0.0, 1.0)
143 | glVertex(cube[0])
144 | glVertex(cube[5])
145 | glVertex(cube[6])
146 | glVertex(cube[1])
147 |
148 | glColor(0.0, 1.0, 1.0)
149 | glVertex(cube[5])
150 | glVertex(cube[4])
151 | glVertex(cube[7])
152 | glVertex(cube[6])
153 |
154 | glColor(1.0, 1.0, 0.0)
155 | glVertex(cube[5])
156 | glVertex(cube[0])
157 | glVertex(cube[3])
158 | glVertex(cube[4])
159 |
160 | glColor(1.0, 0.0, 1.0)
161 | glVertex(cube[6])
162 | glVertex(cube[1])
163 | glVertex(cube[2])
164 | glVertex(cube[7])
165 |
166 | end
167 |
168 | glEnd()
169 |
170 | glMatrixMode(GL_MODELVIEW)
171 | glRotate(5.0, 1.0, 1.0, 1.0)
172 |
173 | window.gl_swap
174 |
175 | end
176 |
--------------------------------------------------------------------------------
/sample/testsprite.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 | require 'optparse'
3 |
4 | MAX_SPEED = 1
5 |
6 | class Integer
7 | def bit?(mask)
8 | (self & mask) != 0
9 | end
10 | end
11 |
12 | class Cycle < Struct.new(:cycle_color, :cycle_alpha, :color, :alpha,
13 | :color_direction, :alpha_direction)
14 | def update
15 | self.color += self.color_direction
16 | if self.color < 0
17 | self.color = 0
18 | self.color_direction *= -1
19 | end
20 | if color > 255
21 | self.color = 255
22 | self.color_direction *= -1
23 | end
24 | self.alpha += self.alpha_direction
25 | if self.alpha < 0
26 | self.alpha = 0
27 | self.alpha_direction *= -1
28 | end
29 | if self.alpha > 255
30 | self.alpha = 255
31 | self.alpha_direction *= -1
32 | end
33 | end
34 | end
35 |
36 |
37 | class WindowData
38 | def initialize(sdl_window, renderer, cycle, blend_mode, use_color_key)
39 | @sdl_window = sdl_window
40 | @renderer = renderer
41 | @cycle = cycle
42 | @blend_mode = blend_mode
43 | @use_color_key = use_color_key
44 | end
45 |
46 | def setup(spritepath, num_sprites)
47 | load_sprite(spritepath)
48 | @sprite.blend_mode = @blend_mode
49 | @sprites = Array.new(num_sprites){
50 | Sprite.new(@sdl_window, @renderer, @sprite)
51 | }
52 | end
53 |
54 | def load_sprite(fname)
55 | bitmap = SDL2::Surface.load(fname)
56 | bitmap.color_key = bitmap.pixel(0, 0) if @use_color_key
57 | @sprite = @renderer.create_texture_from(bitmap)
58 | bitmap.destroy
59 | end
60 |
61 | def update
62 | @sprites.each(&:update)
63 | end
64 |
65 | def draw
66 | viewport = @renderer.viewport
67 | # Draw a gray background
68 | @renderer.draw_color = [0xA0, 0xA0, 0xA0]
69 | @renderer.clear
70 |
71 | # Points
72 | @renderer.draw_color = [0xff, 0, 0]
73 | @renderer.draw_point(0, 0)
74 | @renderer.draw_point(viewport.w - 1, 0)
75 | @renderer.draw_point(0 ,viewport.h - 1)
76 | @renderer.draw_point(viewport.w - 1, viewport.h - 1)
77 |
78 | # Fill rect
79 | @renderer.draw_color = [0xff, 0xff, 0xff]
80 | @renderer.fill_rect(SDL2::Rect[viewport.w/2 - 50, viewport.h/2 - 50, 100, 100])
81 |
82 | # Draw rect
83 | @renderer.draw_color = [0, 0, 0]
84 | @renderer.draw_rect(SDL2::Rect[viewport.w/2 - 50, viewport.h/2 - 50, 100, 100])
85 |
86 | # lines
87 | @renderer.draw_color = [0, 0xff, 0]
88 | @renderer.draw_line(1, 1, viewport.w - 2, viewport.h - 2)
89 | @renderer.draw_line(1, viewport.h - 2, viewport.w - 2, 1)
90 |
91 | @sprite.color_mod = [@cycle.color, 255, @cycle.color] if @cycle.cycle_color
92 | @sprite.alpha_mod = @cycle.alpha if @cycle.cycle_alpha
93 |
94 | @sprites.each(&:draw)
95 | @renderer.present
96 | end
97 |
98 | class Sprite
99 | def initialize(window, renderer, sprite, max_speed = MAX_SPEED)
100 | @renderer = renderer
101 | @sprite = sprite
102 | window_w, window_h = window.size
103 | @position = SDL2::Rect[rand(window_w - sprite.w),
104 | rand(window_h - sprite.h),
105 | sprite.w, sprite.h]
106 | @velocity = random_velocity(max_speed)
107 | end
108 |
109 | def random_velocity(max_speed)
110 | loop do
111 | dx = rand(2*max_speed + 1) - max_speed
112 | dy = rand(2*max_speed + 1) - max_speed
113 | return SDL2::Point[dx, dy] if dx != 0 || dy != 0
114 | end
115 | end
116 |
117 | def update
118 | viewport = @renderer.viewport
119 | @position.x += @velocity.x
120 | @position.y += @velocity.y
121 | unless (0 .. viewport.w - @sprite.w).cover?(@position.x)
122 | @velocity.x *= -1
123 | @position.x += @velocity.x
124 | end
125 |
126 | unless (0 .. viewport.h - @sprite.h).cover?(@position.y)
127 | @velocity.y *= -1
128 | @position.y += @velocity.y
129 | end
130 | end
131 |
132 | def draw
133 | @renderer.copy(@sprite, nil, @position)
134 | end
135 | end
136 | end
137 |
138 | class App
139 | def initialize
140 | @title = "testsprite"
141 | @window_flags = 0
142 | @icon_path = nil
143 | @num_window = 1
144 | @window_x = @window_y = SDL2::Window::POS_UNDEFINED
145 | @window_w = 640
146 | @window_h = 480
147 | @spritepath = "icon.bmp"
148 | @renderer_flags = 0
149 | @num_sprites = 100
150 | @blend_mode = SDL2::BlendMode::BLEND
151 | @cycle = Cycle.new(false, false, rand(255), rand(255), [1,-1].sample, [1,-1].sample)
152 | @use_color_key = true
153 | end
154 |
155 | def options
156 | opts = OptionParser.new("Usage: testsprite [options] [SPRITE]")
157 | opts.version = SDL2::VERSION
158 | opts.release = nil
159 |
160 | opts.on("-m", "--mode MODE", "fullscreen|fullscreen-desktop|window"){|mode|
161 | case mode
162 | when "fullscreen"
163 | raise "Only one window is available for fullscreen mode" if @num_window != 1
164 | @window_flags |= SDL2::Window::FULLSCREEN
165 | when "fullscreen-desktop"
166 | raise "Only one window is available for fullscreen-desktop mode" if @num_window != 1
167 | @window_flags |= SDL2::Window::FULLSCREEN_DESKTOP
168 | when "window"
169 | @window_flags &= ~(SDL2::Window::FULLSCREEN|SDL2::Window::FULLSCREEN_DESKTOP)
170 | end
171 | }
172 |
173 | opts.on("--windows N", "Number of windows", Integer){|n|
174 | if n != 1 and @window_flags.bit?(SDL2::Window::FULLSCREEN|SDL2::Window::FULLSCREEN_DESKTOP)
175 | raise "Only one window is available for fullscreen mode"
176 | end
177 | @num_window = n
178 | }
179 |
180 | opts.on("--geometry WxH", "size of the window/screen"){|s|
181 | raise "geomtry format error #{s}" unless /\A(\d+)x(\d+)\Z/ =~ s
182 | @window_w = Integer($1); @window_h = Integer($2)
183 | }
184 |
185 | opts.on("--center", "Window will be centered"){
186 | @window_x = @window_y = SDL2::Window::POS_CENTERED
187 | }
188 |
189 | opts.on("-t", "--title TITLE", "Window title"){|title| @title = title }
190 |
191 | opts.on("--icon ICON", "Window icon"){|path| @icon_path = path }
192 |
193 | opts.on("--position X,Y", "Position of the window", Array){|x,y|
194 | @window_x = Integer(x); @window_y = Integer(y)
195 | }
196 |
197 | opts.on("--noframe", "Window is borderless") { @window_flags |= SDL2::Window::BORDERLESS }
198 |
199 | opts.on("--resize", "Window is resizable") { @window_flags |= SDL2::Window::RESIZABLE }
200 |
201 | opts.on("--allow-highdip"){ @window_flags |= SDL2::Window::ALLOW_HIGHDPI }
202 |
203 | opts.on("--blend MODE", "none|blend|add|mod"){|blend_mode|
204 | @blend_mode = case blend_mode
205 | when "none"
206 | SDL2::BlendMode::NONE
207 | when "blend"
208 | SDL2::BlendMode::BLEND
209 | when "add"
210 | SDL2::BlendMode::ADD
211 | when "mod"
212 | SDL2::BlendMode::MOD
213 | end
214 | }
215 |
216 | opts.on("--cycle-color"){ @cycle.cycle_color = true }
217 |
218 | opts.on("--cycle-alpha"){ @cycle.cycle_alpha = true }
219 |
220 | opts.on("--vsync", "Present is syncronized with the refresh rate") {
221 | @renderer_flags |= SDL2::Renderer::Flags::PRESENTVSYNC
222 | }
223 |
224 | opts.on("--use-color-key (yes|no)", TrueClass){|bool| @use_color_key = bool }
225 | opts
226 | end
227 |
228 | def run(argv)
229 | options.parse!(argv)
230 | @spritepath = argv.shift || @spritepath
231 |
232 | SDL2.init(SDL2::INIT_VIDEO)
233 | icon = load_icon
234 | @windows = @num_window.times.map do |n|
235 | window = SDL2::Window.create("#{@title} #{n}",
236 | @window_x, @window_y, @window_w, @window_h,
237 | @window_flags)
238 | renderer = window.create_renderer(-1, @renderer_flags)
239 | window.icon = icon if icon
240 |
241 | WindowData.new(window, renderer, @cycle, @blend_mode, @use_color_key)
242 | end
243 |
244 | @windows.each{|win| win.setup(@spritepath, @num_sprites) }
245 |
246 | loop do
247 | while event = SDL2::Event.poll
248 | handle_event(event)
249 | end
250 | @cycle.update
251 | @windows.each {|window| window.update; window.draw }
252 | sleep 0.01
253 | end
254 | end
255 |
256 | def load_icon
257 | return nil if @icon_path.nil?
258 | SDL2::Surface.load(@icon_path)
259 | end
260 |
261 | def handle_event(event)
262 | case event
263 | when SDL2::Event::Quit
264 | exit
265 | when SDL2::Event::KeyDown
266 | case event.sym
267 | when SDL2::Key::ESCAPE
268 | exit
269 | when SDL2::Key::RETURN
270 | if event.mod.bit?(SDL2::Key::Mod::ALT)
271 | if event.window.fullscreen_mode == 0
272 | event.window.fullscreen_mode = SDL2::Window::FULLSCREEN_DESKTOP
273 | else
274 | event.window.fullscreen_mode = 0
275 | end
276 | end
277 | when SDL2::Key::M
278 | if event.mod.bit?(SDL2::Key::Mod::CTRL|SDL2::Key::Mod::CAPS)
279 | window = event.window
280 | if window.flags & SDL2::Window::MAXIMIZED != 0
281 | window.restore
282 | else
283 | window.maximize
284 | end
285 | end
286 | when SDL2::Key::Z
287 | if event.mod.bit?(SDL2::Key::Mod::CTRL|SDL2::Key::Mod::CAPS)
288 | window = event.window
289 | window.minimize if window
290 | end
291 | end
292 | end
293 | end
294 | end
295 |
296 | App.new.run(ARGV)
297 |
--------------------------------------------------------------------------------
/sample/testspriteminimal.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | SpriteMotion = Struct.new(:position, :velocity)
4 | WINDOW_W = 640
5 | WINDOW_H = 480
6 | NUM_SPRITES = 100
7 | MAX_SPEED = 1
8 |
9 | def load_sprite(filename, renderer)
10 | bitmap = SDL2::Surface.load_bmp(filename)
11 | bitmap.color_key = bitmap.pixel(0, 0)
12 | sprite = renderer.create_texture_from(bitmap)
13 | bitmap.destroy
14 |
15 | sprite
16 | end
17 |
18 | def move_sprite(motions, renderer, sprite)
19 | # draw a gray background
20 | renderer.draw_color = [0xA0, 0xA0, 0xA0]
21 | renderer.clear
22 |
23 | sprite_w = sprite.w; sprite_h = sprite.h
24 |
25 | motions.each do |m|
26 | m.position.x += m.velocity.x
27 | if m.position.x < 0 || m.position.x >= WINDOW_W - sprite_w
28 | m.velocity.x *= -1
29 | m.position.x += m.velocity.x
30 | end
31 |
32 | m.position.y += m.velocity.y
33 | if m.position.y < 0 || m.position.y >= WINDOW_H - sprite_h
34 | m.velocity.y *= -1
35 | m.position.y += m.velocity.y
36 | end
37 |
38 | renderer.copy(sprite, nil, m.position)
39 | end
40 |
41 | renderer.present
42 | end
43 |
44 | def random_position(sprite_w, sprite_h)
45 | SDL2::Rect[rand(WINDOW_W - sprite_w), rand(WINDOW_H - sprite_h), sprite_w, sprite_h]
46 | end
47 |
48 | def random_velocity
49 | loop do
50 | x = rand(MAX_SPEED*2+1) - MAX_SPEED
51 | y = rand(MAX_SPEED*2+1) - MAX_SPEED
52 | return SDL2::Point.new(x, y) if x != 0 || y != 0
53 | end
54 | end
55 |
56 | window = SDL2::Window.create("testspritesimple", 0, 0, WINDOW_W, WINDOW_H, 0)
57 | renderer = window.create_renderer(-1, 0)
58 | sprite = load_sprite("icon.bmp", renderer)
59 | sprite_w = sprite.w; sprite_h = sprite.h
60 |
61 | motions = Array.new(NUM_SPRITES) do
62 | SpriteMotion.new(random_position(sprite_w, sprite_h), random_velocity)
63 | end
64 |
65 | loop do
66 | while event = SDL2::Event.poll
67 | case event
68 | when SDL2::Event::Quit, SDL2::Event::KeyDown
69 | exit
70 | end
71 | end
72 |
73 | move_sprite(motions, renderer, sprite)
74 | sleep 0.01
75 | end
76 |
--------------------------------------------------------------------------------
/sample/timer.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | SDL2.init(SDL2::INIT_TIMER)
4 |
5 | p SDL2.get_ticks
6 | p SDL2.get_performance_counter
7 | p SDL2.get_performance_frequency
8 | SDL2.delay 50
9 | p SDL2.get_ticks
10 | p SDL2.get_performance_counter
11 | p SDL2.get_performance_frequency
12 |
--------------------------------------------------------------------------------
/sample/version.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 |
3 | p SDL2::LIBSDL_VERSION
4 | p SDL2::LIBSDL_VERSION_NUMBER
5 | p SDL2::LIBSDL_REVISION
6 | p SDL2::LIBSDL_REVISION_NUMBER
7 | p SDL2::LIBSDL_IMAGE_VERSION
8 | p SDL2::LIBSDL_IMAGE_VERSION_NUMBER
9 | p SDL2::LIBSDL_MIXER_VERSION
10 | p SDL2::LIBSDL_MIXER_VERSION_NUMBER
11 | p SDL2::LIBSDL_TTF_VERSION
12 | p SDL2::LIBSDL_TTF_VERSION_NUMBER
13 |
--------------------------------------------------------------------------------
/sample/video_info.rb:
--------------------------------------------------------------------------------
1 | require 'sdl2'
2 | require "pp"
3 |
4 | SDL2.init(SDL2::INIT_VIDEO)
5 |
6 | pp SDL2::PixelFormat::FORMATS.map{|f| f.name}
7 | p SDL2::PixelFormat::RGBA8888
8 |
9 | p SDL2::Display.displays
10 | SDL2::Display.displays.each{|display| p display.modes }
11 | display = SDL2::Display.displays.first
12 | print "curent mode: "; p display.current_mode
13 | print "desktop mode: "; p display.desktop_mode
14 | search_mode = SDL2::Display::Mode.new(SDL2::PixelFormat::UNKNOWN, 640, 480, 60)
15 | puts "The mode closest to #{search_mode.inspect} is #{display.closest_mode(search_mode).inspect}"
16 | print "bounds: "; p display.bounds
17 | puts "current video driver: #{SDL2.current_video_driver}"
18 |
19 | window = SDL2::Window.create("video info", 10, 10, 640, 480, 0)
20 | renderer = window.create_renderer(-1, 0)
21 | texture = renderer.load_texture("icon.bmp")
22 |
23 | puts "window id: #{window.window_id}"
24 | p SDL2::Window.all_windows
25 | p window.display_mode
26 | p window.display
27 | print "window brightness: "; p window.brightness
28 | print "window input grabbing: "; p window.input_is_grabbed?
29 | print "window maximum size: "; p window.maximum_size
30 | print "window minimum size: "; p window.minimum_size
31 | print "window position: "; p window.position
32 | print "window size: "; p window.size
33 | print "window title: "; p window.title
34 | print "window bordered: "; p window.bordered
35 |
36 | p window.renderer
37 | p renderer
38 |
39 | p SDL2.video_drivers
40 | p SDL2::Renderer.drivers_info
41 | p renderer.info
42 | p renderer.clip_rect
43 | p renderer.logical_size
44 | p renderer.scale
45 | p renderer.viewport
46 | p renderer.support_render_target?
47 | p renderer.output_size
48 |
49 | p renderer.info.texture_formats
50 | renderer.info.texture_formats.each do |format|
51 | p [format.format, format.name, format.type, format.order, format.layout, format.bpp, format.bytes_per_pixel,
52 | format.indexed?, format.alpha?, format.fourcc?]
53 |
54 | end
55 |
56 | p texture
57 | texture.destroy
58 | p texture
59 |
60 | p SDL2::ScreenSaver.enabled?
61 | SDL2::ScreenSaver.disable
62 | p SDL2::ScreenSaver.enabled?
63 |
64 | renderer.present
65 |
66 |
--------------------------------------------------------------------------------
/timer.c:
--------------------------------------------------------------------------------
1 | #include "rubysdl2_internal.h"
2 | #include
3 |
4 | /*
5 | * @overload delay(ms)
6 | * @param [Integer] ms the number of milliseconds to delay
7 | *
8 | * Wait a specified milliseconds.
9 | *
10 | * This function stops all ruby threads. If you want to keep running other
11 | * threads, you should use Kernel.sleep instead of this function.
12 | *
13 | * @return [nil]
14 | */
15 | static VALUE SDL_s_delay(VALUE self, VALUE ms)
16 | {
17 | SDL_Delay(NUM2UINT(ms));
18 | return Qnil;
19 | }
20 |
21 | /*
22 | * Return the number of milliseconds since {SDL2.init} is called.
23 | *
24 | * @return [Integer] milliseconds in 32bit unsigned value.
25 | */
26 | static VALUE SDL_s_get_ticks(VALUE self)
27 | {
28 | return UINT2NUM(SDL_GetTicks());
29 | }
30 |
31 | /*
32 | * Return the current value of the high resolution counter.
33 | *
34 | * This method is typically used for profiling.
35 | *
36 | * @return [Integer] the current counter value
37 | * @see SDL2.get_performance_frequency
38 | */
39 | static VALUE SDL_s_get_performance_counter(VALUE self)
40 | {
41 | return ULL2NUM(SDL_GetPerformanceCounter());
42 | }
43 |
44 | /*
45 | * Return the frequency (count per second) of the high resolution counter.
46 | *
47 | * @return [Integer] a platform-specific count per second.
48 | * @see SDL2.get_performance_counter
49 | */
50 | static VALUE SDL_s_get_performance_frequency(VALUE self)
51 | {
52 | return ULL2NUM(SDL_GetPerformanceFrequency());
53 | }
54 |
55 | void rubysdl2_init_timer(void)
56 | {
57 | rb_define_module_function(mSDL2, "delay", SDL_s_delay, 1);
58 | rb_define_module_function(mSDL2, "get_ticks", SDL_s_get_ticks, 0);
59 | rb_define_module_function(mSDL2, "get_performance_counter",
60 | SDL_s_get_performance_counter, 0);
61 | rb_define_module_function(mSDL2, "get_performance_frequency",
62 | SDL_s_get_performance_frequency,0);
63 | }
64 |
--------------------------------------------------------------------------------
/ttf.c.m4:
--------------------------------------------------------------------------------
1 | /* -*- mode: C -*- */
2 | #ifdef HAVE_SDL_TTF_H
3 | #include "rubysdl2_internal.h"
4 | #include
5 | #include
6 |
7 | static VALUE cTTF;
8 | static VALUE mStyle;
9 | static VALUE mHinting;
10 |
11 | #define TTF_ERROR() do { HANDLE_ERROR(SDL_SetError("%s", TTF_GetError())); } while (0)
12 | #define HANDLE_TTF_ERROR(code) \
13 | do { if ((code) < 0) { TTF_ERROR(); } } while (0)
14 |
15 | typedef struct TTF {
16 | TTF_Font* font;
17 | } TTF;
18 |
19 | #define TTF_ATTRIBUTE(attr, capitalized_attr, ruby2c, c2ruby) \
20 | static VALUE TTF_##attr(VALUE self) \
21 | { \
22 | return c2ruby(TTF_Get##capitalized_attr(Get_TTF_Font(self))); \
23 | } \
24 | static VALUE TTF_set_##attr(VALUE self, VALUE val) \
25 | { \
26 | TTF_Set##capitalized_attr(Get_TTF_Font(self), ruby2c(val)); \
27 | return Qnil; \
28 | }
29 |
30 | #define TTF_ATTRIBUTE_INT(attr, capitalized_attr) \
31 | TTF_ATTRIBUTE(attr, capitalized_attr, NUM2INT, INT2NUM)
32 |
33 | #define TTF_ATTR_READER(attr, capitalized_attr, c2ruby) \
34 | static VALUE TTF_##attr(VALUE self) \
35 | { \
36 | return c2ruby(TTF_Font##capitalized_attr(Get_TTF_Font(self))); \
37 | }
38 |
39 | static void TTF_free(TTF* f)
40 | {
41 | if (rubysdl2_is_active() && f->font)
42 | TTF_CloseFont(f->font);
43 | free(f);
44 | }
45 |
46 | static VALUE TTF_new(TTF_Font* font)
47 | {
48 | TTF* f = ALLOC(TTF);
49 | f->font = font;
50 | return Data_Wrap_Struct(cTTF, 0, TTF_free, f);
51 | }
52 |
53 | DEFINE_WRAPPER(TTF_Font, TTF, font, cTTF, "SDL2::TTF");
54 |
55 | /*
56 | * Document-class: SDL2::TTF
57 | *
58 | * This class represents font information.
59 | *
60 | * You can render TrueType fonts using [SDL_ttf](https://www.libsdl.org/projects/SDL_ttf/)
61 | * and this class.
62 | *
63 | * @!attribute style
64 | * Font style.
65 | * The OR'd bits of the constants of {SDL2::TTF::Style}.
66 | * @return [Integer]
67 | *
68 | * @!attribute outline
69 | * The outline pixel width of the font.
70 | * @return [Integer]
71 | *
72 | * @!attribute hinting
73 | * Font hinting.
74 | * One of the constants of {SDL2::TTF::Hinting}.
75 | * @return [Integer]
76 | *
77 | * @!attribute kerning
78 | * True if kerning is enabled.
79 | * @return [boolean]
80 | *
81 | * @!attribute [r] height
82 | * The maximum pixel height of all glyphs of the font.
83 | * You can use this height to render text as close together vertically
84 | * as possible.
85 | * @return [Integer]
86 | * @see #line_skip
87 | *
88 | * @!attribute [r] ascent
89 | * The maximum pixel ascent of all glyphs of the font.
90 | * The distance from the top of the font to the baseline.
91 | * @return [Integer]
92 | * @see #descent
93 | *
94 | * @!attribute [r] descent
95 | * The maximum pixel descent of all glyphs of the font.
96 | * The distance from the bottom of the font to the baseline.
97 | * @return [Integer]
98 | * @see #ascent
99 | *
100 | * @!attribute [r] line_skip
101 | * The recommended pixel height of rendered line of text
102 | * of the font. Normally, this value is larger than {#height}.
103 | * @return [Integer]
104 | *
105 | * @!attribute [r] num_faces
106 | * The number of faces available in the font.
107 | * @return [Integer]
108 | *
109 | * @!attribute [r] face_family_name
110 | * The current font face family name of the font.
111 | * @return [String]
112 | *
113 | * @!attribute [r] face_style_name
114 | * The current font face style name of the font.
115 | * @return [String]
116 | *
117 | */
118 |
119 | /*
120 | * Initialize TrueType font rendering submodule.
121 | *
122 | * This function must be called before calling other methods/class methods
123 | * of {TTF}.
124 | *
125 | * @return [nil]
126 | */
127 | static VALUE TTF_s_init(VALUE self)
128 | {
129 | HANDLE_TTF_ERROR(TTF_Init());
130 | return Qnil;
131 | }
132 |
133 | /*
134 | * Open a font data from file.
135 | *
136 | * @overload open(fname, ptsize, index=0)
137 | * @param fname [String] the path of the font file
138 | * @param ptsize [Integer] the point size of the font (72DPI).
139 | * @param index [Integer] the index of the font faces.
140 | * Some font files have multiple font faces, and you
141 | * can select one of them using **index**. 0 origin.
142 | * If a font have only one font face, 0 is the only
143 | * valid index.
144 | *
145 | * @return [SDL2::TTF] opened font information
146 | * @raise [SDL2::Error] occurs when you fail to open a file.
147 | *
148 | */
149 | static VALUE TTF_s_open(int argc, VALUE* argv, VALUE self)
150 | {
151 | TTF_Font* font;
152 | VALUE fname, ptsize, index;
153 | rb_scan_args(argc, argv, "21", &fname, &ptsize, &index);
154 |
155 | font = TTF_OpenFontIndex(StringValueCStr(fname), NUM2INT(ptsize),
156 | index == Qnil ? 0 : NUM2LONG(index));
157 | if (!font)
158 | TTF_ERROR();
159 |
160 | return TTF_new(font);
161 | }
162 |
163 | /*
164 | * Destroy the font data and release memory.
165 | *
166 | * @return [nil]
167 | */
168 | static VALUE TTF_destroy(VALUE self)
169 | {
170 | TTF* f = Get_TTF(self);
171 | if (f->font)
172 | TTF_CloseFont(f->font);
173 | f->font = NULL;
174 | return Qnil;
175 | }
176 |
177 | TTF_ATTRIBUTE_INT(style, FontStyle);
178 | TTF_ATTRIBUTE_INT(outline, FontOutline);
179 | TTF_ATTRIBUTE_INT(hinting, FontHinting);
180 | TTF_ATTRIBUTE(kerning, FontKerning, RTEST, INT2BOOL);
181 | TTF_ATTR_READER(height, Height, INT2FIX);
182 | TTF_ATTR_READER(ascent, Ascent, INT2FIX);
183 | TTF_ATTR_READER(descent, Descent, INT2FIX);
184 | TTF_ATTR_READER(line_skip, LineSkip, INT2FIX);
185 | TTF_ATTR_READER(num_faces, Faces, LONG2NUM);
186 | TTF_ATTR_READER(face_is_fixed_width_p, FaceIsFixedWidth, INT2BOOL);
187 | TTF_ATTR_READER(face_family_name, FaceFamilyName, utf8str_new_cstr);
188 | TTF_ATTR_READER(face_style_name, FaceStyleName, utf8str_new_cstr);
189 |
190 | /*
191 | * @overload size_text(text)
192 | * Calculate the size of rendered surface of **text** using the font.
193 | *
194 | * @param text [String] the string to size up
195 | *
196 | * @return [Array(Integer, Integer)] a pair of width and height of the rendered surface
197 | *
198 | * @raise [SDL2::Error] It is raised when an error occurs, such as a glyph ins the
199 | * string not being found.
200 | */
201 | static VALUE TTF_size_text(VALUE self, VALUE text)
202 | {
203 | int w, h;
204 | text = rb_str_export_to_enc(text, rb_utf8_encoding());
205 | HANDLE_TTF_ERROR(TTF_SizeUTF8(Get_TTF_Font(self), StringValueCStr(text), &w, &h));
206 | return rb_ary_new3(2, INT2NUM(w), INT2NUM(h));
207 | }
208 |
209 | static SDL_Surface* render_solid(TTF_Font* font, const char* text, SDL_Color fg, SDL_Color bg)
210 | {
211 | return TTF_RenderUTF8_Solid(font, text, fg);
212 | }
213 |
214 | static SDL_Surface* render_blended(TTF_Font* font, const char* text, SDL_Color fg, SDL_Color bg)
215 | {
216 | return TTF_RenderUTF8_Blended(font, text, fg);
217 | }
218 |
219 | static VALUE render(SDL_Surface* (*renderer)(TTF_Font*, const char*, SDL_Color, SDL_Color),
220 | VALUE font, VALUE text, VALUE fg, VALUE bg)
221 | {
222 | SDL_Surface* surface;
223 | text = rb_str_export_to_enc(text, rb_utf8_encoding());
224 | surface = renderer(Get_TTF_Font(font), StringValueCStr(text),
225 | Array_to_SDL_Color(fg), Array_to_SDL_Color(bg));
226 | if (!surface)
227 | TTF_ERROR();
228 |
229 | return Surface_new(surface);
230 | }
231 |
232 | /*
233 | * @overload render_solid(text, fg)
234 | * Render **text** using the font with fg color on a new surface, using
235 | * *Solid* mode.
236 | *
237 | * Solid mode rendering is quick but dirty.
238 | *
239 | * @param text [String] the text to render
240 | * @param fg [Array(Integer, Integer, Integer)]
241 | * the color to render. An array of r, g, and b components.
242 | *
243 | * @return [SDL2::Surface]
244 | *
245 | * @raise [SDL2::Error] raised when the randering fails.
246 | */
247 | static VALUE TTF_render_solid(VALUE self, VALUE text, VALUE fg)
248 | {
249 | return render(render_solid, self, text, fg, Qnil);
250 | }
251 |
252 | /*
253 | * @overload render_shaded(text, fg, bg)
254 | * Render **text** using the font with fg color on a new surface, using
255 | * *Shaded* mode.
256 | *
257 | * Shaded mode rendering is slow and nice, but with a solid box filled by
258 | * the background color.
259 | *
260 | * @param text [String] the text to render
261 | * @param fg [Array(Integer, Integer, Integer)]
262 | * the color to render. An array of r, g, and b components.
263 | * @param bg [Array(Integer, Integer, Integer)]
264 | * the background color. An array of r, g, and b components.
265 | *
266 | * @return [SDL2::Surface]
267 | *
268 | * @raise [SDL2::Error] raised when the randering fails.
269 | */
270 | static VALUE TTF_render_shaded(VALUE self, VALUE text, VALUE fg, VALUE bg)
271 | {
272 | return render(TTF_RenderUTF8_Shaded, self, text, fg, bg);
273 | }
274 |
275 | /*
276 | * @overload render_blended(text, fg)
277 | * Render **text** using the font with fg color on a new surface, using
278 | * *Blended* mode.
279 | *
280 | * Blended mode rendering is very slow but very nice.
281 | * The rendered surface has an alpha channel,
282 | *
283 | * @param text [String] the text to render
284 | * @param fg [Array(Integer, Integer, Integer)]
285 | * the color to render. An array of r, g, and b components.
286 | *
287 | * @return [SDL2::Surface]
288 | *
289 | * @raise [SDL2::Error] raised when the randering fails.
290 | */
291 | static VALUE TTF_render_blended(VALUE self, VALUE text, VALUE fg)
292 | {
293 | return render(render_blended, self, text, fg, Qnil);
294 | }
295 |
296 | /*
297 | * Document-module: SDL2::TTF::Style
298 | *
299 | * Constants represents font styles.
300 | */
301 |
302 | /* Document-module: SDL2::TTF::Hinting
303 | *
304 | * Constants represents font hinting for FreeType.
305 | */
306 | void rubysdl2_init_ttf(void)
307 | {
308 | cTTF = rb_define_class_under(mSDL2, "TTF", rb_cObject);
309 | rb_undef_alloc_func(cTTF);
310 |
311 | rb_define_singleton_method(cTTF, "init", TTF_s_init, 0);
312 | rb_define_singleton_method(cTTF, "open", TTF_s_open, -1);
313 | /* Return true if the font is destroyed by {#destroy}. */
314 | rb_define_method(cTTF, "destroy?", TTF_destroy_p, 0);
315 | rb_define_method(cTTF, "destroy", TTF_destroy, 0);
316 |
317 | #define DEFINE_TTF_ATTRIBUTE(attr) do { \
318 | rb_define_method(cTTF, #attr, TTF_##attr, 0); \
319 | rb_define_method(cTTF, #attr "=", TTF_set_##attr, 1); \
320 | } while (0)
321 |
322 | DEFINE_TTF_ATTRIBUTE(style);
323 | DEFINE_TTF_ATTRIBUTE(outline);
324 | DEFINE_TTF_ATTRIBUTE(hinting);
325 | DEFINE_TTF_ATTRIBUTE(kerning);
326 |
327 | #define DEFINE_TTF_ATTR_READER(attr) \
328 | rb_define_method(cTTF, #attr, TTF_##attr, 0)
329 |
330 | DEFINE_TTF_ATTR_READER(height);
331 | DEFINE_TTF_ATTR_READER(ascent);
332 | DEFINE_TTF_ATTR_READER(descent);
333 | DEFINE_TTF_ATTR_READER(line_skip);
334 | DEFINE_TTF_ATTR_READER(num_faces);
335 | /* Return true if the font is fixed width. */
336 | rb_define_method(cTTF, "face_is_fixed_width?", TTF_face_is_fixed_width_p, 0);
337 | DEFINE_TTF_ATTR_READER(face_family_name);
338 | DEFINE_TTF_ATTR_READER(face_style_name);
339 |
340 | rb_define_method(cTTF, "size_text", TTF_size_text, 1);
341 | rb_define_method(cTTF, "render_solid", TTF_render_solid, 2);
342 | rb_define_method(cTTF, "render_shaded", TTF_render_shaded, 3);
343 | rb_define_method(cTTF, "render_blended", TTF_render_blended, 2);
344 |
345 | mStyle = rb_define_module_under(cTTF, "Style");
346 | /* define(`DEFINE_TTF_STYLE_CONST',`rb_define_const(mStyle, "$1", INT2NUM((TTF_STYLE_$1)))') */
347 | /* @return [Integer] integer representing normal style */
348 | DEFINE_TTF_STYLE_CONST(NORMAL);
349 | /* @return [Integer] integer representing bold style */
350 | DEFINE_TTF_STYLE_CONST(BOLD);
351 | /* @return [Integer] integer representing italic style */
352 | DEFINE_TTF_STYLE_CONST(ITALIC);
353 | /* @return [Integer] integer representing underline style */
354 | DEFINE_TTF_STYLE_CONST(UNDERLINE);
355 | /* @return [Integer] integer representing strikethrough style */
356 | DEFINE_TTF_STYLE_CONST(STRIKETHROUGH);
357 |
358 | mHinting = rb_define_module_under(cTTF, "Hinting");
359 | /* define(`DEFINE_TTF_HINTING_CONST',`rb_define_const(mHinting, "$1", INT2NUM((TTF_HINTING_$1)))') */
360 | /* @return [Integer] integer representing normal hinting, default */
361 | DEFINE_TTF_HINTING_CONST(NORMAL);
362 | /* @return [Integer] integer representing lighter hinting for non-monochrome modes */
363 | DEFINE_TTF_HINTING_CONST(LIGHT);
364 | /* @return [Integer] integer representing strong hinting, only used for monochrome output */
365 | DEFINE_TTF_HINTING_CONST(MONO);
366 | /* @return [Integer] integer representing no hinting */
367 | DEFINE_TTF_HINTING_CONST(NONE);
368 | }
369 |
370 | #else /* HAVE_SDL_TTF_H */
371 | void rubysdl2_init_ttf(void)
372 | {
373 | }
374 | #endif /* HAVE_SDL_TTF_H */
375 |
376 |
--------------------------------------------------------------------------------