├── .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 | --------------------------------------------------------------------------------