gosu 0.7.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (249) hide show
  1. data/COPYING.txt +29 -0
  2. data/Gosu/Async.hpp +48 -0
  3. data/Gosu/Audio.hpp +145 -0
  4. data/Gosu/AutoLink.hpp +16 -0
  5. data/Gosu/Bitmap.hpp +85 -0
  6. data/Gosu/ButtonsMac.hpp +114 -0
  7. data/Gosu/ButtonsWin.hpp +111 -0
  8. data/Gosu/ButtonsX.hpp +115 -0
  9. data/Gosu/Color.hpp +172 -0
  10. data/Gosu/Directories.hpp +36 -0
  11. data/Gosu/Font.hpp +59 -0
  12. data/Gosu/Fwd.hpp +31 -0
  13. data/Gosu/Gosu.hpp +26 -0
  14. data/Gosu/Graphics.hpp +86 -0
  15. data/Gosu/GraphicsBase.hpp +45 -0
  16. data/Gosu/IO.hpp +255 -0
  17. data/Gosu/Image.hpp +148 -0
  18. data/Gosu/ImageData.hpp +45 -0
  19. data/Gosu/Input.hpp +116 -0
  20. data/Gosu/Math.hpp +95 -0
  21. data/Gosu/Platform.hpp +61 -0
  22. data/Gosu/RotFlip.hpp +116 -0
  23. data/Gosu/Sockets.hpp +129 -0
  24. data/Gosu/Text.hpp +47 -0
  25. data/Gosu/TextInput.hpp +57 -0
  26. data/Gosu/Timing.hpp +16 -0
  27. data/Gosu/Utility.hpp +24 -0
  28. data/Gosu/WinUtility.hpp +76 -0
  29. data/Gosu/Window.hpp +84 -0
  30. data/GosuImpl/Async.cpp +37 -0
  31. data/GosuImpl/AudioFmod.cpp +417 -0
  32. data/GosuImpl/AudioSDL.cpp +255 -0
  33. data/GosuImpl/DirectoriesMac.mm +38 -0
  34. data/GosuImpl/DirectoriesUnix.cpp +48 -0
  35. data/GosuImpl/DirectoriesWin.cpp +42 -0
  36. data/GosuImpl/FileUnix.cpp +100 -0
  37. data/GosuImpl/FileWin.cpp +83 -0
  38. data/GosuImpl/Graphics/Bitmap.cpp +116 -0
  39. data/GosuImpl/Graphics/BitmapBMP.cpp +232 -0
  40. data/GosuImpl/Graphics/BitmapColorKey.cpp +39 -0
  41. data/GosuImpl/Graphics/BitmapPNG.cpp +276 -0
  42. data/GosuImpl/Graphics/BitmapUtils.cpp +67 -0
  43. data/GosuImpl/Graphics/BlockAllocator.cpp +127 -0
  44. data/GosuImpl/Graphics/BlockAllocator.hpp +34 -0
  45. data/GosuImpl/Graphics/Color.cpp +126 -0
  46. data/GosuImpl/Graphics/Common.hpp +21 -0
  47. data/GosuImpl/Graphics/DrawOp.hpp +154 -0
  48. data/GosuImpl/Graphics/Font.cpp +110 -0
  49. data/GosuImpl/Graphics/Graphics.cpp +295 -0
  50. data/GosuImpl/Graphics/Image.cpp +159 -0
  51. data/GosuImpl/Graphics/LargeImageData.cpp +115 -0
  52. data/GosuImpl/Graphics/LargeImageData.hpp +37 -0
  53. data/GosuImpl/Graphics/RotFlip.cpp +184 -0
  54. data/GosuImpl/Graphics/TexChunk.cpp +77 -0
  55. data/GosuImpl/Graphics/TexChunk.hpp +40 -0
  56. data/GosuImpl/Graphics/Text.cpp +223 -0
  57. data/GosuImpl/Graphics/TextMac.cpp +242 -0
  58. data/GosuImpl/Graphics/TextPangoFT.cpp +186 -0
  59. data/GosuImpl/Graphics/TextWin.cpp +172 -0
  60. data/GosuImpl/Graphics/Texture.cpp +104 -0
  61. data/GosuImpl/Graphics/Texture.hpp +34 -0
  62. data/GosuImpl/IO.cpp +48 -0
  63. data/GosuImpl/InputMac.mm +677 -0
  64. data/GosuImpl/InputWin.cpp +444 -0
  65. data/GosuImpl/InputX.cpp +158 -0
  66. data/GosuImpl/MacUtility.hpp +48 -0
  67. data/GosuImpl/Math.cpp +49 -0
  68. data/GosuImpl/RubyGosu.swg +474 -0
  69. data/GosuImpl/RubyGosuStub.mm +17 -0
  70. data/GosuImpl/RubyGosu_DllMain.cxx +30 -0
  71. data/GosuImpl/RubyGosu_wrap.cxx +8521 -0
  72. data/GosuImpl/RubyGosu_wrap.h +31 -0
  73. data/GosuImpl/Sockets/CommSocket.cpp +304 -0
  74. data/GosuImpl/Sockets/ListenerSocket.cpp +60 -0
  75. data/GosuImpl/Sockets/MessageSocket.cpp +136 -0
  76. data/GosuImpl/Sockets/Socket.cpp +145 -0
  77. data/GosuImpl/Sockets/Sockets.hpp +66 -0
  78. data/GosuImpl/TextInputMac.mm +207 -0
  79. data/GosuImpl/TextInputWin.cpp +197 -0
  80. data/GosuImpl/TextInputX.cpp +201 -0
  81. data/GosuImpl/TextTTFWin.cpp +247 -0
  82. data/GosuImpl/TimingUnix.cpp +17 -0
  83. data/GosuImpl/TimingWin.cpp +28 -0
  84. data/GosuImpl/Utility.cpp +140 -0
  85. data/GosuImpl/WinMain.cpp +69 -0
  86. data/GosuImpl/WinUtility.cpp +137 -0
  87. data/GosuImpl/WindowMac.mm +466 -0
  88. data/GosuImpl/WindowWin.cpp +447 -0
  89. data/GosuImpl/WindowX.cpp +392 -0
  90. data/GosuImpl/X11vroot.h +118 -0
  91. data/README.txt +13 -0
  92. data/Rakefile +178 -0
  93. data/examples/ChipmunkIntegration.rb +275 -0
  94. data/examples/CptnRuby.rb +231 -0
  95. data/examples/MoreChipmunkAndRMagick.rb +155 -0
  96. data/examples/OpenGLIntegration.rb +232 -0
  97. data/examples/RMagickIntegration.rb +449 -0
  98. data/examples/TextInput.cpp +170 -0
  99. data/examples/TextInput.rb +139 -0
  100. data/examples/Tutorial.cpp +215 -0
  101. data/examples/Tutorial.rb +137 -0
  102. data/examples/media/Beep.wav +0 -0
  103. data/examples/media/CptnRuby Gem.png +0 -0
  104. data/examples/media/CptnRuby Map.txt +25 -0
  105. data/examples/media/CptnRuby Tileset.png +0 -0
  106. data/examples/media/CptnRuby.png +0 -0
  107. data/examples/media/Cursor.png +0 -0
  108. data/examples/media/Earth.png +0 -0
  109. data/examples/media/Explosion.wav +0 -0
  110. data/examples/media/LargeStar.png +0 -0
  111. data/examples/media/Sky.jpg +0 -0
  112. data/examples/media/Smoke.png +0 -0
  113. data/examples/media/Soldier.png +0 -0
  114. data/examples/media/Space.png +0 -0
  115. data/examples/media/Star.png +0 -0
  116. data/examples/media/Starfighter.bmp +0 -0
  117. data/linux/Makefile.in +98 -0
  118. data/linux/configure +5658 -0
  119. data/linux/configure.ac +126 -0
  120. data/linux/extconf.rb +11 -0
  121. data/mac/English.lproj/InfoPlist.strings +0 -0
  122. data/mac/Gosu-Info.plist +26 -0
  123. data/mac/Gosu.xcodeproj/project.pbxproj +1194 -0
  124. data/mac/RubyGosu Template-Info.plist +26 -0
  125. data/mac/libboost_thread_1_34_1_universal.a +0 -0
  126. data/mac/libboost_thread_d_1_34_1_universal.a +0 -0
  127. data/mac/libfmod_universal.a +0 -0
  128. data/mac/libpng_universal.a +0 -0
  129. data/mac/libz_universal.a +0 -0
  130. data/reference/Async_8hpp-source.html +70 -0
  131. data/reference/Audio_8hpp-source.html +114 -0
  132. data/reference/Audio_8hpp.html +50 -0
  133. data/reference/AutoLink_8hpp-source.html +38 -0
  134. data/reference/AutoLink_8hpp.html +34 -0
  135. data/reference/Bitmap_8hpp-source.html +85 -0
  136. data/reference/Bitmap_8hpp.html +58 -0
  137. data/reference/ButtonsMac_8hpp-source.html +133 -0
  138. data/reference/ButtonsWin_8hpp-source.html +133 -0
  139. data/reference/ButtonsX_8hpp-source.html +134 -0
  140. data/reference/Color_8hpp-source.html +169 -0
  141. data/reference/Color_8hpp.html +85 -0
  142. data/reference/Directories_8hpp-source.html +42 -0
  143. data/reference/Directories_8hpp.html +46 -0
  144. data/reference/Font_8hpp-source.html +65 -0
  145. data/reference/Font_8hpp.html +41 -0
  146. data/reference/Fwd_8hpp-source.html +52 -0
  147. data/reference/Fwd_8hpp.html +37 -0
  148. data/reference/Gosu_8hpp-source.html +48 -0
  149. data/reference/Gosu_8hpp.html +34 -0
  150. data/reference/GraphicsBase_8hpp-source.html +57 -0
  151. data/reference/GraphicsBase_8hpp.html +56 -0
  152. data/reference/Graphics_8hpp-source.html +96 -0
  153. data/reference/Graphics_8hpp.html +53 -0
  154. data/reference/IO_8hpp-source.html +255 -0
  155. data/reference/IO_8hpp.html +74 -0
  156. data/reference/ImageData_8hpp-source.html +62 -0
  157. data/reference/ImageData_8hpp.html +43 -0
  158. data/reference/Image_8hpp-source.html +126 -0
  159. data/reference/Image_8hpp.html +48 -0
  160. data/reference/Input_8hpp-source.html +118 -0
  161. data/reference/Input_8hpp.html +50 -0
  162. data/reference/Math_8hpp-source.html +92 -0
  163. data/reference/Math_8hpp.html +74 -0
  164. data/reference/Platform_8hpp-source.html +83 -0
  165. data/reference/Platform_8hpp.html +73 -0
  166. data/reference/RotFlip_8hpp-source.html +138 -0
  167. data/reference/RotFlip_8hpp.html +77 -0
  168. data/reference/Sockets_8hpp-source.html +130 -0
  169. data/reference/Sockets_8hpp.html +66 -0
  170. data/reference/TextInput_8hpp-source.html +64 -0
  171. data/reference/TextInput_8hpp.html +41 -0
  172. data/reference/Text_8hpp-source.html +51 -0
  173. data/reference/Text_8hpp.html +46 -0
  174. data/reference/Timing_8hpp-source.html +36 -0
  175. data/reference/Timing_8hpp.html +42 -0
  176. data/reference/Utility_8hpp-source.html +44 -0
  177. data/reference/Utility_8hpp.html +48 -0
  178. data/reference/WinUtility_8hpp-source.html +79 -0
  179. data/reference/WinUtility_8hpp.html +64 -0
  180. data/reference/Window_8hpp-source.html +91 -0
  181. data/reference/Window_8hpp.html +41 -0
  182. data/reference/annotated.html +51 -0
  183. data/reference/classGosu_1_1Audio-members.html +34 -0
  184. data/reference/classGosu_1_1Audio.html +46 -0
  185. data/reference/classGosu_1_1Bitmap-members.html +44 -0
  186. data/reference/classGosu_1_1Bitmap.html +263 -0
  187. data/reference/classGosu_1_1Buffer-members.html +44 -0
  188. data/reference/classGosu_1_1Buffer.html +78 -0
  189. data/reference/classGosu_1_1Buffer.png +0 -0
  190. data/reference/classGosu_1_1Button-members.html +36 -0
  191. data/reference/classGosu_1_1Button.html +143 -0
  192. data/reference/classGosu_1_1Color-members.html +56 -0
  193. data/reference/classGosu_1_1Color.html +387 -0
  194. data/reference/classGosu_1_1File-members.html +41 -0
  195. data/reference/classGosu_1_1File.html +69 -0
  196. data/reference/classGosu_1_1File.png +0 -0
  197. data/reference/classGosu_1_1Font-members.html +39 -0
  198. data/reference/classGosu_1_1Font.html +309 -0
  199. data/reference/classGosu_1_1Graphics-members.html +50 -0
  200. data/reference/classGosu_1_1Graphics.html +234 -0
  201. data/reference/classGosu_1_1Image-members.html +45 -0
  202. data/reference/classGosu_1_1Image.html +518 -0
  203. data/reference/classGosu_1_1ImageData-members.html +37 -0
  204. data/reference/classGosu_1_1ImageData.html +60 -0
  205. data/reference/classGosu_1_1Input-members.html +44 -0
  206. data/reference/classGosu_1_1Input.html +223 -0
  207. data/reference/classGosu_1_1MessageSocket-members.html +40 -0
  208. data/reference/classGosu_1_1MessageSocket.html +233 -0
  209. data/reference/classGosu_1_1Resource-members.html +39 -0
  210. data/reference/classGosu_1_1Resource.html +116 -0
  211. data/reference/classGosu_1_1Resource.png +0 -0
  212. data/reference/classGosu_1_1Sample-members.html +37 -0
  213. data/reference/classGosu_1_1Sample.html +200 -0
  214. data/reference/classGosu_1_1SampleInstance-members.html +38 -0
  215. data/reference/classGosu_1_1SampleInstance.html +169 -0
  216. data/reference/classGosu_1_1Song-members.html +43 -0
  217. data/reference/classGosu_1_1Song.html +260 -0
  218. data/reference/classGosu_1_1TextInput-members.html +38 -0
  219. data/reference/classGosu_1_1TextInput.html +121 -0
  220. data/reference/classGosu_1_1Window-members.html +50 -0
  221. data/reference/classGosu_1_1Window.html +271 -0
  222. data/reference/doxyfile +233 -0
  223. data/reference/doxygen.css +433 -0
  224. data/reference/doxygen.png +0 -0
  225. data/reference/files.html +54 -0
  226. data/reference/functions.html +236 -0
  227. data/reference/functions_enum.html +45 -0
  228. data/reference/functions_func.html +227 -0
  229. data/reference/functions_vars.html +47 -0
  230. data/reference/hierarchy.html +53 -0
  231. data/reference/index.html +26 -0
  232. data/reference/namespaceGosu.html +2890 -0
  233. data/reference/namespaceGosu_1_1Colors.html +70 -0
  234. data/reference/namespaceGosu_1_1Win.html +275 -0
  235. data/reference/namespacemembers.html +216 -0
  236. data/reference/namespacemembers_enum.html +52 -0
  237. data/reference/namespacemembers_eval.html +54 -0
  238. data/reference/namespacemembers_func.html +185 -0
  239. data/reference/namespacemembers_type.html +46 -0
  240. data/reference/namespacemembers_vars.html +46 -0
  241. data/reference/namespaces.html +35 -0
  242. data/reference/tab_b.gif +0 -0
  243. data/reference/tab_l.gif +0 -0
  244. data/reference/tab_r.gif +0 -0
  245. data/reference/tabs.css +102 -0
  246. data/windows/Gosu.sln +29 -0
  247. data/windows/Gosu.vcproj +553 -0
  248. data/windows/RubyGosu.vcproj +138 -0
  249. metadata +305 -0
@@ -0,0 +1,34 @@
1
+ #ifndef GOSUIMPL_GRAPHICS_TEXTURE_HPP
2
+ #define GOSUIMPL_GRAPHICS_TEXTURE_HPP
3
+
4
+ #include <Gosu/Fwd.hpp>
5
+ #include <GosuImpl/Graphics/Common.hpp>
6
+ #include <GosuImpl/Graphics/TexChunk.hpp>
7
+ #include <GosuImpl/Graphics/BlockAllocator.hpp>
8
+ #include <boost/cstdint.hpp>
9
+ #include <boost/shared_ptr.hpp>
10
+ #include <vector>
11
+
12
+ namespace Gosu
13
+ {
14
+ class Texture
15
+ {
16
+ BlockAllocator allocator;
17
+ GLuint name;
18
+ unsigned num;
19
+
20
+ public:
21
+ static unsigned maxTextureSize();
22
+
23
+ Texture(unsigned size);
24
+ ~Texture();
25
+ unsigned size() const;
26
+ GLuint texName() const;
27
+ std::auto_ptr<TexChunk>
28
+ tryAlloc(Graphics& graphics, DrawOpQueue& queue, boost::shared_ptr<Texture> ptr, const Bitmap& bmp,
29
+ unsigned srcX, unsigned srcY, unsigned srcWidth, unsigned srcHeight, unsigned padding);
30
+ void free(unsigned x, unsigned y);
31
+ };
32
+ }
33
+
34
+ #endif
@@ -0,0 +1,48 @@
1
+ #include <Gosu/IO.hpp>
2
+ #include <cassert>
3
+ #include <cstring>
4
+
5
+ void Gosu::Reader::read(void* dest, std::size_t length)
6
+ {
7
+ res->read(pos, length, dest);
8
+ seek(length);
9
+ }
10
+
11
+ void Gosu::Writer::write(const void* source, std::size_t length)
12
+ {
13
+ // Try to resize the source if necessary.
14
+ if (pos + length > res->size())
15
+ res->resize(pos + length);
16
+
17
+ res->write(pos, length, source);
18
+ seek(length);
19
+ }
20
+
21
+ void Gosu::Buffer::read(std::size_t offset, std::size_t length,
22
+ void* destBuffer) const
23
+ {
24
+ assert(offset + length <= size());
25
+ if (length)
26
+ std::memcpy(destBuffer, &buf[offset], length);
27
+ }
28
+
29
+ void Gosu::Buffer::write(std::size_t offset, std::size_t length,
30
+ const void* sourceBuffer)
31
+ {
32
+ assert(offset + length <= size());
33
+ if (length)
34
+ std::memcpy(&buf[offset], sourceBuffer, length);
35
+ }
36
+
37
+ void Gosu::loadFile(Buffer& buffer, const std::wstring& filename)
38
+ {
39
+ File file(filename);
40
+ buffer.resize(file.size());
41
+ file.read(0, buffer.size(), buffer.data());
42
+ }
43
+
44
+ void Gosu::saveFile(const Buffer& buffer, const std::wstring& filename)
45
+ {
46
+ File file(filename, fmReplace);
47
+ file.write(0, buffer.size(), buffer.data());
48
+ }
@@ -0,0 +1,677 @@
1
+ #import <AppKit/AppKit.h>
2
+ #import <Carbon/Carbon.h>
3
+ #include <Gosu/Input.hpp>
4
+ #include <Gosu/TextInput.hpp>
5
+ #include <Gosu/Utility.hpp>
6
+ #include <IOKit/hidsystem/IOLLEvent.h>
7
+ #include <boost/array.hpp>
8
+ #include <boost/cstdint.hpp>
9
+ #include <map>
10
+ #include <string>
11
+ #include <vector>
12
+
13
+ #include <mach/mach.h>
14
+ #include <mach/mach_error.h>
15
+ #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
16
+ #include <CoreFoundation/CoreFoundation.h>
17
+ #include <IOKit/hid/IOHIDLib.h>
18
+ #include <IOKit/hid/IOHIDKeys.h>
19
+ #include <IOKit/IOKitLib.h>
20
+ #include <IOKit/IOCFPlugIn.h>
21
+ #include <stdexcept>
22
+ #include <vector>
23
+ #include <boost/utility.hpp>
24
+ #include <boost/shared_ptr.hpp>
25
+
26
+ // USB Gamepad code, likely to be moved somewhere else later.
27
+ // This is Frankencode until the Input redesign happens.
28
+ namespace {
29
+ using namespace std;
30
+ using namespace Gosu;
31
+ using boost::shared_ptr;
32
+
33
+ class CFScope : boost::noncopyable
34
+ {
35
+ CFTypeRef ref;
36
+ public:
37
+ CFScope(CFTypeRef ref) : ref(ref) {}
38
+ ~CFScope() { CFRelease(ref); }
39
+ };
40
+
41
+ class IOScope : boost::noncopyable
42
+ {
43
+ io_object_t ref;
44
+ public:
45
+ IOScope(io_object_t ref) : ref(ref) {}
46
+ ~IOScope() { IOObjectRelease(ref); }
47
+ };
48
+
49
+ template<typename Negatable>
50
+ void checkTrue(Negatable cond, const char* message = "work")
51
+ {
52
+ if (!cond)
53
+ throw runtime_error(string("HID system failed to ") + message);
54
+ }
55
+
56
+ void checkIO(IOReturn val, const char* message = "work")
57
+ {
58
+ checkTrue(val == kIOReturnSuccess, message);
59
+ }
60
+
61
+ string getDictString(CFMutableDictionaryRef dict, CFStringRef key, const char* what)
62
+ {
63
+ char buf[256];
64
+ CFStringRef str =
65
+ (CFStringRef)CFDictionaryGetValue(dict, key);
66
+ checkTrue(str && CFStringGetCString(str, buf, sizeof buf, CFStringGetSystemEncoding()),
67
+ what);
68
+ return buf;
69
+ }
70
+
71
+ SInt32 getDictSInt32(CFMutableDictionaryRef dict, CFStringRef key, const char* what)
72
+ {
73
+ SInt32 value;
74
+ CFNumberRef number =
75
+ (CFNumberRef)CFDictionaryGetValue(dict, key);
76
+ checkTrue(number && CFNumberGetValue(number, kCFNumberSInt32Type, &value),
77
+ what);
78
+ return value;
79
+ }
80
+
81
+ struct Axis
82
+ {
83
+ IOHIDElementCookie cookie;
84
+ long min, max;
85
+ enum Role { mainX, mainY } role;
86
+
87
+ // Some devices report more axis than they have, with random constant values.
88
+ // An axis has to go into, and out of the neutral zone so it will be reported.
89
+ bool wasNeutralOnce;
90
+
91
+ Axis(CFMutableDictionaryRef dict, Role role)
92
+ : role(role), wasNeutralOnce(false)
93
+ {
94
+ cookie = (IOHIDElementCookie)getDictSInt32(dict, CFSTR(kIOHIDElementCookieKey),
95
+ "get an element cookie");
96
+ min = getDictSInt32(dict, CFSTR(kIOHIDElementMinKey),
97
+ "get a min value");
98
+ max = getDictSInt32(dict, CFSTR(kIOHIDElementMaxKey),
99
+ "get a max value");
100
+ }
101
+ };
102
+
103
+ struct Hat
104
+ {
105
+ IOHIDElementCookie cookie;
106
+ enum { fourWay, eightWay, unknown } kind;
107
+ long min;
108
+
109
+ Hat(CFMutableDictionaryRef dict)
110
+ {
111
+ cookie = (IOHIDElementCookie)getDictSInt32(dict, CFSTR(kIOHIDElementCookieKey),
112
+ "an element cookie");
113
+ min = getDictSInt32(dict, CFSTR(kIOHIDElementMinKey),
114
+ "a min value");
115
+ SInt32 max = getDictSInt32(dict, CFSTR(kIOHIDElementMaxKey),
116
+ "a max value");
117
+ if ((max - min) == 3)
118
+ kind = fourWay;
119
+ else if ((max - min) == 7)
120
+ kind = eightWay;
121
+ else
122
+ kind = unknown;
123
+ }
124
+ };
125
+
126
+ struct Button
127
+ {
128
+ IOHIDElementCookie cookie;
129
+
130
+ Button(CFMutableDictionaryRef dict)
131
+ {
132
+ cookie = (IOHIDElementCookie)getDictSInt32(dict, CFSTR(kIOHIDElementCookieKey),
133
+ "element cookie");
134
+ }
135
+ };
136
+
137
+ struct Device
138
+ {
139
+ shared_ptr<IOHIDDeviceInterface*> interface;
140
+
141
+ std::string name;
142
+ vector<Axis> axis;
143
+ vector<Hat> hats;
144
+ vector<Button> buttons;
145
+ };
146
+
147
+ class System
148
+ {
149
+ vector<Device> devices;
150
+
151
+ static void closeAndReleaseInterface(IOHIDDeviceInterface** ptr)
152
+ {
153
+ (*ptr)->close(ptr); // Won't hurt if open() wasn't called
154
+ (*ptr)->Release(ptr);
155
+ }
156
+
157
+ static void eraseDevice(void* target, IOReturn, void* refcon, void*)
158
+ {
159
+ System& self = *static_cast<System*>(target);
160
+ for (unsigned i = 0; i < self.devices.size(); ++i)
161
+ if (self.devices[i].interface.get() == refcon)
162
+ {
163
+ self.devices.erase(self.devices.begin() + i);
164
+ return;
165
+ }
166
+ assert(false);
167
+ }
168
+
169
+ bool isDeviceInteresting(CFMutableDictionaryRef properties)
170
+ {
171
+ // Get usage page/usage.
172
+ SInt32 page = getDictSInt32(properties, CFSTR(kIOHIDPrimaryUsagePageKey),
173
+ "a usage page");
174
+ SInt32 usage = getDictSInt32(properties, CFSTR(kIOHIDPrimaryUsageKey),
175
+ "a usage value");
176
+ // Device uninteresting?
177
+ return page == kHIDPage_GenericDesktop &&
178
+ (usage == kHIDUsage_GD_Joystick ||
179
+ usage == kHIDUsage_GD_GamePad ||
180
+ usage == kHIDUsage_GD_MultiAxisController);
181
+ }
182
+
183
+ shared_ptr<IOHIDDeviceInterface*> getDeviceInterface(io_registry_entry_t object)
184
+ {
185
+ IOCFPlugInInterface** intermediate = 0;
186
+ SInt32 theScore;
187
+ checkIO(IOCreatePlugInInterfaceForService(object, kIOHIDDeviceUserClientTypeID,
188
+ kIOCFPlugInInterfaceID, &intermediate, &theScore),
189
+ "get intermediate device interface");
190
+
191
+ IOHIDDeviceInterface** rawResult = 0;
192
+ HRESULT ret = (*intermediate)->QueryInterface(intermediate,
193
+ CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
194
+ reinterpret_cast<void**>(&rawResult));
195
+ (*intermediate)->Release(intermediate);
196
+ if (ret != S_OK)
197
+ checkTrue(false, "get real device interface through intermediate");
198
+
199
+ // Yay - got it safe in here.
200
+ shared_ptr<IOHIDDeviceInterface*> result(rawResult, closeAndReleaseInterface);
201
+
202
+ checkIO((*result)->open(result.get(), 0));
203
+ checkIO((*result)->setRemovalCallback(result.get(), eraseDevice, result.get(), this));
204
+
205
+ return result;
206
+ }
207
+
208
+ static void addElement(const void* value, void* parameter)
209
+ {
210
+ CFMutableDictionaryRef dict = (CFMutableDictionaryRef)value;
211
+ Device& target = *static_cast<Device*>(parameter);
212
+
213
+ SInt32 elementType = getDictSInt32(dict, CFSTR(kIOHIDElementTypeKey),
214
+ ("an element type on " + target.name).c_str());
215
+ SInt32 usagePage = getDictSInt32(dict, CFSTR(kIOHIDElementUsagePageKey),
216
+ ("an element page on " + target.name).c_str());
217
+ SInt32 usage = getDictSInt32(dict, CFSTR(kIOHIDElementUsageKey),
218
+ ("an element usage on " + target.name).c_str());
219
+
220
+ if ((elementType == kIOHIDElementTypeInput_Misc) ||
221
+ (elementType == kIOHIDElementTypeInput_Button) ||
222
+ (elementType == kIOHIDElementTypeInput_Axis))
223
+ {
224
+ switch (usagePage)
225
+ {
226
+ case kHIDPage_GenericDesktop:
227
+ switch (usage)
228
+ {
229
+ case kHIDUsage_GD_Y:
230
+ case kHIDUsage_GD_Ry:
231
+ target.axis.push_back(Axis(dict, Axis::mainY));
232
+ break;
233
+ case kHIDUsage_GD_X:
234
+ case kHIDUsage_GD_Rx:
235
+ case kHIDUsage_GD_Z:
236
+ case kHIDUsage_GD_Rz:
237
+ case kHIDUsage_GD_Slider:
238
+ case kHIDUsage_GD_Dial:
239
+ case kHIDUsage_GD_Wheel:
240
+ target.axis.push_back(Axis(dict, Axis::mainX));
241
+ break;
242
+ case kHIDUsage_GD_Hatswitch:
243
+ target.hats.push_back(Hat(dict));
244
+ break;
245
+ }
246
+ break;
247
+ case kHIDPage_Button:
248
+ target.buttons.push_back(Button(dict));
249
+ break;
250
+ }
251
+ }
252
+ else if (elementType == kIOHIDElementTypeCollection)
253
+ addElementCollection(target, dict);
254
+ }
255
+
256
+ static void addElementCollection(Device& target, CFMutableDictionaryRef properties)
257
+ {
258
+ CFArrayRef array =
259
+ (CFArrayRef)CFDictionaryGetValue(properties, CFSTR(kIOHIDElementKey));
260
+ checkTrue(array,
261
+ ("get an element list for " + target.name).c_str());
262
+
263
+ CFRange range = { 0, CFArrayGetCount(array) };
264
+ CFArrayApplyFunction(array, range, addElement, &target);
265
+ }
266
+
267
+ void addDevice(io_object_t object)
268
+ {
269
+ // Get handle to device properties.
270
+ CFMutableDictionaryRef properties;
271
+ IORegistryEntryCreateCFProperties(object, &properties,
272
+ kCFAllocatorDefault, kNilOptions);
273
+ if (!properties)
274
+ return;
275
+ CFScope guard(properties);
276
+
277
+ if (!isDeviceInteresting(properties))
278
+ return;
279
+
280
+ Device newDevice;
281
+ newDevice.interface = getDeviceInterface(object);
282
+ newDevice.name = getDictString(properties, CFSTR(kIOHIDProductKey),
283
+ "a product name");
284
+ addElementCollection(newDevice, properties);
285
+ devices.push_back(newDevice);
286
+ }
287
+
288
+ public:
289
+ System()
290
+ {
291
+ mach_port_t masterPort;
292
+ checkIO(IOMasterPort(bootstrap_port, &masterPort),
293
+ "get master port");
294
+
295
+ CFMutableDictionaryRef hidDeviceKey =
296
+ IOServiceMatching(kIOHIDDeviceKey);
297
+ checkTrue(hidDeviceKey,
298
+ "build device list");
299
+
300
+ io_iterator_t iterator;
301
+ checkIO(IOServiceGetMatchingServices(masterPort, hidDeviceKey, &iterator),
302
+ "set up HID iterator");
303
+ IOScope guard(iterator);
304
+
305
+ while (io_registry_entry_t deviceObject = IOIteratorNext(iterator))
306
+ {
307
+ IOScope guard(deviceObject);
308
+ addDevice(deviceObject);
309
+ }
310
+ }
311
+
312
+ unsigned countDevices() const
313
+ {
314
+ return devices.size();
315
+ }
316
+
317
+ const Device& getDevice(unsigned i) const
318
+ {
319
+ return devices.at(i);
320
+ }
321
+
322
+ boost::array<bool, gpNum> poll()
323
+ {
324
+ boost::array<bool, gpNum> result;
325
+ result.assign(false);
326
+
327
+ IOHIDEventStruct event;
328
+ for (int dev = 0; dev < devices.size(); ++dev)
329
+ {
330
+ // Axis
331
+ for (int ax = 0; ax < devices[dev].axis.size(); ++ax)
332
+ {
333
+ checkIO((*devices[dev].interface)->getElementValue(
334
+ devices[dev].interface.get(),
335
+ devices[dev].axis[ax].cookie, &event));
336
+
337
+ Axis& a = devices[dev].axis[ax];
338
+ if (event.value < (3 * a.min + 1 * a.max) / 4.0)
339
+ {
340
+ if (a.wasNeutralOnce)
341
+ result[(a.role == Axis::mainX ? gpLeft : gpUp) - gpRangeBegin] = true;
342
+ }
343
+ else if (event.value > (1 * a.min + 3 * a.max) / 4.0)
344
+ {
345
+ if (a.wasNeutralOnce)
346
+ result[(a.role == Axis::mainX ? gpRight : gpDown) - gpRangeBegin] = true;
347
+ }
348
+ else
349
+ a.wasNeutralOnce = true;
350
+ }
351
+
352
+ // Hats (merge into axis)
353
+ for (int hat = 0; hat < devices[dev].hats.size(); ++hat)
354
+ {
355
+ checkIO((*devices[dev].interface)->getElementValue(
356
+ devices[dev].interface.get(),
357
+ devices[dev].hats[hat].cookie, &event));
358
+
359
+ // In case device does not start at 0 as expected.
360
+ event.value -= devices[dev].hats[hat].min;
361
+
362
+ // Treat all hats as being 8-way.
363
+ if (devices[dev].hats[hat].kind == Hat::fourWay)
364
+ event.value *= 2;
365
+
366
+ switch (event.value)
367
+ {
368
+ // Must...resist...doing...crappy...fallthrough...magic...
369
+ case 0:
370
+ result[gpUp - gpRangeBegin] = true;
371
+ break;
372
+ case 1:
373
+ result[gpUp - gpRangeBegin] = true;
374
+ result[gpRight - gpRangeBegin] = true;
375
+ break;
376
+ case 2:
377
+ result[gpRight - gpRangeBegin] = true;
378
+ break;
379
+ case 3:
380
+ result[gpDown - gpRangeBegin] = true;
381
+ result[gpRight - gpRangeBegin] = true;
382
+ break;
383
+ case 4:
384
+ result[gpDown - gpRangeBegin] = true;
385
+ break;
386
+ case 5:
387
+ result[gpDown - gpRangeBegin] = true;
388
+ result[gpLeft - gpRangeBegin] = true;
389
+ break;
390
+ case 6:
391
+ result[gpLeft - gpRangeBegin] = true;
392
+ break;
393
+ case 7:
394
+ result[gpUp - gpRangeBegin] = true;
395
+ result[gpLeft - gpRangeBegin] = true;
396
+ break;
397
+ }
398
+ }
399
+
400
+ // Buttons
401
+ for (int btn = 0; btn < devices[dev].buttons.size() && btn < 16; ++btn)
402
+ {
403
+ checkIO((*devices[dev].interface)->getElementValue(
404
+ devices[dev].interface.get(),
405
+ devices[dev].buttons[btn].cookie, &event));
406
+
407
+ if (event.value >= 1)
408
+ result[gpButton0 + btn - gpRangeBegin] = true;
409
+ }
410
+ }
411
+
412
+ return result;
413
+ }
414
+ };
415
+ }
416
+
417
+ // Needed for char translation.
418
+ namespace Gosu
419
+ {
420
+ std::wstring macRomanToWstring(const std::string& s);
421
+ }
422
+
423
+ namespace {
424
+ const unsigned numScancodes = 128;
425
+
426
+ boost::array<wchar_t, numScancodes> idChars;
427
+ std::map<wchar_t, unsigned> charIds;
428
+
429
+ void initCharTranslation()
430
+ {
431
+ static bool initializedCharData = false;
432
+ if (initializedCharData)
433
+ return;
434
+ initializedCharData = true;
435
+
436
+ idChars.assign(0);
437
+
438
+ const void* KCHR = reinterpret_cast<const void*>(GetScriptManagerVariable(smKCHRCache));
439
+ if (!KCHR)
440
+ return;
441
+
442
+ for (int code = numScancodes - 1; code >= 0; --code)
443
+ {
444
+ UInt32 deadKeyState = 0;
445
+ UInt32 value = KeyTranslate(KCHR, code, &deadKeyState);
446
+ // If this key triggered a dead key, hit it again to obtain the actual value.
447
+ if (deadKeyState != 0)
448
+ value = KeyTranslate(KCHR, code, &deadKeyState);
449
+
450
+ // No character! Pity.
451
+ if (value == 0)
452
+ continue;
453
+
454
+ // Ignore special characters except newline.
455
+ if (value == 3)
456
+ value = 13; // convert Enter to Return
457
+ if (value < 32 && value != 13)
458
+ continue;
459
+
460
+ // Now we have a character which is *not* limited to the ASCII range. To correctly
461
+ // translate this into a wchar_t, we need to convert it based on the current locale.
462
+ // TODO: That locale stuff should be explicit. Locales always cause trouble.
463
+
464
+ std::string str(1, char(value));
465
+ wchar_t ch = Gosu::macRomanToWstring(str).at(0);
466
+
467
+ idChars[code] = ch;
468
+ charIds[ch] = code;
469
+ }
470
+ }
471
+
472
+ boost::array<bool, Gosu::numButtons> buttonStates;
473
+ }
474
+
475
+ struct Gosu::Input::Impl
476
+ {
477
+ Input& input;
478
+ NSWindow* window;
479
+ TextInput* textInput;
480
+ double mouseX, mouseY;
481
+ double mouseFactorX, mouseFactorY;
482
+
483
+ unsigned currentMods;
484
+
485
+ struct WaitingButton
486
+ {
487
+ Button btn;
488
+ bool down;
489
+ WaitingButton(unsigned btnId, bool down) : btn(btnId), down(down) {}
490
+ };
491
+ std::vector<WaitingButton> queue;
492
+
493
+ Impl(Input& input)
494
+ : input(input), textInput(0), mouseFactorX(1), mouseFactorY(1), currentMods(0)
495
+ {
496
+ }
497
+
498
+ void enqueue(unsigned btnId, bool down)
499
+ {
500
+ queue.push_back(WaitingButton(btnId, down));
501
+ }
502
+
503
+ void updateMods(unsigned newMods)
504
+ {
505
+ const unsigned ids[8] = { kbLeftShift, kbLeftControl,
506
+ kbLeftAlt, kbLeftMeta,
507
+ kbRightShift, kbRightControl,
508
+ kbRightAlt, kbRightMeta };
509
+ const unsigned bits[8] = { NX_DEVICELSHIFTKEYMASK, NX_DEVICELCTLKEYMASK,
510
+ NX_DEVICELALTKEYMASK, NX_DEVICELCMDKEYMASK,
511
+ NX_DEVICERSHIFTKEYMASK, NX_DEVICERCTLKEYMASK,
512
+ NX_DEVICERALTKEYMASK, NX_DEVICERCMDKEYMASK };
513
+
514
+ for (unsigned i = 0; i < 8; ++i)
515
+ if ((currentMods & bits[i]) != (newMods & bits[i]))
516
+ enqueue(ids[i], (newMods & bits[i]) != 0);
517
+
518
+ currentMods = newMods;
519
+ }
520
+ };
521
+
522
+ Gosu::Input::Input(void* window)
523
+ : pimpl(new Impl(*this))
524
+ {
525
+ pimpl->window = static_cast<NSWindow*>(window);
526
+ buttonStates.assign(false);
527
+ initCharTranslation();
528
+ }
529
+
530
+ Gosu::Input::~Input()
531
+ {
532
+ }
533
+
534
+ bool Gosu::Input::feedNSEvent(void* event)
535
+ {
536
+ NSEvent* ev = (NSEvent*)event;
537
+ unsigned type = [ev type];
538
+
539
+ if (type == NSKeyDown && textInput() && textInput()->feedNSEvent(event))
540
+ return true;
541
+
542
+ if (type == NSKeyDown && [ev isARepeat])
543
+ return false;
544
+
545
+ // Process modifier keys.
546
+ unsigned mods = [ev modifierFlags];
547
+ if (mods != pimpl->currentMods)
548
+ pimpl->updateMods(mods);
549
+
550
+
551
+ // Handle mouse input.
552
+ switch (type)
553
+ {
554
+ case NSLeftMouseDown:
555
+ pimpl->enqueue(msLeft, true);
556
+ return true;
557
+ case NSLeftMouseUp:
558
+ pimpl->enqueue(msLeft, false);
559
+ return true;
560
+ case NSRightMouseDown:
561
+ pimpl->enqueue(msRight, true);
562
+ return true;
563
+ case NSRightMouseUp:
564
+ pimpl->enqueue(msRight, false);
565
+ return true;
566
+ case NSScrollWheel:
567
+ if ([ev deltaY] > 0)
568
+ {
569
+ pimpl->enqueue(msWheelUp, true);
570
+ pimpl->enqueue(msWheelUp, false);
571
+ }
572
+ else if ([ev deltaY] < 0)
573
+ {
574
+ pimpl->enqueue(msWheelDown, true);
575
+ pimpl->enqueue(msWheelDown, false);
576
+ }
577
+ else
578
+ return false;
579
+ return true;
580
+ }
581
+
582
+ // Handle other keys.
583
+ if (type == NSKeyDown || type == NSKeyUp)
584
+ {
585
+ pimpl->enqueue([ev keyCode], type == NSKeyDown);
586
+ return true;
587
+ }
588
+
589
+ return false;
590
+ }
591
+
592
+ wchar_t Gosu::Input::idToChar(Button btn)
593
+ {
594
+ if (btn.getId() < numScancodes)
595
+ return idChars[btn.getId()];
596
+ else
597
+ return 0;
598
+ }
599
+
600
+ Gosu::Button Gosu::Input::charToId(wchar_t ch)
601
+ {
602
+ std::map<wchar_t, unsigned>::const_iterator iter = charIds.find(ch);
603
+ if (iter == charIds.end())
604
+ return noButton;
605
+ return Gosu::Button(iter->second);
606
+ }
607
+
608
+ bool Gosu::Input::down(Gosu::Button btn) const
609
+ {
610
+ return buttonStates.at(btn.getId());
611
+ }
612
+
613
+ double Gosu::Input::mouseX() const
614
+ {
615
+ return pimpl->mouseX * pimpl->mouseFactorX;
616
+ }
617
+
618
+ double Gosu::Input::mouseY() const
619
+ {
620
+ return pimpl->mouseY * pimpl->mouseFactorY;
621
+ }
622
+
623
+ void Gosu::Input::setMouseFactors(double factorX, double factorY)
624
+ {
625
+ pimpl->mouseFactorX = factorX;
626
+ pimpl->mouseFactorY = factorY;
627
+ }
628
+
629
+ void Gosu::Input::update()
630
+ {
631
+ NSPoint mousePos = [NSEvent mouseLocation];
632
+ if (pimpl->window)
633
+ {
634
+ mousePos = [pimpl->window convertScreenToBase: mousePos];
635
+ mousePos.y = [[pimpl->window contentView] frame].size.height - mousePos.y;
636
+ }
637
+ else
638
+ mousePos.y = CGDisplayBounds(CGMainDisplayID()).size.height - mousePos.y;
639
+ pimpl->mouseX = mousePos.x;
640
+ pimpl->mouseY = mousePos.y;
641
+
642
+ for (unsigned i = 0; i < pimpl->queue.size(); ++i)
643
+ {
644
+ Impl::WaitingButton& wb = pimpl->queue[i];
645
+ buttonStates.at(wb.btn.getId()) = wb.down;
646
+ if (wb.down && onButtonDown)
647
+ onButtonDown(wb.btn);
648
+ else if (!wb.down && onButtonUp)
649
+ onButtonUp(wb.btn);
650
+ }
651
+ pimpl->queue.clear();
652
+
653
+ static System sys;
654
+ boost::array<bool, gpNum> gpState = sys.poll();
655
+ for (unsigned i = 0; i < gpNum; ++i)
656
+ {
657
+ if (buttonStates[i + gpRangeBegin] != gpState[i])
658
+ {
659
+ buttonStates[i + gpRangeBegin] = gpState[i];
660
+ if (gpState[i] && onButtonDown)
661
+ onButtonDown(Button(gpRangeBegin + i));
662
+ else if (!gpState[i] && onButtonUp)
663
+ onButtonUp(Button(gpRangeBegin + i));
664
+ }
665
+ }
666
+ }
667
+
668
+ Gosu::TextInput* Gosu::Input::textInput() const
669
+ {
670
+ return pimpl->textInput;
671
+ }
672
+
673
+ void Gosu::Input::setTextInput(TextInput* textInput)
674
+ {
675
+ pimpl->textInput = textInput;
676
+ }
677
+