shoes 3.0.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 (275) hide show
  1. data/.gitignore +12 -0
  2. data/CHANGELOG +29 -0
  3. data/COPYING +30 -0
  4. data/README.md +76 -0
  5. data/README.old +117 -0
  6. data/Rakefile +130 -0
  7. data/Rakefile.bk +651 -0
  8. data/app.yaml +7 -0
  9. data/bin/main.skel +104 -0
  10. data/bugs/issue-012e9468ddc7b0cb7e1503413a8a11c4f8707b67.yaml +21 -0
  11. data/bugs/issue-0711ff8b67baa63586f11d0096fee9dd4436bf58.yaml +23 -0
  12. data/bugs/issue-07f87998d93eb401e22395c11085676389b935c4.yaml +22 -0
  13. data/bugs/issue-0bff2d80008803cbc7efbcdacdc60ef2163664d2.yaml +22 -0
  14. data/bugs/issue-0c66f223d19efbca7b692f3b91961f407ad7abec.yaml +24 -0
  15. data/bugs/issue-183bc3c7a8f575aff2b24e0bf22aa0dfcc8e4fc2.yaml +29 -0
  16. data/bugs/issue-1bad1e60a04cb3adb6a8f3ded128af2e16b56ffe.yaml +18 -0
  17. data/bugs/issue-1f1d43f76bf4de79d7f94adbae6dd506a9d81633.yaml +41 -0
  18. data/bugs/issue-320924117cadb045cc91f2a6fca922b4d81d4bb5.yaml +20 -0
  19. data/bugs/issue-33e5c8355fbf65252ea9e939714651df8bd8cb3b.yaml +25 -0
  20. data/bugs/issue-36f7c8262a72bcd5e28cfa4ed29328b8237ea79b.yaml +20 -0
  21. data/bugs/issue-39bf04ee50c3bef67b89b8e9beb28657805587ce.yaml +18 -0
  22. data/bugs/issue-3e28ba7754f73d02ef416eef989af707a8c00f01.yaml +23 -0
  23. data/bugs/issue-3f638cc03f41f9c3fdf217355321f1563f8fee45.yaml +22 -0
  24. data/bugs/issue-41e48e14f1ef4f6deb69be68165d7dd3ffd99156.yaml +22 -0
  25. data/bugs/issue-4c9408166a2aa5dd333bbb05520deebf4ded08e0.yaml +22 -0
  26. data/bugs/issue-55c687e108ecd5aeb8e0d0aeb4294984f84acd3e.yaml +18 -0
  27. data/bugs/issue-5811d34692cc292717a58ec1df1f8948cf30d826.yaml +28 -0
  28. data/bugs/issue-5a7c1f59a0526f9cea7fb366867cf1e6ed8ef69d.yaml +22 -0
  29. data/bugs/issue-688a2c2566bef6a03efa57a4ae1f7cfa8fc74444.yaml +21 -0
  30. data/bugs/issue-78b16c7988ade2ef96e716fa8cb9a004bd4ced65.yaml +33 -0
  31. data/bugs/issue-804ee49f9800154eddf0650690c7a9bb5626253f.yaml +27 -0
  32. data/bugs/issue-807dd1c610b2e941a5d454e26b2eac27e89a0ab4.yaml +22 -0
  33. data/bugs/issue-822ee33ec11ebd25f90ea86e8438de7891e63aee.yaml +18 -0
  34. data/bugs/issue-82ff7152cebb8a1cb065b864cb4fe22de2328146.yaml +29 -0
  35. data/bugs/issue-856afbdcdd4970ad54b8ce4a6c017fbaab45f49d.yaml +31 -0
  36. data/bugs/issue-8d49e32fb21522651c67490adb4d80076dbb14e1.yaml +24 -0
  37. data/bugs/issue-91583359653a9d530c1e32be72d874d847a306e7.yaml +21 -0
  38. data/bugs/issue-af41a5229613172764e7d3c98431172db337053d.yaml +20 -0
  39. data/bugs/issue-bd9856b3788dd429c998a5af1b2aae8221cb80b7.yaml +28 -0
  40. data/bugs/issue-be22aef5c564fb7b5e9938343136a9cd6a2edb7b.yaml +22 -0
  41. data/bugs/issue-bf8697523c77c326e7e35fc82cf3dd739ae92310.yaml +27 -0
  42. data/bugs/issue-cdd4b4795f34d6ff46e56ece10f8216a4a5456b2.yaml +25 -0
  43. data/bugs/issue-d9bfb5581d745ef9deb3b9b2e08ef74d1cd43082.yaml +20 -0
  44. data/bugs/issue-e0ce2f687cc096f35715d28b0af88589f4ab6cc6.yaml +18 -0
  45. data/bugs/issue-e4b0aca4a10a574b333aeaecaaf221dbc5ed22c6.yaml +38 -0
  46. data/bugs/issue-f263746594b95ba778455730478ee8df60ee639d.yaml +22 -0
  47. data/bugs/project.yaml +72 -0
  48. data/builddeps.sh +102 -0
  49. data/fonts/Coolvetica.ttf +0 -0
  50. data/fonts/Lacuna.ttf +0 -0
  51. data/gemlib/shoes.rb +1 -0
  52. data/lib/shoes.rb +548 -0
  53. data/lib/shoes/cache.rb +54 -0
  54. data/lib/shoes/chipmunk.rb +35 -0
  55. data/lib/shoes/data.rb +39 -0
  56. data/lib/shoes/help.rb +468 -0
  57. data/lib/shoes/image.rb +25 -0
  58. data/lib/shoes/inspect.rb +128 -0
  59. data/lib/shoes/log.rb +48 -0
  60. data/lib/shoes/minitar.rb +986 -0
  61. data/lib/shoes/override.rb +38 -0
  62. data/lib/shoes/pack.rb +543 -0
  63. data/lib/shoes/search.rb +46 -0
  64. data/lib/shoes/setup.rb +329 -0
  65. data/lib/shoes/shy.rb +131 -0
  66. data/lib/shoes/shybuilder.rb +44 -0
  67. data/lib/shoes/version.rb +3 -0
  68. data/make/darwin/deps.vlc +12 -0
  69. data/make/darwin/dylibs.shoes +22 -0
  70. data/make/darwin/dylibs.video +11 -0
  71. data/make/darwin/env.rb +81 -0
  72. data/make/darwin/tasks.rb +105 -0
  73. data/make/linux/env.rb +65 -0
  74. data/make/linux/tasks.rb +61 -0
  75. data/make/make.rb +85 -0
  76. data/make/mingw/dlls +19 -0
  77. data/make/mingw/env.rb +69 -0
  78. data/make/mingw/tasks.rb +70 -0
  79. data/make/rakefile_common.rb +8 -0
  80. data/manual-snapshots/class-book.png +0 -0
  81. data/manual-snapshots/expert-definr.png +0 -0
  82. data/manual-snapshots/expert-funnies.png +0 -0
  83. data/manual-snapshots/expert-irb.png +0 -0
  84. data/manual-snapshots/expert-minesweeper.png +0 -0
  85. data/manual-snapshots/expert-othello.png +0 -0
  86. data/manual-snapshots/expert-pong.png +0 -0
  87. data/manual-snapshots/expert-tankspank.png +0 -0
  88. data/manual-snapshots/good-arc.png +0 -0
  89. data/manual-snapshots/good-clock.png +0 -0
  90. data/manual-snapshots/good-follow.png +0 -0
  91. data/manual-snapshots/good-reminder.png +0 -0
  92. data/manual-snapshots/good-vjot.png +0 -0
  93. data/manual-snapshots/simple-accordion.png +0 -0
  94. data/manual-snapshots/simple-anim-shapes.png +0 -0
  95. data/manual-snapshots/simple-anim-text.png +0 -0
  96. data/manual-snapshots/simple-arc.png +0 -0
  97. data/manual-snapshots/simple-bounce.png +0 -0
  98. data/manual-snapshots/simple-calc.png +0 -0
  99. data/manual-snapshots/simple-chipmunk.png +0 -0
  100. data/manual-snapshots/simple-control-sizes.png +0 -0
  101. data/manual-snapshots/simple-curve.png +0 -0
  102. data/manual-snapshots/simple-dialogs.png +0 -0
  103. data/manual-snapshots/simple-downloader.png +0 -0
  104. data/manual-snapshots/simple-draw.png +0 -0
  105. data/manual-snapshots/simple-editor.png +0 -0
  106. data/manual-snapshots/simple-form.png +0 -0
  107. data/manual-snapshots/simple-mask.png +0 -0
  108. data/manual-snapshots/simple-menu.png +0 -0
  109. data/manual-snapshots/simple-menu1.png +0 -0
  110. data/manual-snapshots/simple-rubygems.png +0 -0
  111. data/manual-snapshots/simple-slide.png +0 -0
  112. data/manual-snapshots/simple-sphere.png +0 -0
  113. data/manual-snapshots/simple-sqlite3.png +0 -0
  114. data/manual-snapshots/simple-timer.png +0 -0
  115. data/manual-snapshots/simple-video.png +0 -0
  116. data/platform/mac/Info.plist +55 -0
  117. data/platform/mac/build-deps.sh +658 -0
  118. data/platform/mac/command-manual.rb +1 -0
  119. data/platform/mac/deps-osx.patch +159 -0
  120. data/platform/mac/dmg_ds_store +0 -0
  121. data/platform/mac/pangorc +2 -0
  122. data/platform/mac/pkg-dmg +1447 -0
  123. data/platform/mac/shoes +31 -0
  124. data/platform/mac/shoes-launch +7 -0
  125. data/platform/mac/stub.m +178 -0
  126. data/platform/mac/version.plist +14 -0
  127. data/platform/msw/base.nsi +644 -0
  128. data/platform/msw/installer-1.bmp +0 -0
  129. data/platform/msw/installer-2.bmp +0 -0
  130. data/platform/msw/shoes.exe.manifest +17 -0
  131. data/platform/msw/shoes.ico +0 -0
  132. data/platform/msw/stub-inject.c +59 -0
  133. data/platform/msw/stub.c +271 -0
  134. data/platform/msw/stub32.h +14 -0
  135. data/platform/msw/stub32.rc +16 -0
  136. data/platform/nix/INSTALL +56 -0
  137. data/platform/nix/Makefile +144 -0
  138. data/platform/nix/shoes.launch +20 -0
  139. data/platform/skel.rb +27 -0
  140. data/rakefile_darwin.rb +7 -0
  141. data/rakefile_linux.rb +3 -0
  142. data/rakefile_mingw.rb +7 -0
  143. data/samples/class-book.rb +43 -0
  144. data/samples/class-book.yaml +387 -0
  145. data/samples/expert-definr.rb +23 -0
  146. data/samples/expert-funnies.rb +51 -0
  147. data/samples/expert-irb.rb +112 -0
  148. data/samples/expert-minesweeper.rb +267 -0
  149. data/samples/expert-othello.rb +319 -0
  150. data/samples/expert-pong.rb +62 -0
  151. data/samples/expert-tankspank.rb +385 -0
  152. data/samples/good-arc.rb +37 -0
  153. data/samples/good-clock.rb +51 -0
  154. data/samples/good-follow.rb +26 -0
  155. data/samples/good-reminder.rb +174 -0
  156. data/samples/good-vjot.rb +56 -0
  157. data/samples/simple-accordion.rb +75 -0
  158. data/samples/simple-anim-shapes.rb +17 -0
  159. data/samples/simple-anim-text.rb +13 -0
  160. data/samples/simple-arc.rb +23 -0
  161. data/samples/simple-bounce.rb +24 -0
  162. data/samples/simple-calc.rb +70 -0
  163. data/samples/simple-chipmunk.rb +26 -0
  164. data/samples/simple-control-sizes.rb +24 -0
  165. data/samples/simple-curve.rb +26 -0
  166. data/samples/simple-dialogs.rb +29 -0
  167. data/samples/simple-downloader.rb +27 -0
  168. data/samples/simple-draw.rb +13 -0
  169. data/samples/simple-editor.rb +28 -0
  170. data/samples/simple-form.rb +28 -0
  171. data/samples/simple-form.shy +0 -0
  172. data/samples/simple-mask.rb +21 -0
  173. data/samples/simple-menu.rb +31 -0
  174. data/samples/simple-menu1.rb +35 -0
  175. data/samples/simple-rubygems.rb +29 -0
  176. data/samples/simple-slide.rb +45 -0
  177. data/samples/simple-sphere.rb +28 -0
  178. data/samples/simple-sqlite3.rb +13 -0
  179. data/samples/simple-timer.rb +13 -0
  180. data/samples/simple-video.rb +13 -0
  181. data/shoes.gemspec +21 -0
  182. data/shoes/app.c +591 -0
  183. data/shoes/app.h +110 -0
  184. data/shoes/appwin32.h +13 -0
  185. data/shoes/appwin32.rc +28 -0
  186. data/shoes/canvas.c +2202 -0
  187. data/shoes/canvas.h +682 -0
  188. data/shoes/code.h +14 -0
  189. data/shoes/config.h +232 -0
  190. data/shoes/effects.c +243 -0
  191. data/shoes/effects.h +7 -0
  192. data/shoes/http.h +44 -0
  193. data/shoes/http/common.h +86 -0
  194. data/shoes/http/curl.c +259 -0
  195. data/shoes/http/nsurl.m +274 -0
  196. data/shoes/http/windownload.c +114 -0
  197. data/shoes/http/winhttp.c +216 -0
  198. data/shoes/http/winhttp.h +19 -0
  199. data/shoes/image.c +1020 -0
  200. data/shoes/internal.c +46 -0
  201. data/shoes/internal.h +63 -0
  202. data/shoes/native.h +110 -0
  203. data/shoes/native/cocoa.h +105 -0
  204. data/shoes/native/cocoa.m +1557 -0
  205. data/shoes/native/gtk.c +1257 -0
  206. data/shoes/native/windows.c +2392 -0
  207. data/shoes/ruby.c +5221 -0
  208. data/shoes/ruby.h +299 -0
  209. data/shoes/world.c +243 -0
  210. data/shoes/world.h +63 -0
  211. data/static/Shoes.icns +0 -0
  212. data/static/avatar.png +0 -0
  213. data/static/code_highlighter.js +188 -0
  214. data/static/code_highlighter_ruby.js +26 -0
  215. data/static/icon-debug.png +0 -0
  216. data/static/icon-error.png +0 -0
  217. data/static/icon-info.png +0 -0
  218. data/static/icon-warn.png +0 -0
  219. data/static/listbox_button1.png +0 -0
  220. data/static/listbox_button2.png +0 -0
  221. data/static/man-app.png +0 -0
  222. data/static/man-builds.png +0 -0
  223. data/static/man-builds1.png +0 -0
  224. data/static/man-editor-notepad.png +0 -0
  225. data/static/man-editor-osx.png +0 -0
  226. data/static/man-ele-background.png +0 -0
  227. data/static/man-ele-border.png +0 -0
  228. data/static/man-ele-button.png +0 -0
  229. data/static/man-ele-check.png +0 -0
  230. data/static/man-ele-editbox.png +0 -0
  231. data/static/man-ele-editline.png +0 -0
  232. data/static/man-ele-image.png +0 -0
  233. data/static/man-ele-listbox.png +0 -0
  234. data/static/man-ele-progress.png +0 -0
  235. data/static/man-ele-radio.png +0 -0
  236. data/static/man-ele-shape.png +0 -0
  237. data/static/man-ele-textblock.png +0 -0
  238. data/static/man-ele-video.png +0 -0
  239. data/static/man-intro-dmg.png +0 -0
  240. data/static/man-intro-exe.png +0 -0
  241. data/static/man-look-tiger.png +0 -0
  242. data/static/man-look-ubuntu.png +0 -0
  243. data/static/man-look-vista.png +0 -0
  244. data/static/man-run-osx.png +0 -0
  245. data/static/man-run-vista.png +0 -0
  246. data/static/man-run-xp.png +0 -0
  247. data/static/man-shot1.png +0 -0
  248. data/static/manual-en.txt +3531 -0
  249. data/static/manual-ja.txt +2825 -0
  250. data/static/manual.css +167 -0
  251. data/static/menu-corner1.png +0 -0
  252. data/static/menu-corner2.png +0 -0
  253. data/static/menu-gray.png +0 -0
  254. data/static/menu-left.png +0 -0
  255. data/static/menu-right.png +0 -0
  256. data/static/menu-top.png +0 -0
  257. data/static/shoes-dmg.jpg +0 -0
  258. data/static/shoes-icon-blue.png +0 -0
  259. data/static/shoes-icon.png +0 -0
  260. data/static/shoes-manual-apps.gif +0 -0
  261. data/static/shoes_main_window.png +0 -0
  262. data/static/stripe.png +0 -0
  263. data/static/stubs/blank.exe +0 -0
  264. data/static/stubs/blank.hfz +0 -0
  265. data/static/stubs/blank.run +375 -0
  266. data/static/stubs/cocoa-install +0 -0
  267. data/static/stubs/sh-install +49 -0
  268. data/static/stubs/shoes-stub-inject.exe +0 -0
  269. data/static/stubs/shoes-stub.exe +0 -0
  270. data/static/tutor-back.png +0 -0
  271. data/test/shoes_test.rb +8 -0
  272. data/test/test_helper.rb +25 -0
  273. data/use-deps +12 -0
  274. data/use-tmp-dep +8 -0
  275. metadata +509 -0
@@ -0,0 +1,114 @@
1
+ //
2
+ // shoes/http/windownload.c
3
+ // the downloader routines using windows' winhttp lib.
4
+ //
5
+ #include "shoes/app.h"
6
+ #include "shoes/ruby.h"
7
+ #include "shoes/internal.h"
8
+ #include "shoes/config.h"
9
+ #include "shoes/http.h"
10
+ #include "shoes/version.h"
11
+ #include "shoes/world.h"
12
+ #include "shoes/native.h"
13
+ #include <shellapi.h>
14
+ #include <wchar.h>
15
+ #include <time.h>
16
+
17
+ void
18
+ shoes_download(shoes_http_request *req)
19
+ {
20
+ HANDLE file = INVALID_HANDLE_VALUE;
21
+ INTERNET_PORT _port = req->port;
22
+ LPWSTR _method = NULL, _body = NULL;
23
+ LPWSTR _scheme = SHOE_ALLOC_N(WCHAR, MAX_PATH);
24
+ LPWSTR _host = SHOE_ALLOC_N(WCHAR, MAX_PATH);
25
+ LPWSTR _path = SHOE_ALLOC_N(WCHAR, MAX_PATH);
26
+ DWORD _size;
27
+ MultiByteToWideChar(CP_UTF8, 0, req->scheme, -1, _scheme, MAX_PATH);
28
+ MultiByteToWideChar(CP_UTF8, 0, req->host, -1, _host, MAX_PATH);
29
+ MultiByteToWideChar(CP_UTF8, 0, req->path, -1, _path, MAX_PATH);
30
+ if (req->method != NULL)
31
+ {
32
+ _method = SHOE_ALLOC_N(WCHAR, MAX_PATH);
33
+ MultiByteToWideChar(CP_UTF8, 0, req->method, -1, _method, MAX_PATH);
34
+ }
35
+
36
+ if (req->mem == NULL && req->filepath != NULL)
37
+ file = CreateFile(req->filepath, GENERIC_READ | GENERIC_WRITE,
38
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
39
+ shoes_winhttp(_scheme, _host, _port, _path, _method, req->headers, (LPVOID)req->body, req->bodylen,
40
+ &req->mem, req->memlen, file, &_size, req->flags, req->handler, req->data);
41
+ req->size = _size;
42
+ SHOE_FREE(_scheme);
43
+ SHOE_FREE(_host);
44
+ SHOE_FREE(_path);
45
+ if (_method != NULL) SHOE_FREE(_method);
46
+ }
47
+
48
+ DWORD WINAPI
49
+ shoes_download2(LPVOID data)
50
+ {
51
+ shoes_http_request *req = (shoes_http_request *)data;
52
+ shoes_download(req);
53
+ shoes_http_request_free(req);
54
+ free(req);
55
+ return TRUE;
56
+ }
57
+
58
+ void
59
+ shoes_queue_download(shoes_http_request *req)
60
+ {
61
+ DWORD tid;
62
+ CreateThread(0, 0, (LPTHREAD_START_ROUTINE)shoes_download2, (void *)req, 0, &tid);
63
+ }
64
+
65
+ VALUE
66
+ shoes_http_err(SHOES_DOWNLOAD_ERROR code)
67
+ {
68
+ TCHAR msg[1024];
69
+ DWORD msglen;
70
+ if (code > 12000 && code <= 12174)
71
+ {
72
+ msglen = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
73
+ GetModuleHandle("WINHTTP.DLL"), code, 0, msg, sizeof(msg), NULL);
74
+ }
75
+ else
76
+ {
77
+ msglen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
78
+ NULL, code, 0, msg, sizeof(msg), NULL);
79
+ }
80
+ msg[msglen] = '\0';
81
+ return rb_str_new2(msg);
82
+ }
83
+
84
+ SHOES_DOWNLOAD_HEADERS
85
+ shoes_http_headers(VALUE hsh)
86
+ {
87
+ long i;
88
+ LPWSTR hdrs = NULL;
89
+ VALUE keys = rb_funcall(hsh, s_keys, 0);
90
+ if (RARRAY_LEN(keys) > 0)
91
+ {
92
+ VALUE headers = rb_str_new2("");
93
+ //for (i = 0; i < RARRAY(keys)->as.heap.len; i++ )
94
+ for (i = 0; i < RARRAY_LEN(keys); i++ )
95
+ {
96
+ VALUE key = rb_ary_entry(keys, i);
97
+ rb_str_append(headers, key);
98
+ rb_str_cat2(headers, ": ");
99
+ rb_str_append(headers, rb_hash_aref(hsh, key));
100
+ rb_str_cat2(headers, "\n");
101
+ }
102
+
103
+ hdrs = SHOE_ALLOC_N(WCHAR, RSTRING_LEN(headers) + 1);
104
+ SHOE_MEMZERO(hdrs, WCHAR, RSTRING_LEN(headers) + 1);
105
+ MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(headers), -1, hdrs, RSTRING_LEN(headers) + 1);
106
+ }
107
+ return hdrs;
108
+ }
109
+
110
+ void
111
+ shoes_http_headers_free(SHOES_DOWNLOAD_HEADERS headers)
112
+ {
113
+ SHOE_FREE(headers);
114
+ }
@@ -0,0 +1,216 @@
1
+ //
2
+ // shoes/http/winhttp.c
3
+ // the central download routine
4
+ // (isolated so it can be reused in platform/msw/stub.c)
5
+ //
6
+ #include "shoes/app.h"
7
+ #include "shoes/ruby.h"
8
+ #include "shoes/internal.h"
9
+ #include "shoes/config.h"
10
+ #include "shoes/version.h"
11
+ #include "shoes/http/common.h"
12
+ #include "shoes/http/winhttp.h"
13
+
14
+ void shoes_get_time(SHOES_TIME *ts)
15
+ {
16
+ *ts = GetTickCount();
17
+ }
18
+
19
+ unsigned long shoes_diff_time(SHOES_TIME *start, SHOES_TIME *end)
20
+ {
21
+ return *end - *start;
22
+ }
23
+
24
+ void
25
+ shoes_winhttp_headers(HINTERNET req, shoes_http_handler handler, void *data)
26
+ {
27
+ DWORD size;
28
+ WinHttpQueryHeaders(req, WINHTTP_QUERY_RAW_HEADERS,
29
+ WINHTTP_HEADER_NAME_BY_INDEX, NULL, &size, WINHTTP_NO_HEADER_INDEX);
30
+
31
+ if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
32
+ {
33
+ int whdrlen = 0, hdrlen = 0;
34
+ LPCWSTR whdr;
35
+ LPSTR hdr = SHOE_ALLOC_N(CHAR, MAX_PATH);
36
+ LPCWSTR hdrs = SHOE_ALLOC_N(WCHAR, size/sizeof(WCHAR));
37
+ BOOL res = WinHttpQueryHeaders(req, WINHTTP_QUERY_RAW_HEADERS,
38
+ WINHTTP_HEADER_NAME_BY_INDEX, (LPVOID)hdrs, &size, WINHTTP_NO_HEADER_INDEX);
39
+ if (res)
40
+ {
41
+ for (whdr = hdrs; whdr - hdrs < size / sizeof(WCHAR); whdr += whdrlen)
42
+ {
43
+ WideCharToMultiByte(CP_UTF8, 0, whdr, -1, hdr, MAX_PATH, NULL, NULL);
44
+ hdrlen = strlen(hdr);
45
+ HTTP_HEADER(hdr, hdrlen, handler, data);
46
+ whdrlen = wcslen(whdr) + 1;
47
+ }
48
+ }
49
+ SHOE_FREE(hdrs);
50
+ SHOE_FREE(hdr);
51
+ }
52
+ }
53
+
54
+ void
55
+ shoes_winhttp(LPCWSTR scheme, LPCWSTR host, INTERNET_PORT port, LPCWSTR path, LPCWSTR method,
56
+ LPCWSTR headers, LPVOID body, DWORD bodylen, TCHAR **mem, ULONG memlen, HANDLE file,
57
+ LPDWORD size, UCHAR flags, shoes_http_handler handler, void *data)
58
+ {
59
+ LPWSTR proxy;
60
+ DWORD len = 0, rlen = 0, status = 0, complete = 0, flen = 0, total = 0, written = 0;
61
+ LPTSTR buf = SHOE_ALLOC_N(TCHAR, SHOES_BUFSIZE);
62
+ LPTSTR fbuf = SHOE_ALLOC_N(TCHAR, SHOES_CHUNKSIZE);
63
+ LPWSTR uagent = SHOE_ALLOC_N(WCHAR, SHOES_BUFSIZE);
64
+ HINTERNET sess = NULL, conn = NULL, req = NULL;
65
+ SHOES_TIME last = 0;
66
+
67
+ if (buf == NULL || fbuf == NULL || uagent == NULL)
68
+ goto done;
69
+
70
+ _snwprintf(uagent, SHOES_BUFSIZE, L"Shoes/0.r%d (%S) %S/%d", SHOES_REVISION, SHOES_PLATFORM,
71
+ SHOES_RELEASE_NAME, SHOES_BUILD_DATE);
72
+ sess = WinHttpOpen(uagent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
73
+ WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
74
+ if (sess == NULL)
75
+ goto done;
76
+
77
+ conn = WinHttpConnect(sess, host, port, 0);
78
+ if (conn == NULL)
79
+ goto done;
80
+
81
+ if (method == NULL) method = L"GET";
82
+ req = WinHttpOpenRequest(conn, method, path,
83
+ NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
84
+ if (req == NULL)
85
+ goto done;
86
+
87
+ proxy = _wgetenv(L"http_proxy");
88
+ if (proxy != NULL)
89
+ {
90
+ WINHTTP_PROXY_INFO proxy_info;
91
+ proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
92
+ proxy_info.lpszProxy = proxy;
93
+ proxy_info.lpszProxyBypass = NULL;
94
+ WinHttpSetOption(req, WINHTTP_OPTION_PROXY, &proxy_info, sizeof(proxy_info));
95
+ }
96
+
97
+ if (!(flags & SHOES_DL_REDIRECTS))
98
+ {
99
+ DWORD options = WINHTTP_DISABLE_REDIRECTS;
100
+ WinHttpSetOption(req, WINHTTP_OPTION_DISABLE_FEATURE, &options, sizeof(options));
101
+ }
102
+
103
+ if (headers != NULL)
104
+ WinHttpAddRequestHeaders(req, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE);
105
+
106
+ if (!WinHttpSendRequest(req, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
107
+ (LPVOID)body, bodylen, bodylen, 0))
108
+ goto done;
109
+
110
+ if (!WinHttpReceiveResponse(req, NULL))
111
+ goto done;
112
+
113
+ len = sizeof(DWORD);
114
+ if (!WinHttpQueryHeaders(req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
115
+ NULL, &status, &len, NULL))
116
+ goto done;
117
+ else if (handler != NULL)
118
+ {
119
+ shoes_http_event *event = SHOE_ALLOC(shoes_http_event);
120
+ SHOE_MEMZERO(event, shoes_http_event, 1);
121
+ event->stage = SHOES_HTTP_STATUS;
122
+ event->status = status;
123
+ handler(event, data);
124
+ SHOE_FREE(event);
125
+ }
126
+
127
+ if (handler != NULL) shoes_winhttp_headers(req, handler, data);
128
+
129
+ *size = 0;
130
+ len = sizeof(buf);
131
+ if (WinHttpQueryHeaders(req, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER,
132
+ NULL, size, &len, NULL))
133
+ {
134
+ if (*mem != NULL && *size > memlen)
135
+ {
136
+ SHOE_REALLOC_N(*mem, char, (memlen = *size));
137
+ if (*mem == NULL) goto done;
138
+ }
139
+ }
140
+
141
+ HTTP_EVENT(handler, SHOES_HTTP_CONNECTED, last, 0, 0, *size, data, NULL, goto done);
142
+
143
+ total = *size * 100;
144
+ rlen = *size;
145
+ while (1)
146
+ {
147
+ len = 0;
148
+ WinHttpReadData(req, fbuf, SHOES_CHUNKSIZE, &len);
149
+ if (len <= 0)
150
+ break;
151
+
152
+ if (*mem != NULL)
153
+ {
154
+ if (written + len > memlen)
155
+ {
156
+ while (written + len > memlen)
157
+ memlen += SHOES_BUFSIZE;
158
+ SHOE_REALLOC_N(*mem, char, memlen);
159
+ if (*mem == NULL) goto done;
160
+ }
161
+ SHOE_MEMCPY(*mem + written, fbuf, char, len);
162
+ }
163
+ if (file != INVALID_HANDLE_VALUE)
164
+ WriteFile(file, (LPBYTE)fbuf, len, &flen, NULL);
165
+ written += len;
166
+
167
+ if (*size == 0) total = written * 100;
168
+ if (total > 0)
169
+ {
170
+ HTTP_EVENT(handler, SHOES_HTTP_TRANSFER, last, (int)((total - (rlen * 100)) / (total / 100)),
171
+ (total / 100) - rlen, (total / 100), data, NULL, break);
172
+ }
173
+
174
+ if (rlen > len) rlen -= len;
175
+ }
176
+
177
+ *size = written;
178
+
179
+ if (file != INVALID_HANDLE_VALUE)
180
+ {
181
+ CloseHandle(file);
182
+ file = INVALID_HANDLE_VALUE;
183
+ }
184
+
185
+ HTTP_EVENT(handler, SHOES_HTTP_COMPLETED, last, 100, *size, *size, data, *mem, goto done);
186
+ complete = 1;
187
+
188
+ done:
189
+ if (buf != NULL) SHOE_FREE(buf);
190
+ if (fbuf != NULL) SHOE_FREE(fbuf);
191
+ if (uagent != NULL) SHOE_FREE(uagent);
192
+
193
+ if (!complete)
194
+ {
195
+ shoes_http_event *event = SHOE_ALLOC(shoes_http_event);
196
+ SHOE_MEMZERO(event, shoes_http_event, 1);
197
+ event->stage = SHOES_HTTP_ERROR;
198
+ event->error = GetLastError();
199
+ int hx = handler(event, data);
200
+ SHOE_FREE(event);
201
+ if (hx & SHOES_DOWNLOAD_HALT) goto done;
202
+ }
203
+
204
+ if (file != INVALID_HANDLE_VALUE)
205
+ CloseHandle(file);
206
+
207
+ if (req)
208
+ WinHttpCloseHandle(req);
209
+
210
+ if (conn)
211
+ WinHttpCloseHandle(conn);
212
+
213
+ if (sess)
214
+ WinHttpCloseHandle(sess);
215
+ }
216
+
@@ -0,0 +1,19 @@
1
+ //
2
+ // shoes/http/winhttp.h
3
+ // the shoes_winhttp function, used by platform/msw/stub.c.
4
+ //
5
+ #ifndef SHOES_HTTP_WINHTTP_H
6
+ #define SHOES_HTTP_WINHTTP_H
7
+
8
+ #include <stdio.h>
9
+ #include <windows.h>
10
+ #include <wchar.h>
11
+ #include <winhttp.h>
12
+ #include <shellapi.h>
13
+ #include "shoes/http/common.h"
14
+
15
+ #define HTTP_HANDLER(x) reinterpret_cast<shoes_http_handler>(x)
16
+
17
+ void shoes_winhttp(LPCWSTR, LPCWSTR, INTERNET_PORT, LPCWSTR, LPCWSTR, LPCWSTR, LPVOID, DWORD, TCHAR **, ULONG, HANDLE, LPDWORD, UCHAR, shoes_http_handler, void *);
18
+
19
+ #endif
@@ -0,0 +1,1020 @@
1
+ //
2
+ // shoes/image.c
3
+ // Loading image formats in Cairo. I've already tried gdk-pixbuf and imlib2, but
4
+ // the idea here is to cut down dependencies, since I only really need reading of
5
+ // the basics: GIF and JPEG.
6
+ //
7
+ #include <stdio.h>
8
+ #include <setjmp.h>
9
+ #include <sys/stat.h>
10
+ #include "shoes/app.h"
11
+ #include "shoes/canvas.h"
12
+ #include "shoes/ruby.h"
13
+ #include "shoes/internal.h"
14
+ #include "shoes/world.h"
15
+ #include "shoes/native.h"
16
+ #include "shoes/version.h"
17
+ #include "shoes/http.h"
18
+
19
+ #undef HAVE_PROTOTYPES
20
+ #undef HAVE_STDLIB_H
21
+ #undef EXTERN
22
+ #include <jpeglib.h>
23
+ #include <jerror.h>
24
+
25
+ #ifndef SHOES_GTK
26
+ #ifdef DrawText
27
+ #undef DrawText
28
+ #endif
29
+ #endif
30
+ #define DrawText gif_DrawText
31
+ #include <gif_lib.h>
32
+
33
+ shoes_image_format shoes_image_detect(VALUE, int *, int *);
34
+
35
+ #define JPEG_LINES 16
36
+ #define SIZE_SURFACE ((cairo_surface_t *)1)
37
+
38
+ typedef struct {
39
+ unsigned int state[5];
40
+ unsigned int count[2];
41
+ unsigned char buffer[64];
42
+ } SHA1_CTX;
43
+
44
+ void SHA1Transform(unsigned int state[5], unsigned char buffer[64]);
45
+ void SHA1Init(SHA1_CTX* context);
46
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len);
47
+ void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
48
+
49
+ #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
50
+
51
+ /* blk0() and blk() perform the initial expand. */
52
+ /* I got the idea of expanding during the round function from SSLeay */
53
+ #ifdef LITTLE_ENDIAN
54
+ #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
55
+ |(rol(block->l[i],8)&0x00FF00FF))
56
+ #else
57
+ #define blk0(i) block->l[i]
58
+ #endif
59
+ #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
60
+ ^block->l[(i+2)&15]^block->l[i&15],1))
61
+
62
+ /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
63
+ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
64
+ #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
65
+ #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
66
+ #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
67
+ #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
68
+
69
+ /* Hash a single 512-bit block. This is the core of the algorithm. */
70
+ void SHA1Transform(unsigned int state[5], unsigned char buffer[64])
71
+ {
72
+ unsigned int a, b, c, d, e;
73
+ typedef union {
74
+ unsigned char c[64];
75
+ unsigned int l[16];
76
+ } CHAR64LONG16;
77
+ CHAR64LONG16* block;
78
+ #ifdef SHA1HANDSOFF
79
+ static unsigned char workspace[64];
80
+ block = (CHAR64LONG16*)workspace;
81
+ memcpy(block, buffer, 64);
82
+ #else
83
+ block = (CHAR64LONG16*)buffer;
84
+ #endif
85
+ /* Copy context->state[] to working vars */
86
+ a = state[0];
87
+ b = state[1];
88
+ c = state[2];
89
+ d = state[3];
90
+ e = state[4];
91
+ /* 4 rounds of 20 operations each. Loop unrolled. */
92
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
93
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
94
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
95
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
96
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
97
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
98
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
99
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
100
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
101
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
102
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
103
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
104
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
105
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
106
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
107
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
108
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
109
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
110
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
111
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
112
+ /* Add the working vars back into context.state[] */
113
+ state[0] += a;
114
+ state[1] += b;
115
+ state[2] += c;
116
+ state[3] += d;
117
+ state[4] += e;
118
+ /* Wipe variables */
119
+ a = b = c = d = e = 0;
120
+ }
121
+
122
+ /* SHA1Init - Initialize new context */
123
+ void SHA1Init(SHA1_CTX* context)
124
+ {
125
+ /* SHA1 initialization constants */
126
+ context->state[0] = 0x67452301;
127
+ context->state[1] = 0xEFCDAB89;
128
+ context->state[2] = 0x98BADCFE;
129
+ context->state[3] = 0x10325476;
130
+ context->state[4] = 0xC3D2E1F0;
131
+ context->count[0] = context->count[1] = 0;
132
+ }
133
+
134
+ /* Run your data through this. */
135
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len)
136
+ {
137
+ unsigned int i, j;
138
+
139
+ j = (context->count[0] >> 3) & 63;
140
+ if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
141
+ context->count[1] += (len >> 29);
142
+ if ((j + len) > 63) {
143
+ memcpy(&context->buffer[j], data, (i = 64-j));
144
+ SHA1Transform(context->state, context->buffer);
145
+ for ( ; i + 63 < len; i += 64) {
146
+ SHA1Transform(context->state, &data[i]);
147
+ }
148
+ j = 0;
149
+ }
150
+ else i = 0;
151
+ memcpy(&context->buffer[j], &data[i], len - i);
152
+ }
153
+
154
+ /* Add padding and return the message digest. */
155
+ void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
156
+ {
157
+ unsigned int i, j;
158
+ unsigned char finalcount[8];
159
+
160
+ for (i = 0; i < 8; i++) {
161
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
162
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
163
+ }
164
+ SHA1Update(context, (unsigned char *)"\200", 1);
165
+ while ((context->count[0] & 504) != 448) {
166
+ SHA1Update(context, (unsigned char *)"\0", 1);
167
+ }
168
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
169
+ for (i = 0; i < 20; i++) {
170
+ digest[i] = (unsigned char)
171
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
172
+ }
173
+ /* Wipe variables */
174
+ i = j = 0;
175
+ memset(context->buffer, 0, 64);
176
+ memset(context->state, 0, 20);
177
+ memset(context->count, 0, 8);
178
+ memset(&finalcount, 0, 8);
179
+ #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */
180
+ SHA1Transform(context->state, context->buffer);
181
+ #endif
182
+ }
183
+
184
+ cairo_surface_t *
185
+ shoes_surface_create_from_pixels(PIXEL *pixels, int width, int height)
186
+ {
187
+ guchar *cairo_pixels;
188
+ cairo_surface_t *surface;
189
+ cairo_user_data_key_t key;
190
+ int j;
191
+
192
+ cairo_pixels = (guchar *)g_malloc(4 * width * height);
193
+ surface = cairo_image_surface_create_for_data((unsigned char *)cairo_pixels,
194
+ CAIRO_FORMAT_ARGB32,
195
+ width, height, 4 * width);
196
+ cairo_surface_set_user_data(surface, &key,
197
+ cairo_pixels, (cairo_destroy_func_t)g_free);
198
+
199
+ for (j = height; j; j--)
200
+ {
201
+ guchar *p = (guchar *)pixels;
202
+ guchar *q = cairo_pixels;
203
+
204
+ guchar *end = p + 4 * width;
205
+ guint t1,t2,t3;
206
+
207
+ #define MULT(d,c,a,t) G_STMT_START { t = c * a; d = ((t >> 8) + t) >> 8; } G_STMT_END
208
+
209
+ while (p < end)
210
+ {
211
+ #if G_BYTE_ORDER == G_LITTLE_ENDIAN
212
+ MULT(q[2], p[2], p[3], t1);
213
+ MULT(q[1], p[1], p[3], t2);
214
+ MULT(q[0], p[0], p[3], t3);
215
+ q[3] = p[3];
216
+ #else
217
+ q[0] = p[3];
218
+ MULT(q[3], p[0], p[3], t1);
219
+ MULT(q[2], p[1], p[3], t2);
220
+ MULT(q[1], p[2], p[3], t3);
221
+ #endif
222
+
223
+ p += 4;
224
+ q += 4;
225
+ }
226
+
227
+ #undef MULT
228
+
229
+ pixels += width;
230
+ cairo_pixels += 4 * width;
231
+ }
232
+
233
+ return surface;
234
+ }
235
+
236
+ #define PNG_SIG "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"
237
+
238
+ cairo_surface_t *
239
+ shoes_png_size(char *filename, int *width, int *height)
240
+ {
241
+ unsigned char *sig = SHOE_ALLOC_N(unsigned char, 32);
242
+ cairo_surface_t *surface = NULL;
243
+
244
+ #ifdef SHOES_WIN32
245
+ HANDLE hFile;
246
+ hFile = CreateFile( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
247
+ if ( hFile == INVALID_HANDLE_VALUE ) return NULL;
248
+ DWORD readsize;
249
+ ReadFile( hFile, sig, 32, &readsize, NULL );
250
+ if (readsize < 8)
251
+ goto done;
252
+ #else
253
+ FILE *image = fopen(filename, "rb");
254
+ if (image == NULL) return NULL;
255
+
256
+ if (fread(sig, 1, 32, image) < 8)
257
+ goto done;
258
+ #endif
259
+
260
+ if (memcmp(sig, PNG_SIG, 8) != 0)
261
+ goto done;
262
+
263
+ *width = *((int *)(sig + 0x10));
264
+ BE_CPU(*width);
265
+ *height = *((int *)(sig + 0x14));
266
+ BE_CPU(*height);
267
+
268
+ surface = SIZE_SURFACE;
269
+ done:
270
+ #ifdef SHOES_WIN32
271
+ CloseHandle( hFile );
272
+ #else
273
+ fclose(image);
274
+ #endif
275
+ return surface;
276
+ }
277
+
278
+ cairo_surface_t *
279
+ shoes_surface_create_from_gif(char *filename, int *width, int *height, unsigned char load)
280
+ {
281
+ cairo_surface_t *surface = NULL;
282
+ GifFileType *gif;
283
+ PIXEL *ptr = NULL, *pixels = NULL;
284
+ GifPixelType **rows = NULL;
285
+ GifRecordType rec;
286
+ ColorMapObject *cmap;
287
+ int i, j, bg, r, g, b, w = 0, h = 0, done = 0, transp = -1;
288
+ float per = 0.0, per_inc;
289
+ int intoffset[] = { 0, 4, 2, 1 };
290
+ int intjump[] = { 8, 8, 4, 2 };
291
+
292
+ transp = -1;
293
+ gif = DGifOpenFileName(filename);
294
+ if (gif == NULL)
295
+ goto done;
296
+
297
+ do
298
+ {
299
+ if (DGifGetRecordType(gif, &rec) == GIF_ERROR)
300
+ rec = TERMINATE_RECORD_TYPE;
301
+ if ((rec == IMAGE_DESC_RECORD_TYPE) && (!done))
302
+ {
303
+ if (DGifGetImageDesc(gif) == GIF_ERROR)
304
+ {
305
+ /* PrintGifError(); */
306
+ rec = TERMINATE_RECORD_TYPE;
307
+ }
308
+ w = gif->Image.Width;
309
+ if (width != NULL) *width = w;
310
+ h = gif->Image.Height;
311
+ if (height != NULL) *height = h;
312
+ if ((w < 1) || (h < 1) || (w > 8192) || (h > 8192))
313
+ goto done;
314
+
315
+ if (!load)
316
+ {
317
+ surface = SIZE_SURFACE;
318
+ goto done;
319
+ }
320
+
321
+ rows = SHOE_ALLOC_N(GifPixelType *, h);
322
+ if (rows == NULL)
323
+ goto done;
324
+
325
+ SHOE_MEMZERO(rows, GifPixelType *, h);
326
+
327
+ for (i = 0; i < h; i++)
328
+ {
329
+ rows[i] = SHOE_ALLOC_N(GifPixelType, w);
330
+ if (rows[i] == NULL)
331
+ goto done;
332
+ }
333
+
334
+ if (gif->Image.Interlace)
335
+ {
336
+ for (i = 0; i < 4; i++)
337
+ {
338
+ for (j = intoffset[i]; j < h; j += intjump[i])
339
+ DGifGetLine(gif, rows[j], w);
340
+ }
341
+ }
342
+ else
343
+ {
344
+ for (i = 0; i < h; i++)
345
+ DGifGetLine(gif, rows[i], w);
346
+ }
347
+ done = 1;
348
+ }
349
+ else if (rec == EXTENSION_RECORD_TYPE)
350
+ {
351
+ int ext_code;
352
+ GifByteType *ext = NULL;
353
+ DGifGetExtension(gif, &ext_code, &ext);
354
+ while (ext)
355
+ {
356
+ if ((ext_code == 0xf9) && (ext[1] & 1) && (transp < 0))
357
+ transp = (int)ext[4];
358
+ ext = NULL;
359
+ DGifGetExtensionNext(gif, &ext);
360
+ }
361
+ }
362
+ } while (rec != TERMINATE_RECORD_TYPE);
363
+
364
+ bg = gif->SBackGroundColor;
365
+ cmap = (gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap);
366
+ pixels = SHOE_ALLOC_N(PIXEL, w * h);
367
+ if (pixels == NULL)
368
+ goto done;
369
+
370
+ ptr = pixels;
371
+ per_inc = 100.0 / (((float)w) * h);
372
+ for (i = 0; i < h; i++)
373
+ {
374
+ for (j = 0; j < w; j++)
375
+ {
376
+ if (rows[i][j] == transp)
377
+ {
378
+ r = cmap->Colors[bg].Red;
379
+ g = cmap->Colors[bg].Green;
380
+ b = cmap->Colors[bg].Blue;
381
+ *ptr = 0x00ffffff & ((r << 16) | (g << 8) | b);
382
+ LE_CPU(*ptr);
383
+ ptr++;
384
+ }
385
+ else
386
+ {
387
+ r = cmap->Colors[rows[i][j]].Red;
388
+ g = cmap->Colors[rows[i][j]].Green;
389
+ b = cmap->Colors[rows[i][j]].Blue;
390
+ *ptr = (0xff << 24) | (r << 16) | (g << 8) | b;
391
+ LE_CPU(*ptr);
392
+ ptr++;
393
+ }
394
+ per += per_inc;
395
+ }
396
+ }
397
+
398
+ if ((w < 1) || (h < 1) || (w > 8192) || (h > 8192))
399
+ goto done;
400
+
401
+ surface = shoes_surface_create_from_pixels(pixels, w, h);
402
+
403
+ done:
404
+ if (gif != NULL) DGifCloseFile(gif);
405
+ if (pixels != NULL) SHOE_FREE(pixels);
406
+ if (rows != NULL) {
407
+ for (i = 0; i < h; i++)
408
+ if (rows[i] != NULL)
409
+ SHOE_FREE(rows[i]);
410
+ SHOE_FREE(rows);
411
+ }
412
+ return surface;
413
+ }
414
+
415
+ //
416
+ // JPEG handling code
417
+ //
418
+ struct shoes_jpeg_file_src {
419
+ struct jpeg_source_mgr pub; /* public fields */
420
+
421
+ #ifdef SHOES_WIN32
422
+ HANDLE infile; /* source stream */
423
+ #else
424
+ FILE *infile; /* source stream */
425
+ #endif
426
+
427
+ JOCTET *buffer; /* start of buffer */
428
+ boolean start_of_file; /* have we gotten any data yet? */
429
+ };
430
+
431
+ struct shoes_jpeg_error_mgr {
432
+ struct jpeg_error_mgr pub; /* public fields */
433
+ jmp_buf setjmp_buffer; /* for return to caller */
434
+ };
435
+
436
+ typedef struct shoes_jpeg_file_src *shoes_jpeg_file;
437
+ typedef struct shoes_jpeg_error_mgr *shoes_jpeg_err;
438
+
439
+ #define JPEG_INPUT_BUF_SIZE 4096
440
+
441
+ void
442
+ shoes_jpeg_term_source(j_decompress_ptr cinfo)
443
+ {
444
+ }
445
+
446
+ void
447
+ shoes_jpeg_init_source(j_decompress_ptr cinfo)
448
+ {
449
+ shoes_jpeg_file src = (shoes_jpeg_file) cinfo->src;
450
+ src->start_of_file = 1;
451
+ }
452
+
453
+
454
+ boolean
455
+ shoes_jpeg_fill_input_buffer(j_decompress_ptr cinfo)
456
+ {
457
+ shoes_jpeg_file src = (shoes_jpeg_file)cinfo->src;
458
+ size_t nbytes;
459
+
460
+ #ifdef SHOES_WIN32
461
+ DWORD readsize;
462
+ ReadFile( src->infile, src->buffer, JPEG_INPUT_BUF_SIZE, &readsize, NULL );
463
+ nbytes = readsize;
464
+ #else
465
+ nbytes = fread(src->buffer, 1, JPEG_INPUT_BUF_SIZE, src->infile);
466
+ #endif
467
+
468
+ if (nbytes <= 0) {
469
+ if (src->start_of_file) /* Treat empty input file as fatal error */
470
+ ERREXIT(cinfo, JERR_INPUT_EMPTY);
471
+ WARNMS(cinfo, JWRN_JPEG_EOF);
472
+ src->buffer[0] = (JOCTET) 0xFF;
473
+ src->buffer[1] = (JOCTET) JPEG_EOI;
474
+ nbytes = 2;
475
+ }
476
+
477
+ src->pub.next_input_byte = src->buffer;
478
+ src->pub.bytes_in_buffer = nbytes;
479
+ src->start_of_file = 0;
480
+
481
+ return 1;
482
+ }
483
+
484
+ void
485
+ shoes_jpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
486
+ {
487
+ shoes_jpeg_file src = (shoes_jpeg_file)cinfo->src;
488
+
489
+ if (num_bytes > 0)
490
+ {
491
+ while (num_bytes > (long)src->pub.bytes_in_buffer)
492
+ {
493
+ num_bytes -= (long)src->pub.bytes_in_buffer;
494
+ (void)shoes_jpeg_fill_input_buffer(cinfo);
495
+ }
496
+ src->pub.next_input_byte += (size_t)num_bytes;
497
+ src->pub.bytes_in_buffer -= (size_t)num_bytes;
498
+ }
499
+ }
500
+
501
+ void
502
+ #ifdef SHOES_WIN32
503
+ jpeg_file_src(j_decompress_ptr cinfo, HANDLE infile)
504
+ #else
505
+ jpeg_file_src(j_decompress_ptr cinfo, FILE *infile)
506
+ #endif
507
+ {
508
+ shoes_jpeg_file src;
509
+
510
+ if (cinfo->src == NULL)
511
+ {
512
+ /* first time for this JPEG object? */
513
+ cinfo->src = (struct jpeg_source_mgr *)
514
+ (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT,
515
+ sizeof(struct shoes_jpeg_file_src));
516
+ src = (shoes_jpeg_file) cinfo->src;
517
+ src->buffer = (JOCTET *)
518
+ (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT,
519
+ JPEG_INPUT_BUF_SIZE * sizeof(JOCTET));
520
+ }
521
+
522
+ src = (shoes_jpeg_file)cinfo->src;
523
+ src->pub.init_source = shoes_jpeg_init_source;
524
+ src->pub.fill_input_buffer = shoes_jpeg_fill_input_buffer;
525
+ src->pub.skip_input_data = shoes_jpeg_skip_input_data;
526
+ src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
527
+ src->pub.term_source = shoes_jpeg_term_source;
528
+ src->infile = infile;
529
+ src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
530
+ src->pub.next_input_byte = NULL; /* until buffer loaded */
531
+ }
532
+
533
+ void
534
+ shoes_jpeg_fatal(j_common_ptr cinfo)
535
+ {
536
+ shoes_jpeg_err jpgerr = (shoes_jpeg_err)cinfo->err;
537
+ longjmp(jpgerr->setjmp_buffer, 1);
538
+ }
539
+
540
+ cairo_surface_t *
541
+ shoes_surface_create_from_jpeg(char *filename, int *width, int *height, unsigned char load)
542
+ {
543
+ int x, y, w, h, l, i, scans, count, prevy;
544
+ unsigned char *ptr, *rgb = NULL, **line = NULL;
545
+ PIXEL *pixels = NULL, *ptr2;
546
+ cairo_surface_t *surface = NULL;
547
+ struct jpeg_decompress_struct cinfo;
548
+ struct shoes_jpeg_error_mgr jerr;
549
+
550
+ #ifdef SHOES_WIN32
551
+ HANDLE hFile;
552
+ hFile = CreateFile( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
553
+ if ( hFile == INVALID_HANDLE_VALUE ) return NULL;
554
+ #else
555
+ FILE *f = fopen(filename, "rb");
556
+ if (!f) return NULL;
557
+ #endif
558
+
559
+ // TODO: error handling
560
+ cinfo.err = jpeg_std_error(&jerr.pub);
561
+ jerr.pub.error_exit = shoes_jpeg_fatal;
562
+ // jerr.pub.emit_message = shoes_jpeg_error;
563
+ // jerr.pub.output_message = shoes_jpeg_error2;
564
+ if (setjmp(jerr.setjmp_buffer)) {
565
+ // append_jpeg_message(interp, (j_common_ptr) &cinfo);
566
+ jpeg_destroy_decompress(&cinfo);
567
+ return NULL;
568
+ }
569
+
570
+ jpeg_create_decompress(&cinfo);
571
+ #ifdef SHOES_WIN32
572
+ jpeg_file_src(&cinfo, hFile);
573
+ #else
574
+ jpeg_file_src(&cinfo, f);
575
+ #endif
576
+ jpeg_read_header(&cinfo, TRUE);
577
+ cinfo.do_fancy_upsampling = FALSE;
578
+ cinfo.do_block_smoothing = FALSE;
579
+
580
+ line = SHOE_ALLOC_N(unsigned char *, JPEG_LINES);
581
+ jpeg_start_decompress(&cinfo);
582
+ w = cinfo.output_width;
583
+ if (width != NULL) *width = w;
584
+ h = cinfo.output_height;
585
+ if (height != NULL) *height = h;
586
+
587
+ if ((w < 1) || (h < 1) || (w > 8192) || (h > 8192))
588
+ goto done;
589
+
590
+ if (!load)
591
+ {
592
+ surface = SIZE_SURFACE;
593
+ goto done;
594
+ }
595
+
596
+ if (cinfo.rec_outbuf_height > JPEG_LINES)
597
+ goto done;
598
+
599
+ rgb = SHOE_ALLOC_N(unsigned char, w * JPEG_LINES * 3);
600
+ ptr2 = pixels = SHOE_ALLOC_N(PIXEL, w * h);
601
+ if (rgb == NULL || pixels == NULL)
602
+ goto done;
603
+
604
+ count = 0;
605
+ prevy = 0;
606
+ if (cinfo.output_components == 3 || cinfo.output_components == 1)
607
+ {
608
+ int c = cinfo.output_components;
609
+ for (i = 0; i < cinfo.rec_outbuf_height; i++)
610
+ line[i] = rgb + (i * w * c);
611
+ for (l = 0; l < h; l += cinfo.rec_outbuf_height)
612
+ {
613
+ jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
614
+ scans = cinfo.rec_outbuf_height;
615
+ if ((h - l) < scans) scans = h - l;
616
+ ptr = rgb;
617
+ for (y = 0; y < scans; y++)
618
+ {
619
+ for (x = 0; x < w; x++)
620
+ {
621
+ if (c == 3)
622
+ *ptr2 = (0xff000000) | ((ptr[0]) << 16) | ((ptr[1]) << 8) | (ptr[2]);
623
+ else if (c == 1)
624
+ *ptr2 = (0xff000000) | ((ptr[0]) << 16) | ((ptr[0]) << 8) | (ptr[0]);
625
+ LE_CPU(*ptr2);
626
+ ptr += c;
627
+ ptr2++;
628
+ }
629
+ }
630
+ }
631
+ }
632
+
633
+ surface = shoes_surface_create_from_pixels(pixels, w, h);
634
+ jpeg_finish_decompress(&cinfo);
635
+ done:
636
+ free(line);
637
+ if (pixels != NULL) free(pixels);
638
+ if (rgb != NULL) free(rgb);
639
+ jpeg_destroy_decompress(&cinfo);
640
+ #ifdef SHOES_WIN32
641
+ CloseHandle( hFile );
642
+ #else
643
+ fclose(f);
644
+ #endif
645
+ return surface;
646
+ }
647
+
648
+ char
649
+ shoes_has_ext(char *fname, int len, const char *ext)
650
+ {
651
+ return strncmp(fname + (len - strlen(ext)), ext, strlen(ext)) == 0;
652
+ }
653
+
654
+ unsigned char
655
+ shoes_check_file_exists(VALUE path)
656
+ {
657
+ if (!RTEST(rb_funcall(rb_cFile, rb_intern("exists?"), 1, path)))
658
+ {
659
+ StringValue(path);
660
+ shoes_error("Shoes could not find the file %s.", RSTRING_PTR(path));
661
+ return FALSE;
662
+ }
663
+ return TRUE;
664
+ }
665
+
666
+ void
667
+ shoes_unsupported_image(VALUE path)
668
+ {
669
+ VALUE ext = rb_funcall(rb_cFile, rb_intern("extname"), 1, path);
670
+ StringValue(path);
671
+ StringValue(ext);
672
+ shoes_error("Couldn't load %s. Shoes does not support images with the %s extension.",
673
+ RSTRING_PTR(path), RSTRING_PTR(ext));
674
+ }
675
+
676
+ void
677
+ shoes_failed_image(VALUE path)
678
+ {
679
+ VALUE ext = rb_funcall(rb_cFile, rb_intern("extname"), 1, path);
680
+ StringValue(path);
681
+ StringValue(ext);
682
+ shoes_error("Couldn't load %s. Is the file a valid %s?",
683
+ RSTRING_PTR(path), RSTRING_PTR(ext));
684
+ }
685
+
686
+ cairo_surface_t *
687
+ shoes_surface_create_from_file(VALUE imgpath, int *width, int *height)
688
+ {
689
+ cairo_surface_t *img = NULL;
690
+ shoes_image_format format = shoes_image_detect(imgpath, width, height);
691
+ if (format == SHOES_IMAGE_PNG)
692
+ {
693
+ img = cairo_image_surface_create_from_png(RSTRING_PTR(imgpath));
694
+ if (cairo_surface_status(img) != CAIRO_STATUS_SUCCESS)
695
+ img = NULL;
696
+ }
697
+ else if (format == SHOES_IMAGE_JPEG)
698
+ img = shoes_surface_create_from_jpeg(RSTRING_PTR(imgpath), width, height, TRUE);
699
+ else if (format == SHOES_IMAGE_GIF)
700
+ img = shoes_surface_create_from_gif(RSTRING_PTR(imgpath), width, height, TRUE);
701
+ else
702
+ return shoes_world->blank_image;
703
+
704
+ if (img == NULL)
705
+ {
706
+ shoes_failed_image(imgpath);
707
+ img = shoes_world->blank_image;
708
+ }
709
+
710
+ return img;
711
+ }
712
+
713
+ int
714
+ shoes_file_mtime(char *path)
715
+ {
716
+ int mtime = 0;
717
+ struct stat *s = SHOE_ALLOC(struct stat);
718
+ stat(path, s);
719
+ mtime = s->st_mtime;
720
+ SHOE_FREE(s);
721
+ return mtime;
722
+ }
723
+
724
+ shoes_cached_image *
725
+ shoes_cached_image_new(int width, int height, cairo_surface_t *surface)
726
+ {
727
+ shoes_cached_image *cached = SHOE_ALLOC(shoes_cached_image);
728
+ if (surface == NULL)
729
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
730
+ cached->surface = surface;
731
+ cached->pattern = NULL;
732
+ cached->width = width;
733
+ cached->height = height;
734
+ cached->mtime = 0;
735
+ return cached;
736
+ }
737
+
738
+ int
739
+ shoes_cache_lookup(char *imgpath, shoes_cached_image **image)
740
+ {
741
+ shoes_cache_entry *cached = NULL;
742
+ int ret = st_lookup(shoes_world->image_cache, (st_data_t)imgpath, (st_data_t *)&cached);
743
+ // if (ret && shoes_file_mtime(imgpath) == cached->image->mtime) {
744
+ // st_delete(shoes_world->image_cache, (st_data_t *)&imgpath, 0);
745
+ // // TODO: memory leak if an image's mtime changes (it's overwritten)
746
+ // // need to free this struct, but only after the surface is out of play
747
+ // // shoes_world_free_image_cache(imgpath, cached, NULL);
748
+ // ret = 0;
749
+ // }
750
+ if (ret) *image = cached->image;
751
+ return ret;
752
+ }
753
+
754
+ void
755
+ shoes_cache_insert(unsigned char type, VALUE imgpath, shoes_cached_image *image)
756
+ {
757
+ char *path = strdup(RSTRING_PTR(imgpath));
758
+ shoes_cache_entry *cached = SHOE_ALLOC(shoes_cache_entry);
759
+ cached->type = type;
760
+ cached->image = image;
761
+ if (type == SHOES_CACHE_FILE) image->mtime = shoes_file_mtime(path);
762
+ st_insert(shoes_world->image_cache, (st_data_t)path, (st_data_t)cached);
763
+ }
764
+
765
+ shoes_image_format
766
+ shoes_image_detect(VALUE imgpath, int *width, int *height)
767
+ {
768
+ shoes_image_format format = SHOES_IMAGE_NONE;
769
+ shoes_cached_image *cached = NULL;
770
+ cairo_surface_t *img = NULL;
771
+ VALUE filename = rb_funcall(imgpath, s_downcase, 0);
772
+ char *fname = RSTRING_PTR(filename);
773
+ int len = RSTRING_LEN(filename);
774
+
775
+ if (shoes_cache_lookup(RSTRING_PTR(imgpath), &cached))
776
+ {
777
+ *width = cached->width;
778
+ *height = cached->height;
779
+ return cached->format;
780
+ }
781
+
782
+ if (!shoes_check_file_exists(imgpath))
783
+ return SHOES_IMAGE_NONE;
784
+ else if (shoes_has_ext(fname, len, ".png"))
785
+ {
786
+ img = shoes_png_size(RSTRING_PTR(imgpath), width, height);
787
+ format = SHOES_IMAGE_PNG;
788
+ }
789
+ else if (shoes_has_ext(fname, len, ".jpg") || shoes_has_ext(fname, len, ".jpeg"))
790
+ {
791
+ img = shoes_surface_create_from_jpeg(RSTRING_PTR(imgpath), width, height, FALSE);
792
+ format = SHOES_IMAGE_JPEG;
793
+ }
794
+ else if (shoes_has_ext(fname, len, ".gif"))
795
+ {
796
+ img = shoes_surface_create_from_gif(RSTRING_PTR(imgpath), width, height, FALSE);
797
+ format = SHOES_IMAGE_GIF;
798
+ }
799
+
800
+ if (img != SIZE_SURFACE)
801
+ {
802
+ if (format != SHOES_IMAGE_PNG &&
803
+ (img = shoes_png_size(RSTRING_PTR(imgpath), width, height)) == SIZE_SURFACE)
804
+ format = SHOES_IMAGE_PNG;
805
+ else if (format != SHOES_IMAGE_JPEG &&
806
+ (img = shoes_surface_create_from_jpeg(RSTRING_PTR(imgpath), width, height, FALSE)) == SIZE_SURFACE)
807
+ format = SHOES_IMAGE_JPEG;
808
+ else if (format != SHOES_IMAGE_GIF &&
809
+ (img = shoes_surface_create_from_gif(RSTRING_PTR(imgpath), width, height, FALSE)) == SIZE_SURFACE)
810
+ format = SHOES_IMAGE_GIF;
811
+ }
812
+
813
+ if (format == SHOES_IMAGE_NONE)
814
+ {
815
+ shoes_unsupported_image(imgpath);
816
+ return format;
817
+ }
818
+
819
+ if (img != SIZE_SURFACE)
820
+ shoes_failed_image(imgpath);
821
+
822
+ return format;
823
+ }
824
+
825
+ shoes_code
826
+ shoes_load_imagesize(VALUE imgpath, int *width, int *height)
827
+ {
828
+ if (shoes_image_detect(imgpath, width, height) == SHOES_IMAGE_NONE)
829
+ return SHOES_FAIL;
830
+ return SHOES_OK;
831
+ }
832
+
833
+ unsigned char
834
+ shoes_image_downloaded(shoes_image_download_event *idat)
835
+ {
836
+ int i, j, width, height;
837
+ SHA1_CTX context;
838
+ unsigned char *digest = SHOE_ALLOC_N(unsigned char, 20),
839
+ *buffer = SHOE_ALLOC_N(unsigned char, 16384);
840
+
841
+ if (idat->status == 304)
842
+ {
843
+ idat->filepath = idat->cachepath;
844
+ idat->cachepath = NULL;
845
+ }
846
+ else if (idat->status != 200)
847
+ {
848
+ shoes_error("Shoes could not load the file at %s. [%lu]", idat->uripath, idat->status);
849
+ return 0;
850
+ }
851
+
852
+ cairo_surface_t *img = shoes_surface_create_from_file(rb_str_new2(idat->filepath), &width, &height);
853
+ if (img != NULL)
854
+ {
855
+ shoes_cached_image *cached;
856
+ if (shoes_cache_lookup(idat->uripath, &cached) && cached->surface == shoes_world->blank_image)
857
+ {
858
+ cached->surface = img;
859
+ cached->width = width;
860
+ cached->height = height;
861
+ cached->mtime = shoes_file_mtime(idat->filepath);
862
+
863
+ if (idat->status != 304)
864
+ {
865
+ #ifdef SHOES_WIN32
866
+ HANDLE hFile;
867
+ hFile = CreateFile( idat->filepath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
868
+ SHA1Init(&context);
869
+ DWORD readsize;
870
+ while (1)
871
+ {
872
+ ReadFile( hFile, buffer, 16384, &readsize, NULL );
873
+ if (readsize != 16384) break;
874
+ SHA1Update(&context, buffer, readsize);
875
+ }
876
+ SHA1Final(digest, &context);
877
+ CloseHandle( hFile );
878
+ #else
879
+ FILE* fp = fopen(idat->filepath, "rb");
880
+ if (fp == NULL)
881
+ {
882
+ shoes_error("Shoes was unable to open the cached file at %s.", idat->filepath);
883
+ return 0;
884
+ }
885
+ SHA1Init(&context);
886
+ while (!feof(fp))
887
+ {
888
+ i = fread(buffer, 1, 16384, fp);
889
+ SHA1Update(&context, buffer, i);
890
+ }
891
+ SHA1Final(digest, &context);
892
+ fclose(fp);
893
+ #endif
894
+
895
+ for (i = 0; i < 5; i++)
896
+ for (j = 0; j < 4; j++)
897
+ sprintf(&idat->hexdigest[(i*8)+(j*2)], "%02x", digest[i*4+j]);
898
+ idat->hexdigest[41] = '\0';
899
+ }
900
+ }
901
+ }
902
+
903
+ SHOE_FREE(digest);
904
+ SHOE_FREE(buffer);
905
+ return 1;
906
+ }
907
+
908
+ int
909
+ shoes_http_image_handler(shoes_http_event *de, void *data)
910
+ {
911
+ shoes_image_download_event *idat = (shoes_image_download_event *)data;
912
+ if (de->stage == SHOES_HTTP_STATUS)
913
+ {
914
+ idat->status = de->status;
915
+ }
916
+ else if (de->stage == SHOES_HTTP_HEADER)
917
+ {
918
+ if (de->hkeylen == 4 && strncmp(de->hkey, "ETag", 4) == 0 && de->hvallen > 0)
919
+ {
920
+ idat->etag = SHOE_ALLOC_N(char, de->hvallen + 1);
921
+ SHOE_MEMCPY(idat->etag, de->hval, char, de->hvallen);
922
+ idat->etag[de->hvallen] = '\0';
923
+ }
924
+ }
925
+ else if (de->stage == SHOES_HTTP_COMPLETED)
926
+ {
927
+ shoes_image_download_event *side = SHOE_ALLOC(shoes_image_download_event);
928
+ SHOE_MEMCPY(side, idat, shoes_image_download_event, 1);
929
+ return shoes_throw_message(SHOES_IMAGE_DOWNLOAD, idat->slot, side);
930
+ }
931
+ return SHOES_DOWNLOAD_CONTINUE;
932
+ }
933
+
934
+ shoes_cached_image *
935
+ shoes_load_image(VALUE slot, VALUE imgpath)
936
+ {
937
+ shoes_cached_image *cached = NULL;
938
+ cairo_surface_t *img = NULL;
939
+ VALUE filename = rb_funcall(imgpath, s_downcase, 0);
940
+ StringValue(filename);
941
+ char *fname = RSTRING_PTR(filename);
942
+ int width = 1, height = 1;
943
+
944
+ if (shoes_cache_lookup(RSTRING_PTR(imgpath), &cached))
945
+ goto done;
946
+
947
+ if (strlen(fname) > 7 && (strncmp(fname, "http://", 7) == 0 || strncmp(fname, "https://", 8) == 0))
948
+ {
949
+ struct timeval tv;
950
+ VALUE cache, uext, hdrs, tmppath, uri, scheme, host, port, requ, path, cachepath = Qnil, hash = Qnil;
951
+ rb_require("shoes/data");
952
+ uri = rb_funcall(cShoes, rb_intern("uri"), 1, imgpath);
953
+ scheme = rb_funcall(uri, s_scheme, 0);
954
+ host = rb_funcall(uri, s_host, 0);
955
+ port = rb_funcall(uri, s_port, 0);
956
+ requ = rb_funcall(uri, s_request_uri, 0);
957
+ path = rb_funcall(uri, s_path, 0);
958
+ path = rb_funcall(path, s_downcase, 0);
959
+
960
+ cache = rb_funcall(rb_const_get(rb_cObject, rb_intern("DATABASE")), rb_intern("check_cache_for"), 1, imgpath);
961
+ uext = rb_funcall(rb_cFile, rb_intern("extname"), 1, path);
962
+ hdrs = Qnil;
963
+ if (!NIL_P(cache))
964
+ {
965
+ VALUE etag = rb_hash_aref(cache, ID2SYM(rb_intern("etag")));
966
+ hash = rb_hash_aref(cache, ID2SYM(rb_intern("hash")));
967
+ if (!NIL_P(hash)) cachepath = rb_funcall(cShoes, rb_intern("image_cache_path"), 2, hash, uext);
968
+ int saved = NUM2INT(rb_hash_aref(cache, ID2SYM(rb_intern("saved"))));
969
+ gettimeofday(&tv, 0);
970
+ if (tv.tv_sec - saved < SHOES_IMAGE_EXPIRE)
971
+ {
972
+ cached = shoes_load_image(slot, cachepath);
973
+ if (cached != NULL)
974
+ {
975
+ shoes_cache_insert(SHOES_CACHE_ALIAS, imgpath, cached);
976
+ goto done;
977
+ }
978
+ }
979
+ else if (!NIL_P(etag))
980
+ rb_hash_aset(hdrs = rb_hash_new(), rb_str_new2("If-None-Match"), etag);
981
+ }
982
+
983
+ cached = shoes_cached_image_new(1, 1, shoes_world->blank_image);
984
+ shoes_cache_insert(SHOES_CACHE_FILE, imgpath, cached);
985
+ tmppath = rb_funcall(cShoes, rb_intern("image_temp_path"), 2, uri, uext);
986
+
987
+ shoes_http_request *req = SHOE_ALLOC(shoes_http_request);
988
+ SHOE_MEMZERO(req, shoes_http_request, 1);
989
+ shoes_image_download_event *idat = SHOE_ALLOC(shoes_image_download_event);
990
+ SHOE_MEMZERO(idat, shoes_image_download_event, 1);
991
+ req->url = strdup(RSTRING_PTR(imgpath));
992
+ req->scheme = strdup(RSTRING_PTR(scheme));
993
+ req->host = strdup(RSTRING_PTR(host));
994
+ req->port = NUM2INT(port);
995
+ req->path = strdup(RSTRING_PTR(requ));
996
+ req->handler = shoes_http_image_handler;
997
+ req->filepath = strdup(RSTRING_PTR(tmppath));
998
+ idat->filepath = strdup(RSTRING_PTR(tmppath));
999
+ idat->uripath = strdup(RSTRING_PTR(imgpath));
1000
+ idat->slot = slot;
1001
+ if (!NIL_P(cachepath)) idat->cachepath = strdup(RSTRING_PTR(cachepath));
1002
+ if (!NIL_P(hash)) SHOE_MEMCPY(idat->hexdigest, RSTRING_PTR(hash), char, min(42, RSTRING_LEN(hash)));
1003
+ if (!NIL_P(hdrs)) req->headers = shoes_http_headers(hdrs);
1004
+ req->data = idat;
1005
+ shoes_queue_download(req);
1006
+ goto done;
1007
+ }
1008
+
1009
+ img = shoes_surface_create_from_file(imgpath, &width, &height);
1010
+ if (img != shoes_world->blank_image)
1011
+ {
1012
+ cached = shoes_cached_image_new(width, height, img);
1013
+ shoes_cache_insert(SHOES_CACHE_FILE, imgpath, cached);
1014
+ }
1015
+
1016
+ done:
1017
+ if (cached == NULL)
1018
+ cached = shoes_world->blank_cache;
1019
+ return cached;
1020
+ }