gosu 0.7.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+