konpeito 0.1.0

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 (180) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +75 -0
  4. data/CONTRIBUTING.md +123 -0
  5. data/LICENSE +21 -0
  6. data/README.md +257 -0
  7. data/Rakefile +11 -0
  8. data/bin/konpeito +6 -0
  9. data/konpeito.gemspec +43 -0
  10. data/lib/konpeito/ast/typed_ast.rb +620 -0
  11. data/lib/konpeito/ast/visitor.rb +78 -0
  12. data/lib/konpeito/cache/cache_manager.rb +230 -0
  13. data/lib/konpeito/cache/dependency_graph.rb +192 -0
  14. data/lib/konpeito/cache.rb +8 -0
  15. data/lib/konpeito/cli/base_command.rb +187 -0
  16. data/lib/konpeito/cli/build_command.rb +220 -0
  17. data/lib/konpeito/cli/check_command.rb +104 -0
  18. data/lib/konpeito/cli/config.rb +231 -0
  19. data/lib/konpeito/cli/deps_command.rb +128 -0
  20. data/lib/konpeito/cli/doctor_command.rb +340 -0
  21. data/lib/konpeito/cli/fmt_command.rb +199 -0
  22. data/lib/konpeito/cli/init_command.rb +312 -0
  23. data/lib/konpeito/cli/lsp_command.rb +40 -0
  24. data/lib/konpeito/cli/run_command.rb +150 -0
  25. data/lib/konpeito/cli/test_command.rb +248 -0
  26. data/lib/konpeito/cli/watch_command.rb +212 -0
  27. data/lib/konpeito/cli.rb +301 -0
  28. data/lib/konpeito/codegen/builtin_methods.rb +229 -0
  29. data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
  30. data/lib/konpeito/codegen/debug_info.rb +352 -0
  31. data/lib/konpeito/codegen/inliner.rb +486 -0
  32. data/lib/konpeito/codegen/jvm_backend.rb +197 -0
  33. data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
  34. data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
  35. data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
  36. data/lib/konpeito/codegen/monomorphizer.rb +359 -0
  37. data/lib/konpeito/codegen/profile_runtime.c +341 -0
  38. data/lib/konpeito/codegen/profiler.rb +99 -0
  39. data/lib/konpeito/compiler.rb +592 -0
  40. data/lib/konpeito/dependency_resolver.rb +296 -0
  41. data/lib/konpeito/diagnostics/collector.rb +127 -0
  42. data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
  43. data/lib/konpeito/diagnostics/renderer.rb +144 -0
  44. data/lib/konpeito/formatter/formatter.rb +1214 -0
  45. data/lib/konpeito/hir/builder.rb +7167 -0
  46. data/lib/konpeito/hir/nodes.rb +2465 -0
  47. data/lib/konpeito/lsp/document_manager.rb +820 -0
  48. data/lib/konpeito/lsp/server.rb +183 -0
  49. data/lib/konpeito/lsp/transport.rb +38 -0
  50. data/lib/konpeito/parser/prism_adapter.rb +65 -0
  51. data/lib/konpeito/platform.rb +103 -0
  52. data/lib/konpeito/profile/report.rb +136 -0
  53. data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
  54. data/lib/konpeito/stdlib/compression/compression.rb +72 -0
  55. data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
  56. data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
  57. data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
  58. data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
  59. data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
  60. data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
  61. data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
  62. data/lib/konpeito/stdlib/http/extconf.rb +19 -0
  63. data/lib/konpeito/stdlib/http/http.rb +125 -0
  64. data/lib/konpeito/stdlib/http/http.rbs +57 -0
  65. data/lib/konpeito/stdlib/http/http_native.c +440 -0
  66. data/lib/konpeito/stdlib/json/extconf.rb +17 -0
  67. data/lib/konpeito/stdlib/json/json.rb +44 -0
  68. data/lib/konpeito/stdlib/json/json.rbs +33 -0
  69. data/lib/konpeito/stdlib/json/json_native.c +286 -0
  70. data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
  71. data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
  72. data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
  73. data/lib/konpeito/stdlib/ui/ui.rb +318 -0
  74. data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
  75. data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
  76. data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
  77. data/lib/konpeito/type_checker/inferrer.rb +565 -0
  78. data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
  79. data/lib/konpeito/type_checker/type_resolver.rb +276 -0
  80. data/lib/konpeito/type_checker/types.rb +1434 -0
  81. data/lib/konpeito/type_checker/unification.rb +323 -0
  82. data/lib/konpeito/ui/animation/animated_state.rb +80 -0
  83. data/lib/konpeito/ui/animation/easing.rb +59 -0
  84. data/lib/konpeito/ui/animation/value_tween.rb +66 -0
  85. data/lib/konpeito/ui/app.rb +379 -0
  86. data/lib/konpeito/ui/box.rb +38 -0
  87. data/lib/konpeito/ui/castella.rb +70 -0
  88. data/lib/konpeito/ui/castella_native.rb +76 -0
  89. data/lib/konpeito/ui/chart/area_chart.rb +305 -0
  90. data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
  91. data/lib/konpeito/ui/chart/base_chart.rb +210 -0
  92. data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
  93. data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
  94. data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
  95. data/lib/konpeito/ui/chart/line_chart.rb +289 -0
  96. data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
  97. data/lib/konpeito/ui/chart/scales.rb +77 -0
  98. data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
  99. data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
  100. data/lib/konpeito/ui/column.rb +271 -0
  101. data/lib/konpeito/ui/core.rb +2199 -0
  102. data/lib/konpeito/ui/dsl.rb +443 -0
  103. data/lib/konpeito/ui/frame.rb +171 -0
  104. data/lib/konpeito/ui/frame_native.rb +494 -0
  105. data/lib/konpeito/ui/markdown/ast.rb +124 -0
  106. data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
  107. data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
  108. data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
  109. data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
  110. data/lib/konpeito/ui/markdown/parser.rb +805 -0
  111. data/lib/konpeito/ui/markdown/renderer.rb +639 -0
  112. data/lib/konpeito/ui/markdown/theme.rb +165 -0
  113. data/lib/konpeito/ui/render_node.rb +260 -0
  114. data/lib/konpeito/ui/row.rb +207 -0
  115. data/lib/konpeito/ui/spacer.rb +18 -0
  116. data/lib/konpeito/ui/style.rb +799 -0
  117. data/lib/konpeito/ui/theme.rb +563 -0
  118. data/lib/konpeito/ui/themes/material.rb +35 -0
  119. data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
  120. data/lib/konpeito/ui/widgets/button.rb +103 -0
  121. data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
  122. data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
  123. data/lib/konpeito/ui/widgets/container.rb +91 -0
  124. data/lib/konpeito/ui/widgets/data_table.rb +667 -0
  125. data/lib/konpeito/ui/widgets/divider.rb +29 -0
  126. data/lib/konpeito/ui/widgets/image.rb +105 -0
  127. data/lib/konpeito/ui/widgets/input.rb +485 -0
  128. data/lib/konpeito/ui/widgets/markdown.rb +57 -0
  129. data/lib/konpeito/ui/widgets/modal.rb +163 -0
  130. data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
  131. data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
  132. data/lib/konpeito/ui/widgets/net_image.rb +100 -0
  133. data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
  134. data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
  135. data/lib/konpeito/ui/widgets/slider.rb +133 -0
  136. data/lib/konpeito/ui/widgets/switch.rb +84 -0
  137. data/lib/konpeito/ui/widgets/tabs.rb +157 -0
  138. data/lib/konpeito/ui/widgets/text.rb +110 -0
  139. data/lib/konpeito/ui/widgets/tree.rb +426 -0
  140. data/lib/konpeito/version.rb +5 -0
  141. data/lib/konpeito.rb +109 -0
  142. data/test_native_array.rb +172 -0
  143. data/test_native_array_class.rb +197 -0
  144. data/test_native_class.rb +151 -0
  145. data/tools/konpeito-asm/build.sh +65 -0
  146. data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
  147. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
  148. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
  149. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
  150. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
  151. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
  152. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
  153. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
  154. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
  155. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
  156. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
  157. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
  158. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
  159. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
  160. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
  161. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
  162. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
  163. data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
  164. data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
  165. data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
  166. data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
  167. data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
  168. data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
  169. data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
  170. data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
  171. data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
  172. data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
  173. data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
  174. data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
  175. data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
  176. data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
  177. data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
  178. data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
  179. data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
  180. metadata +267 -0
@@ -0,0 +1,1625 @@
1
+ /*
2
+ * Konpeito UI Native - SDL3 + Skia CRuby Extension
3
+ *
4
+ * Provides window management, event polling, and 2D drawing
5
+ * for the Castella UI framework on the LLVM backend.
6
+ *
7
+ * Architecture:
8
+ * Ruby (NativeFrame) -> CRuby C API -> SDL3 (window/events) + Skia (drawing)
9
+ *
10
+ * Event model: Polling (not callbacks) -- avoids C->Ruby callback requirement.
11
+ * SDL3 events are polled into a ring buffer; Ruby reads them one at a time.
12
+ */
13
+
14
+ #include "konpeito_ui_native.h"
15
+
16
+ #include <SDL3/SDL.h>
17
+
18
+ #include <include/core/SkCanvas.h>
19
+ #include <include/core/SkSurface.h>
20
+ #include <include/core/SkPaint.h>
21
+ #include <include/core/SkPath.h>
22
+ #include <include/core/SkFont.h>
23
+ #include <include/core/SkFontMgr.h>
24
+ #include <include/core/SkTypeface.h>
25
+ #include <include/core/SkImage.h>
26
+ #include <include/core/SkData.h>
27
+ #include <include/core/SkColorSpace.h>
28
+ #include <include/core/SkRRect.h>
29
+ #include <include/core/SkFontStyle.h>
30
+ #include <include/core/SkFontMetrics.h>
31
+ #include <include/core/SkTextBlob.h>
32
+ #include <include/core/SkStream.h>
33
+ #include <include/core/SkBitmap.h>
34
+ #include <include/codec/SkCodec.h>
35
+ #include <include/ports/SkFontMgr_directory.h>
36
+
37
+ /* Windows: GetWindowsDirectoryA for font path */
38
+ #ifdef _WIN32
39
+ #include <windows.h>
40
+ #endif
41
+
42
+ /* GPU backend headers (Metal on macOS, GL on Linux/Windows) */
43
+ #ifdef __APPLE__
44
+ #include <include/gpu/GrDirectContext.h>
45
+ #include <include/gpu/GrBackendSurface.h>
46
+ #include <include/gpu/ganesh/mtl/GrMtlDirectContext.h>
47
+ #include <include/gpu/ganesh/mtl/GrMtlBackendContext.h>
48
+ #include <include/gpu/ganesh/mtl/GrMtlTypes.h>
49
+ #include <include/gpu/ganesh/SkSurfaceGanesh.h>
50
+ #include <SDL3/SDL_metal.h>
51
+ #import <Metal/Metal.h>
52
+ #import <QuartzCore/CAMetalLayer.h>
53
+ #else
54
+ #include <include/gpu/GrDirectContext.h>
55
+ #include <include/gpu/GrBackendSurface.h>
56
+ #include <include/gpu/ganesh/gl/GrGLDirectContext.h>
57
+ #include <include/gpu/ganesh/gl/GrGLInterface.h>
58
+ #include <include/gpu/ganesh/SkSurfaceGanesh.h>
59
+ #include <SDL3/SDL_opengl.h>
60
+ #endif
61
+
62
+ #include <cmath>
63
+ #include <cstring>
64
+ #include <cstdio>
65
+ #include <cstdlib>
66
+ #include <vector>
67
+ #include <unordered_map>
68
+ #include <string>
69
+ #include <chrono>
70
+
71
+ /* ---------- SDL3 scancode -> JWM key ordinal mapping ---------- */
72
+
73
+ static int sdl_scancode_to_jwm_ordinal(SDL_Scancode sc) {
74
+ switch (sc) {
75
+ /* Function keys */
76
+ case SDL_SCANCODE_CAPSLOCK: return 0;
77
+ case SDL_SCANCODE_F1: return 1;
78
+ case SDL_SCANCODE_F2: return 2;
79
+ case SDL_SCANCODE_F3: return 3;
80
+ case SDL_SCANCODE_F4: return 4;
81
+ case SDL_SCANCODE_F5: return 5;
82
+ case SDL_SCANCODE_F6: return 6;
83
+ case SDL_SCANCODE_F7: return 7;
84
+ case SDL_SCANCODE_F8: return 8;
85
+ case SDL_SCANCODE_F9: return 9;
86
+ case SDL_SCANCODE_F10: return 10;
87
+ case SDL_SCANCODE_RETURN: return 11; /* ENTER */
88
+ case SDL_SCANCODE_BACKSPACE: return 12;
89
+ case SDL_SCANCODE_TAB: return 13;
90
+ case SDL_SCANCODE_SPACE: return 14;
91
+ case SDL_SCANCODE_PRINTSCREEN: return 15;
92
+ case SDL_SCANCODE_SCROLLLOCK: return 16;
93
+ case SDL_SCANCODE_ESCAPE: return 17;
94
+ case SDL_SCANCODE_INSERT: return 20;
95
+ case SDL_SCANCODE_END: return 21;
96
+ case SDL_SCANCODE_HOME: return 22;
97
+ case SDL_SCANCODE_LEFT: return 23;
98
+ case SDL_SCANCODE_UP: return 24;
99
+ case SDL_SCANCODE_RIGHT: return 25;
100
+ case SDL_SCANCODE_DOWN: return 26;
101
+ case SDL_SCANCODE_PAGEUP: return 27;
102
+ case SDL_SCANCODE_PAGEDOWN: return 28;
103
+
104
+ /* Punctuation (ordinals 29-34) */
105
+ case SDL_SCANCODE_COMMA: return 29;
106
+ case SDL_SCANCODE_PERIOD: return 30;
107
+ case SDL_SCANCODE_SLASH: return 31;
108
+ case SDL_SCANCODE_LEFTBRACKET: return 32;
109
+ case SDL_SCANCODE_RIGHTBRACKET: return 33;
110
+ case SDL_SCANCODE_BACKSLASH: return 34;
111
+
112
+ /* Digits (ordinals 35-44) -> 0-9 */
113
+ case SDL_SCANCODE_0: return 35;
114
+ case SDL_SCANCODE_1: return 36;
115
+ case SDL_SCANCODE_2: return 37;
116
+ case SDL_SCANCODE_3: return 38;
117
+ case SDL_SCANCODE_4: return 39;
118
+ case SDL_SCANCODE_5: return 40;
119
+ case SDL_SCANCODE_6: return 41;
120
+ case SDL_SCANCODE_7: return 42;
121
+
122
+ /* Letter keys A-Z (ordinals 43-68) */
123
+ case SDL_SCANCODE_A: return 43;
124
+ case SDL_SCANCODE_B: return 44;
125
+ case SDL_SCANCODE_C: return 45;
126
+ case SDL_SCANCODE_D: return 46;
127
+ case SDL_SCANCODE_E: return 47;
128
+ case SDL_SCANCODE_F: return 48;
129
+ case SDL_SCANCODE_G: return 49;
130
+ case SDL_SCANCODE_H: return 50;
131
+ case SDL_SCANCODE_I: return 51;
132
+ case SDL_SCANCODE_J: return 52;
133
+ case SDL_SCANCODE_K: return 53;
134
+ case SDL_SCANCODE_L: return 54;
135
+ case SDL_SCANCODE_M: return 55;
136
+ case SDL_SCANCODE_N: return 56;
137
+ case SDL_SCANCODE_O: return 57;
138
+ case SDL_SCANCODE_P: return 58;
139
+ case SDL_SCANCODE_Q: return 59;
140
+ case SDL_SCANCODE_R: return 60;
141
+ case SDL_SCANCODE_S: return 61;
142
+ case SDL_SCANCODE_T: return 62;
143
+ case SDL_SCANCODE_U: return 63;
144
+ case SDL_SCANCODE_V: return 64;
145
+ case SDL_SCANCODE_W: return 65;
146
+ case SDL_SCANCODE_X: return 66;
147
+ case SDL_SCANCODE_Y: return 67;
148
+ case SDL_SCANCODE_Z: return 68;
149
+
150
+ case SDL_SCANCODE_DELETE: return 75;
151
+
152
+ default: return -1; /* Unknown */
153
+ }
154
+ }
155
+
156
+ /* ---------- SDL3 modifier conversion ---------- */
157
+
158
+ static int sdl_mod_to_jwm_mod(SDL_Keymod mod) {
159
+ int result = 0;
160
+ if (mod & SDL_KMOD_SHIFT) result |= KUI_MOD_SHIFT;
161
+ if (mod & SDL_KMOD_CTRL) result |= KUI_MOD_CONTROL;
162
+ if (mod & SDL_KMOD_ALT) result |= KUI_MOD_ALT;
163
+ if (mod & SDL_KMOD_GUI) result |= KUI_MOD_SUPER;
164
+ return result;
165
+ }
166
+
167
+ /* ---------- KUIContext structure ---------- */
168
+
169
+ struct KUIContext {
170
+ /* SDL */
171
+ SDL_Window* window;
172
+ int width, height;
173
+ float scale;
174
+ bool dirty;
175
+ bool frame_requested;
176
+ bool text_input_enabled;
177
+
178
+ /* Skia GPU context */
179
+ #ifdef __APPLE__
180
+ SDL_MetalView metal_view;
181
+ sk_sp<GrDirectContext> gr_context;
182
+ id<MTLDevice> mtl_device;
183
+ id<MTLCommandQueue> mtl_queue;
184
+ CAMetalLayer* metal_layer;
185
+ id<CAMetalDrawable> current_drawable; /* Retained between begin/end frame */
186
+ #else
187
+ SDL_GLContext gl_context;
188
+ sk_sp<GrDirectContext> gr_context;
189
+ #endif
190
+
191
+ /* Skia drawing state */
192
+ sk_sp<SkSurface> surface;
193
+ SkCanvas* canvas;
194
+ SkPath current_path;
195
+
196
+ /* Font manager */
197
+ sk_sp<SkFontMgr> font_mgr;
198
+
199
+ /* Image cache */
200
+ std::unordered_map<int, sk_sp<SkImage>> images;
201
+ int next_image_id;
202
+
203
+ /* Event ring buffer */
204
+ KUIEvent events[KUI_EVENT_BUFFER_SIZE];
205
+ int event_read;
206
+ int event_write;
207
+ int event_count;
208
+
209
+ /* Clipboard cache (to return stable pointer) */
210
+ std::string clipboard_cache;
211
+
212
+ /* Number-to-string cache */
213
+ char num_str_buf[64];
214
+ };
215
+
216
+ /* ---------- Event ring buffer helpers ---------- */
217
+
218
+ static void push_event(KUIContext* ctx, const KUIEvent& ev) {
219
+ if (ctx->event_count >= KUI_EVENT_BUFFER_SIZE) return; /* drop if full */
220
+ ctx->events[ctx->event_write] = ev;
221
+ ctx->event_write = (ctx->event_write + 1) % KUI_EVENT_BUFFER_SIZE;
222
+ ctx->event_count++;
223
+ }
224
+
225
+ static KUIEvent* peek_event(KUIContext* ctx) {
226
+ if (ctx->event_count == 0) return nullptr;
227
+ return &ctx->events[ctx->event_read];
228
+ }
229
+
230
+ /* ---------- Color helpers ---------- */
231
+
232
+ static SkColor uint32_to_skcolor(uint32_t c) {
233
+ /* Konpeito/JWM colors are 0xAARRGGBB (same as SkColor) */
234
+ return (SkColor)c;
235
+ }
236
+
237
+ static uint8_t clamp_u8(int v) {
238
+ if (v < 0) return 0;
239
+ if (v > 255) return 255;
240
+ return (uint8_t)v;
241
+ }
242
+
243
+ /* ---------- Font helper ---------- */
244
+
245
+ static sk_sp<SkTypeface> find_typeface(KUIContext* ctx, const char* family,
246
+ int weight, int slant) {
247
+ SkFontStyle style(
248
+ weight == 1 ? SkFontStyle::kBold_Weight : SkFontStyle::kNormal_Weight,
249
+ SkFontStyle::kNormal_Width,
250
+ slant == 1 ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant
251
+ );
252
+ sk_sp<SkTypeface> tf = ctx->font_mgr->matchFamilyStyle(family, style);
253
+ if (!tf) {
254
+ /* Fallback to default */
255
+ tf = ctx->font_mgr->matchFamilyStyle(nullptr, style);
256
+ }
257
+ if (!tf) {
258
+ /* Last resort: try sans-serif */
259
+ tf = ctx->font_mgr->matchFamilyStyle("Helvetica", style);
260
+ }
261
+ return tf;
262
+ }
263
+
264
+ /* ============================================================
265
+ * C API Implementation
266
+ * ============================================================ */
267
+
268
+ extern "C" {
269
+
270
+ /* --- Window management --- */
271
+
272
+ KUIContext* kui_create_window(const char* title, int width, int height) {
273
+ if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) {
274
+ fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
275
+ return nullptr;
276
+ }
277
+
278
+ KUIContext* ctx = new KUIContext();
279
+ memset(ctx->events, 0, sizeof(ctx->events));
280
+ ctx->event_read = 0;
281
+ ctx->event_write = 0;
282
+ ctx->event_count = 0;
283
+ ctx->dirty = true;
284
+ ctx->frame_requested = true;
285
+ ctx->text_input_enabled = false;
286
+ ctx->next_image_id = 1;
287
+ ctx->canvas = nullptr;
288
+ ctx->scale = 1.0f;
289
+
290
+ #ifdef __APPLE__
291
+ /* Metal backend on macOS */
292
+ SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal");
293
+ ctx->window = SDL_CreateWindow(title, width, height,
294
+ SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_METAL);
295
+ if (!ctx->window) {
296
+ fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
297
+ delete ctx;
298
+ return nullptr;
299
+ }
300
+
301
+ ctx->metal_view = SDL_Metal_CreateView(ctx->window);
302
+ ctx->metal_layer = (__bridge CAMetalLayer*)SDL_Metal_GetLayer(ctx->metal_view);
303
+
304
+ ctx->mtl_device = MTLCreateSystemDefaultDevice();
305
+ ctx->metal_layer.device = ctx->mtl_device;
306
+ ctx->metal_layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
307
+ ctx->metal_layer.framebufferOnly = NO;
308
+ ctx->current_drawable = nil;
309
+
310
+ ctx->mtl_queue = [ctx->mtl_device newCommandQueue];
311
+
312
+ GrMtlBackendContext backend_ctx = {};
313
+ backend_ctx.fDevice.retain((__bridge void*)ctx->mtl_device);
314
+ backend_ctx.fQueue.retain((__bridge void*)ctx->mtl_queue);
315
+
316
+ ctx->gr_context = GrDirectContexts::MakeMetal(backend_ctx);
317
+ if (!ctx->gr_context) {
318
+ fprintf(stderr, "Failed to create Skia Metal context\n");
319
+ SDL_DestroyWindow(ctx->window);
320
+ delete ctx;
321
+ return nullptr;
322
+ }
323
+ #else
324
+ /* OpenGL backend on Linux/Windows */
325
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
326
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
327
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
328
+ SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
329
+
330
+ ctx->window = SDL_CreateWindow(title, width, height,
331
+ SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_OPENGL);
332
+ if (!ctx->window) {
333
+ fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
334
+ delete ctx;
335
+ return nullptr;
336
+ }
337
+
338
+ ctx->gl_context = SDL_GL_CreateContext(ctx->window);
339
+ SDL_GL_MakeCurrent(ctx->window, ctx->gl_context);
340
+
341
+ auto gl_interface = GrGLMakeNativeInterface();
342
+ ctx->gr_context = GrDirectContexts::MakeGL(gl_interface);
343
+ if (!ctx->gr_context) {
344
+ fprintf(stderr, "Failed to create Skia GL context\n");
345
+ SDL_DestroyWindow(ctx->window);
346
+ delete ctx;
347
+ return nullptr;
348
+ }
349
+ #endif
350
+
351
+ /* Get actual pixel size (for HiDPI) */
352
+ int pw, ph;
353
+ SDL_GetWindowSizeInPixels(ctx->window, &pw, &ph);
354
+ ctx->width = width;
355
+ ctx->height = height;
356
+ ctx->scale = (float)pw / (float)width;
357
+
358
+ /* Font manager: scan system fonts directory (FreeType-based) */
359
+ #ifdef __APPLE__
360
+ ctx->font_mgr = SkFontMgr_New_Custom_Directory("/System/Library/Fonts");
361
+ #elif defined(_WIN32)
362
+ {
363
+ char windir[MAX_PATH];
364
+ GetWindowsDirectoryA(windir, MAX_PATH);
365
+ std::string font_path = std::string(windir) + "\\Fonts";
366
+ ctx->font_mgr = SkFontMgr_New_Custom_Directory(font_path.c_str());
367
+ }
368
+ #else
369
+ ctx->font_mgr = SkFontMgr_New_Custom_Directory("/usr/share/fonts");
370
+ #endif
371
+
372
+ if (!ctx->font_mgr) {
373
+ /* Fallback */
374
+ ctx->font_mgr = SkFontMgr::RefEmpty();
375
+ }
376
+
377
+ return ctx;
378
+ }
379
+
380
+ void kui_destroy_window(KUIContext* ctx) {
381
+ if (!ctx) return;
382
+
383
+ ctx->images.clear();
384
+ ctx->surface.reset();
385
+ ctx->canvas = nullptr;
386
+
387
+ #ifdef __APPLE__
388
+ ctx->current_drawable = nil;
389
+ #endif
390
+
391
+ ctx->gr_context.reset();
392
+
393
+ #ifdef __APPLE__
394
+ if (ctx->metal_view) {
395
+ SDL_Metal_DestroyView(ctx->metal_view);
396
+ }
397
+ #else
398
+ if (ctx->gl_context) {
399
+ SDL_GL_DestroyContext(ctx->gl_context);
400
+ }
401
+ #endif
402
+
403
+ if (ctx->window) {
404
+ SDL_DestroyWindow(ctx->window);
405
+ }
406
+
407
+ SDL_Quit();
408
+ delete ctx;
409
+ }
410
+
411
+ void kui_step(KUIContext* ctx) {
412
+ if (!ctx) return;
413
+
414
+ SDL_Event sdl_ev;
415
+ while (SDL_PollEvent(&sdl_ev)) {
416
+ KUIEvent ev;
417
+ memset(&ev, 0, sizeof(ev));
418
+
419
+ switch (sdl_ev.type) {
420
+ case SDL_EVENT_QUIT:
421
+ ev.type = KUI_EVENT_QUIT;
422
+ push_event(ctx, ev);
423
+ break;
424
+
425
+ case SDL_EVENT_MOUSE_BUTTON_DOWN:
426
+ ev.type = KUI_EVENT_MOUSE_DOWN;
427
+ ev.x = sdl_ev.button.x;
428
+ ev.y = sdl_ev.button.y;
429
+ ev.button = sdl_ev.button.button - 1; /* SDL: 1-based -> 0-based */
430
+ push_event(ctx, ev);
431
+ break;
432
+
433
+ case SDL_EVENT_MOUSE_BUTTON_UP:
434
+ ev.type = KUI_EVENT_MOUSE_UP;
435
+ ev.x = sdl_ev.button.x;
436
+ ev.y = sdl_ev.button.y;
437
+ ev.button = sdl_ev.button.button - 1;
438
+ push_event(ctx, ev);
439
+ break;
440
+
441
+ case SDL_EVENT_MOUSE_MOTION:
442
+ ev.type = KUI_EVENT_MOUSE_MOVE;
443
+ ev.x = sdl_ev.motion.x;
444
+ ev.y = sdl_ev.motion.y;
445
+ push_event(ctx, ev);
446
+ break;
447
+
448
+ case SDL_EVENT_MOUSE_WHEEL:
449
+ ev.type = KUI_EVENT_MOUSE_WHEEL;
450
+ ev.dx = sdl_ev.wheel.x;
451
+ ev.dy = sdl_ev.wheel.y;
452
+ /* Get current mouse position for wheel events */
453
+ {
454
+ float mx, my;
455
+ SDL_GetMouseState(&mx, &my);
456
+ ev.x = mx;
457
+ ev.y = my;
458
+ }
459
+ push_event(ctx, ev);
460
+ break;
461
+
462
+ case SDL_EVENT_KEY_DOWN:
463
+ case SDL_EVENT_KEY_UP: {
464
+ ev.type = (sdl_ev.type == SDL_EVENT_KEY_DOWN) ? KUI_EVENT_KEY_DOWN : KUI_EVENT_KEY_UP;
465
+ ev.key_code = sdl_scancode_to_jwm_ordinal(sdl_ev.key.scancode);
466
+ ev.modifiers = sdl_mod_to_jwm_mod(sdl_ev.key.mod);
467
+ if (ev.key_code >= 0) {
468
+ push_event(ctx, ev);
469
+ }
470
+ break;
471
+ }
472
+
473
+ case SDL_EVENT_TEXT_INPUT:
474
+ ev.type = KUI_EVENT_TEXT_INPUT;
475
+ strncpy(ev.text, sdl_ev.text.text, sizeof(ev.text) - 1);
476
+ ev.text[sizeof(ev.text) - 1] = '\0';
477
+ push_event(ctx, ev);
478
+ break;
479
+
480
+ case SDL_EVENT_TEXT_EDITING:
481
+ ev.type = KUI_EVENT_IME_PREEDIT;
482
+ strncpy(ev.text, sdl_ev.edit.text, sizeof(ev.text) - 1);
483
+ ev.text[sizeof(ev.text) - 1] = '\0';
484
+ ev.ime_sel_start = sdl_ev.edit.start;
485
+ ev.ime_sel_end = sdl_ev.edit.start + sdl_ev.edit.length;
486
+ push_event(ctx, ev);
487
+ break;
488
+
489
+ case SDL_EVENT_WINDOW_RESIZED:
490
+ case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: {
491
+ ev.type = KUI_EVENT_RESIZE;
492
+ int w, h;
493
+ SDL_GetWindowSize(ctx->window, &w, &h);
494
+ ctx->width = w;
495
+ ctx->height = h;
496
+ int pw, ph;
497
+ SDL_GetWindowSizeInPixels(ctx->window, &pw, &ph);
498
+ ctx->scale = (float)pw / (float)w;
499
+ ctx->dirty = true;
500
+ push_event(ctx, ev);
501
+ break;
502
+ }
503
+
504
+ default:
505
+ break;
506
+ }
507
+ }
508
+ }
509
+
510
+ /* --- Event access --- */
511
+
512
+ bool kui_has_event(KUIContext* ctx) {
513
+ return ctx && ctx->event_count > 0;
514
+ }
515
+
516
+ int kui_event_type(KUIContext* ctx) {
517
+ KUIEvent* ev = peek_event(ctx);
518
+ return ev ? ev->type : KUI_EVENT_NONE;
519
+ }
520
+
521
+ double kui_event_x(KUIContext* ctx) {
522
+ KUIEvent* ev = peek_event(ctx);
523
+ return ev ? ev->x : 0.0;
524
+ }
525
+
526
+ double kui_event_y(KUIContext* ctx) {
527
+ KUIEvent* ev = peek_event(ctx);
528
+ return ev ? ev->y : 0.0;
529
+ }
530
+
531
+ double kui_event_dx(KUIContext* ctx) {
532
+ KUIEvent* ev = peek_event(ctx);
533
+ return ev ? ev->dx : 0.0;
534
+ }
535
+
536
+ double kui_event_dy(KUIContext* ctx) {
537
+ KUIEvent* ev = peek_event(ctx);
538
+ return ev ? ev->dy : 0.0;
539
+ }
540
+
541
+ int kui_event_button(KUIContext* ctx) {
542
+ KUIEvent* ev = peek_event(ctx);
543
+ return ev ? ev->button : 0;
544
+ }
545
+
546
+ int kui_event_key_code(KUIContext* ctx) {
547
+ KUIEvent* ev = peek_event(ctx);
548
+ return ev ? ev->key_code : -1;
549
+ }
550
+
551
+ int kui_event_modifiers(KUIContext* ctx) {
552
+ KUIEvent* ev = peek_event(ctx);
553
+ return ev ? ev->modifiers : 0;
554
+ }
555
+
556
+ const char* kui_event_text(KUIContext* ctx) {
557
+ KUIEvent* ev = peek_event(ctx);
558
+ return ev ? ev->text : "";
559
+ }
560
+
561
+ int kui_event_ime_sel_start(KUIContext* ctx) {
562
+ KUIEvent* ev = peek_event(ctx);
563
+ return ev ? ev->ime_sel_start : 0;
564
+ }
565
+
566
+ int kui_event_ime_sel_end(KUIContext* ctx) {
567
+ KUIEvent* ev = peek_event(ctx);
568
+ return ev ? ev->ime_sel_end : 0;
569
+ }
570
+
571
+ void kui_consume_event(KUIContext* ctx) {
572
+ if (!ctx || ctx->event_count == 0) return;
573
+ ctx->event_read = (ctx->event_read + 1) % KUI_EVENT_BUFFER_SIZE;
574
+ ctx->event_count--;
575
+ }
576
+
577
+ /* --- Frame management --- */
578
+
579
+ void kui_begin_frame(KUIContext* ctx) {
580
+ if (!ctx || !ctx->gr_context) return;
581
+
582
+ #ifdef __APPLE__
583
+ /* Get Metal drawable and create Skia surface */
584
+ int pw = (int)(ctx->width * ctx->scale);
585
+ int ph = (int)(ctx->height * ctx->scale);
586
+
587
+ ctx->metal_layer.drawableSize = CGSizeMake(pw, ph);
588
+ id<CAMetalDrawable> drawable = [ctx->metal_layer nextDrawable];
589
+ if (!drawable) return;
590
+
591
+ ctx->current_drawable = drawable;
592
+
593
+ GrMtlTextureInfo info;
594
+ info.fTexture.retain((__bridge void*)drawable.texture);
595
+
596
+ GrBackendRenderTarget target(pw, ph, info);
597
+
598
+ SkSurfaceProps props(0, kRGB_H_SkPixelGeometry);
599
+ ctx->surface = SkSurfaces::WrapBackendRenderTarget(
600
+ ctx->gr_context.get(),
601
+ target,
602
+ kTopLeft_GrSurfaceOrigin,
603
+ kBGRA_8888_SkColorType,
604
+ SkColorSpace::MakeSRGB(),
605
+ &props
606
+ );
607
+
608
+ if (ctx->surface) {
609
+ ctx->canvas = ctx->surface->getCanvas();
610
+ ctx->canvas->scale(ctx->scale, ctx->scale);
611
+ }
612
+ #else
613
+ /* Create Skia surface from GL framebuffer */
614
+ int pw, ph;
615
+ SDL_GetWindowSizeInPixels(ctx->window, &pw, &ph);
616
+
617
+ GrGLFramebufferInfo fbi;
618
+ fbi.fFBOID = 0;
619
+ fbi.fFormat = GL_RGBA8;
620
+
621
+ GrBackendRenderTarget target(pw, ph, 0, 8, fbi);
622
+
623
+ SkSurfaceProps props(0, kRGB_H_SkPixelGeometry);
624
+ ctx->surface = SkSurfaces::WrapBackendRenderTarget(
625
+ ctx->gr_context.get(),
626
+ target,
627
+ kBottomLeft_GrSurfaceOrigin,
628
+ kRGBA_8888_SkColorType,
629
+ SkColorSpace::MakeSRGB(),
630
+ &props
631
+ );
632
+
633
+ if (ctx->surface) {
634
+ ctx->canvas = ctx->surface->getCanvas();
635
+ ctx->canvas->scale(ctx->scale, ctx->scale);
636
+ }
637
+ #endif
638
+ }
639
+
640
+ void kui_end_frame(KUIContext* ctx) {
641
+ if (!ctx || !ctx->canvas) return;
642
+
643
+ skgpu::ganesh::FlushAndSubmit(ctx->surface);
644
+
645
+ #ifdef __APPLE__
646
+ /* Present the SAME drawable we got in begin_frame */
647
+ if (ctx->current_drawable) {
648
+ id<MTLCommandBuffer> cmdBuf = [ctx->mtl_queue commandBuffer];
649
+ [cmdBuf presentDrawable:ctx->current_drawable];
650
+ [cmdBuf commit];
651
+ ctx->current_drawable = nil;
652
+ }
653
+ #else
654
+ SDL_GL_SwapWindow(ctx->window);
655
+ #endif
656
+
657
+ ctx->surface.reset();
658
+ ctx->canvas = nullptr;
659
+ // NOTE: Don't clear dirty here — the redraw callback may have set dirty=true
660
+ // again (e.g., for animations). Ruby side manages dirty flag via clear_dirty().
661
+ }
662
+
663
+ /* --- Drawing primitives --- */
664
+
665
+ void kui_clear(KUIContext* ctx, uint32_t color) {
666
+ if (!ctx || !ctx->canvas) return;
667
+ ctx->canvas->clear(uint32_to_skcolor(color));
668
+ }
669
+
670
+ void kui_fill_rect(KUIContext* ctx, double x, double y, double w, double h, uint32_t color) {
671
+ if (!ctx || !ctx->canvas) return;
672
+ SkPaint paint;
673
+ paint.setColor(uint32_to_skcolor(color));
674
+ paint.setAntiAlias(true);
675
+ ctx->canvas->drawRect(SkRect::MakeXYWH(x, y, w, h), paint);
676
+ }
677
+
678
+ void kui_stroke_rect(KUIContext* ctx, double x, double y, double w, double h,
679
+ uint32_t color, double stroke_width) {
680
+ if (!ctx || !ctx->canvas) return;
681
+ SkPaint paint;
682
+ paint.setColor(uint32_to_skcolor(color));
683
+ paint.setStyle(SkPaint::kStroke_Style);
684
+ paint.setStrokeWidth(stroke_width);
685
+ paint.setAntiAlias(true);
686
+ ctx->canvas->drawRect(SkRect::MakeXYWH(x, y, w, h), paint);
687
+ }
688
+
689
+ void kui_fill_round_rect(KUIContext* ctx, double x, double y, double w, double h,
690
+ double r, uint32_t color) {
691
+ if (!ctx || !ctx->canvas) return;
692
+ SkPaint paint;
693
+ paint.setColor(uint32_to_skcolor(color));
694
+ paint.setAntiAlias(true);
695
+ ctx->canvas->drawRRect(
696
+ SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, w, h), r, r),
697
+ paint
698
+ );
699
+ }
700
+
701
+ void kui_stroke_round_rect(KUIContext* ctx, double x, double y, double w, double h,
702
+ double r, uint32_t color, double stroke_width) {
703
+ if (!ctx || !ctx->canvas) return;
704
+ SkPaint paint;
705
+ paint.setColor(uint32_to_skcolor(color));
706
+ paint.setStyle(SkPaint::kStroke_Style);
707
+ paint.setStrokeWidth(stroke_width);
708
+ paint.setAntiAlias(true);
709
+ ctx->canvas->drawRRect(
710
+ SkRRect::MakeRectXY(SkRect::MakeXYWH(x, y, w, h), r, r),
711
+ paint
712
+ );
713
+ }
714
+
715
+ void kui_fill_circle(KUIContext* ctx, double cx, double cy, double r, uint32_t color) {
716
+ if (!ctx || !ctx->canvas) return;
717
+ SkPaint paint;
718
+ paint.setColor(uint32_to_skcolor(color));
719
+ paint.setAntiAlias(true);
720
+ ctx->canvas->drawCircle(cx, cy, r, paint);
721
+ }
722
+
723
+ void kui_stroke_circle(KUIContext* ctx, double cx, double cy, double r,
724
+ uint32_t color, double stroke_width) {
725
+ if (!ctx || !ctx->canvas) return;
726
+ SkPaint paint;
727
+ paint.setColor(uint32_to_skcolor(color));
728
+ paint.setStyle(SkPaint::kStroke_Style);
729
+ paint.setStrokeWidth(stroke_width);
730
+ paint.setAntiAlias(true);
731
+ ctx->canvas->drawCircle(cx, cy, r, paint);
732
+ }
733
+
734
+ void kui_draw_line(KUIContext* ctx, double x1, double y1, double x2, double y2,
735
+ uint32_t color, double width) {
736
+ if (!ctx || !ctx->canvas) return;
737
+ SkPaint paint;
738
+ paint.setColor(uint32_to_skcolor(color));
739
+ paint.setStrokeWidth(width);
740
+ paint.setAntiAlias(true);
741
+ ctx->canvas->drawLine(x1, y1, x2, y2, paint);
742
+ }
743
+
744
+ void kui_fill_arc(KUIContext* ctx, double cx, double cy, double r,
745
+ double start_angle, double sweep_angle, uint32_t color) {
746
+ if (!ctx || !ctx->canvas) return;
747
+ SkPaint paint;
748
+ paint.setColor(uint32_to_skcolor(color));
749
+ paint.setAntiAlias(true);
750
+ SkRect oval = SkRect::MakeXYWH(cx - r, cy - r, r * 2, r * 2);
751
+ SkPath path;
752
+ path.moveTo(cx, cy);
753
+ path.arcTo(oval, start_angle, sweep_angle, false);
754
+ path.close();
755
+ ctx->canvas->drawPath(path, paint);
756
+ }
757
+
758
+ void kui_stroke_arc(KUIContext* ctx, double cx, double cy, double r,
759
+ double start_angle, double sweep_angle,
760
+ uint32_t color, double stroke_width) {
761
+ if (!ctx || !ctx->canvas) return;
762
+ SkPaint paint;
763
+ paint.setColor(uint32_to_skcolor(color));
764
+ paint.setStyle(SkPaint::kStroke_Style);
765
+ paint.setStrokeWidth(stroke_width);
766
+ paint.setAntiAlias(true);
767
+ SkRect oval = SkRect::MakeXYWH(cx - r, cy - r, r * 2, r * 2);
768
+ ctx->canvas->drawArc(oval, start_angle, sweep_angle, false, paint);
769
+ }
770
+
771
+ void kui_fill_triangle(KUIContext* ctx, double x1, double y1, double x2, double y2,
772
+ double x3, double y3, uint32_t color) {
773
+ if (!ctx || !ctx->canvas) return;
774
+ SkPaint paint;
775
+ paint.setColor(uint32_to_skcolor(color));
776
+ paint.setAntiAlias(true);
777
+ SkPath path;
778
+ path.moveTo(x1, y1);
779
+ path.lineTo(x2, y2);
780
+ path.lineTo(x3, y3);
781
+ path.close();
782
+ ctx->canvas->drawPath(path, paint);
783
+ }
784
+
785
+ /* --- Text drawing --- */
786
+
787
+ void kui_draw_text(KUIContext* ctx, const char* text, double x, double y,
788
+ const char* font_family, double font_size, uint32_t color) {
789
+ kui_draw_text_styled(ctx, text, x, y, font_family, font_size, color, 0, 0);
790
+ }
791
+
792
+ void kui_draw_text_styled(KUIContext* ctx, const char* text, double x, double y,
793
+ const char* font_family, double font_size, uint32_t color,
794
+ int weight, int slant) {
795
+ if (!ctx || !ctx->canvas || !text) return;
796
+
797
+ sk_sp<SkTypeface> tf = find_typeface(ctx, font_family, weight, slant);
798
+ SkFont font(tf, font_size);
799
+ font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
800
+ font.setSubpixel(true);
801
+
802
+ SkPaint paint;
803
+ paint.setColor(uint32_to_skcolor(color));
804
+ paint.setAntiAlias(true);
805
+
806
+ auto blob = SkTextBlob::MakeFromString(text, font);
807
+ if (blob) {
808
+ ctx->canvas->drawTextBlob(blob, x, y, paint);
809
+ }
810
+ }
811
+
812
+ /* --- Text measurement --- */
813
+
814
+ double kui_measure_text_width(KUIContext* ctx, const char* text,
815
+ const char* font_family, double font_size) {
816
+ if (!ctx || !text) return 0.0;
817
+
818
+ sk_sp<SkTypeface> tf = find_typeface(ctx, font_family, 0, 0);
819
+ SkFont font(tf, font_size);
820
+ font.setSubpixel(true);
821
+
822
+ return font.measureText(text, strlen(text), SkTextEncoding::kUTF8);
823
+ }
824
+
825
+ double kui_measure_text_height(KUIContext* ctx, const char* font_family, double font_size) {
826
+ if (!ctx) return 0.0;
827
+
828
+ sk_sp<SkTypeface> tf = find_typeface(ctx, font_family, 0, 0);
829
+ SkFont font(tf, font_size);
830
+
831
+ SkFontMetrics metrics;
832
+ font.getMetrics(&metrics);
833
+ return metrics.fDescent - metrics.fAscent + metrics.fLeading;
834
+ }
835
+
836
+ double kui_get_text_ascent(KUIContext* ctx, const char* font_family, double font_size) {
837
+ if (!ctx) return 0.0;
838
+
839
+ sk_sp<SkTypeface> tf = find_typeface(ctx, font_family, 0, 0);
840
+ SkFont font(tf, font_size);
841
+
842
+ SkFontMetrics metrics;
843
+ font.getMetrics(&metrics);
844
+ return -metrics.fAscent; /* fAscent is negative, we return positive */
845
+ }
846
+
847
+ /* --- Path drawing --- */
848
+
849
+ void kui_begin_path(KUIContext* ctx) {
850
+ if (!ctx) return;
851
+ ctx->current_path.reset();
852
+ }
853
+
854
+ void kui_path_move_to(KUIContext* ctx, double x, double y) {
855
+ if (!ctx) return;
856
+ ctx->current_path.moveTo(x, y);
857
+ }
858
+
859
+ void kui_path_line_to(KUIContext* ctx, double x, double y) {
860
+ if (!ctx) return;
861
+ ctx->current_path.lineTo(x, y);
862
+ }
863
+
864
+ void kui_close_fill_path(KUIContext* ctx, uint32_t color) {
865
+ if (!ctx || !ctx->canvas) return;
866
+ ctx->current_path.close();
867
+ SkPaint paint;
868
+ paint.setColor(uint32_to_skcolor(color));
869
+ paint.setAntiAlias(true);
870
+ ctx->canvas->drawPath(ctx->current_path, paint);
871
+ }
872
+
873
+ void kui_fill_path(KUIContext* ctx, uint32_t color) {
874
+ if (!ctx || !ctx->canvas) return;
875
+ SkPaint paint;
876
+ paint.setColor(uint32_to_skcolor(color));
877
+ paint.setAntiAlias(true);
878
+ ctx->canvas->drawPath(ctx->current_path, paint);
879
+ }
880
+
881
+ /* --- Canvas state --- */
882
+
883
+ void kui_save(KUIContext* ctx) {
884
+ if (!ctx || !ctx->canvas) return;
885
+ ctx->canvas->save();
886
+ }
887
+
888
+ void kui_restore(KUIContext* ctx) {
889
+ if (!ctx || !ctx->canvas) return;
890
+ ctx->canvas->restore();
891
+ }
892
+
893
+ void kui_translate(KUIContext* ctx, double dx, double dy) {
894
+ if (!ctx || !ctx->canvas) return;
895
+ ctx->canvas->translate(dx, dy);
896
+ }
897
+
898
+ void kui_clip_rect(KUIContext* ctx, double x, double y, double w, double h) {
899
+ if (!ctx || !ctx->canvas) return;
900
+ ctx->canvas->clipRect(SkRect::MakeXYWH(x, y, w, h));
901
+ }
902
+
903
+ /* --- Image operations --- */
904
+
905
+ int kui_load_image(KUIContext* ctx, const char* path) {
906
+ if (!ctx || !path) return 0;
907
+
908
+ auto data = SkData::MakeFromFileName(path);
909
+ if (!data) return 0;
910
+
911
+ /* Use deprecated MakeFromData (still available in m124) */
912
+ auto codec = SkCodec::MakeFromData(data);
913
+ if (!codec) return 0;
914
+
915
+ SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
916
+ SkBitmap bitmap;
917
+ bitmap.allocPixels(info);
918
+ if (codec->getPixels(info, bitmap.getPixels(), bitmap.rowBytes()) != SkCodec::kSuccess) {
919
+ return 0;
920
+ }
921
+
922
+ auto image = bitmap.asImage();
923
+ if (!image) return 0;
924
+
925
+ int id = ctx->next_image_id++;
926
+ ctx->images[id] = image;
927
+ return id;
928
+ }
929
+
930
+ int kui_load_net_image(KUIContext* ctx, const char* url) {
931
+ /* TODO: implement URL image loading (curl + Skia decode) */
932
+ (void)ctx; (void)url;
933
+ return 0;
934
+ }
935
+
936
+ void kui_draw_image(KUIContext* ctx, int image_id, double x, double y, double w, double h) {
937
+ if (!ctx || !ctx->canvas) return;
938
+ auto it = ctx->images.find(image_id);
939
+ if (it == ctx->images.end()) return;
940
+
941
+ SkRect dst = SkRect::MakeXYWH(x, y, w, h);
942
+ ctx->canvas->drawImageRect(it->second.get(), dst, SkSamplingOptions());
943
+ }
944
+
945
+ double kui_get_image_width(KUIContext* ctx, int image_id) {
946
+ if (!ctx) return 0.0;
947
+ auto it = ctx->images.find(image_id);
948
+ return (it != ctx->images.end()) ? (double)it->second->width() : 0.0;
949
+ }
950
+
951
+ double kui_get_image_height(KUIContext* ctx, int image_id) {
952
+ if (!ctx) return 0.0;
953
+ auto it = ctx->images.find(image_id);
954
+ return (it != ctx->images.end()) ? (double)it->second->height() : 0.0;
955
+ }
956
+
957
+ /* --- Color utilities --- */
958
+
959
+ uint32_t kui_interpolate_color(uint32_t c1, uint32_t c2, double t) {
960
+ if (t <= 0.0) return c1;
961
+ if (t >= 1.0) return c2;
962
+ int a1 = (c1 >> 24) & 0xFF, r1 = (c1 >> 16) & 0xFF, g1 = (c1 >> 8) & 0xFF, b1 = c1 & 0xFF;
963
+ int a2 = (c2 >> 24) & 0xFF, r2 = (c2 >> 16) & 0xFF, g2 = (c2 >> 8) & 0xFF, b2 = c2 & 0xFF;
964
+ int a = (int)(a1 + (a2 - a1) * t);
965
+ int r = (int)(r1 + (r2 - r1) * t);
966
+ int g = (int)(g1 + (g2 - g1) * t);
967
+ int b = (int)(b1 + (b2 - b1) * t);
968
+ return ((uint32_t)clamp_u8(a) << 24) | ((uint32_t)clamp_u8(r) << 16) |
969
+ ((uint32_t)clamp_u8(g) << 8) | (uint32_t)clamp_u8(b);
970
+ }
971
+
972
+ uint32_t kui_with_alpha(uint32_t color, int alpha) {
973
+ return (color & 0x00FFFFFF) | ((uint32_t)clamp_u8(alpha) << 24);
974
+ }
975
+
976
+ uint32_t kui_lighten_color(uint32_t color, double amount) {
977
+ int r = (color >> 16) & 0xFF;
978
+ int g = (color >> 8) & 0xFF;
979
+ int b = color & 0xFF;
980
+ int a = (color >> 24) & 0xFF;
981
+ r = (int)(r + (255 - r) * amount);
982
+ g = (int)(g + (255 - g) * amount);
983
+ b = (int)(b + (255 - b) * amount);
984
+ return ((uint32_t)a << 24) | ((uint32_t)clamp_u8(r) << 16) |
985
+ ((uint32_t)clamp_u8(g) << 8) | (uint32_t)clamp_u8(b);
986
+ }
987
+
988
+ uint32_t kui_darken_color(uint32_t color, double amount) {
989
+ int r = (color >> 16) & 0xFF;
990
+ int g = (color >> 8) & 0xFF;
991
+ int b = color & 0xFF;
992
+ int a = (color >> 24) & 0xFF;
993
+ r = (int)(r * (1.0 - amount));
994
+ g = (int)(g * (1.0 - amount));
995
+ b = (int)(b * (1.0 - amount));
996
+ return ((uint32_t)a << 24) | ((uint32_t)clamp_u8(r) << 16) |
997
+ ((uint32_t)clamp_u8(g) << 8) | (uint32_t)clamp_u8(b);
998
+ }
999
+
1000
+ /* --- Window queries --- */
1001
+
1002
+ double kui_get_width(KUIContext* ctx) {
1003
+ return ctx ? (double)ctx->width : 0.0;
1004
+ }
1005
+
1006
+ double kui_get_height(KUIContext* ctx) {
1007
+ return ctx ? (double)ctx->height : 0.0;
1008
+ }
1009
+
1010
+ double kui_get_scale(KUIContext* ctx) {
1011
+ return ctx ? (double)ctx->scale : 1.0;
1012
+ }
1013
+
1014
+ bool kui_is_dark_mode(KUIContext* ctx) {
1015
+ (void)ctx;
1016
+ /* SDL3 doesn't have a direct dark mode query.
1017
+ * For now, return false (light mode default). */
1018
+ return false;
1019
+ }
1020
+
1021
+ void kui_request_frame(KUIContext* ctx) {
1022
+ if (ctx) ctx->frame_requested = true;
1023
+ }
1024
+
1025
+ void kui_mark_dirty(KUIContext* ctx) {
1026
+ if (ctx) ctx->dirty = true;
1027
+ }
1028
+
1029
+ bool kui_needs_redraw(KUIContext* ctx) {
1030
+ if (!ctx) return false;
1031
+ return ctx->dirty || ctx->frame_requested;
1032
+ }
1033
+
1034
+ void kui_clear_frame_requested(KUIContext* ctx) {
1035
+ if (ctx) ctx->frame_requested = false;
1036
+ }
1037
+
1038
+ void kui_clear_dirty(KUIContext* ctx) {
1039
+ if (ctx) ctx->dirty = false;
1040
+ }
1041
+
1042
+ /* --- IME / Text Input --- */
1043
+
1044
+ void kui_set_text_input_enabled(KUIContext* ctx, bool enabled) {
1045
+ if (!ctx) return;
1046
+ if (enabled && !ctx->text_input_enabled) {
1047
+ SDL_StartTextInput(ctx->window);
1048
+ ctx->text_input_enabled = true;
1049
+ } else if (!enabled && ctx->text_input_enabled) {
1050
+ SDL_StopTextInput(ctx->window);
1051
+ ctx->text_input_enabled = false;
1052
+ }
1053
+ }
1054
+
1055
+ void kui_set_text_input_rect(KUIContext* ctx, int x, int y, int w, int h) {
1056
+ if (!ctx) return;
1057
+ SDL_Rect rect = { x, y, w, h };
1058
+ SDL_SetTextInputArea(ctx->window, &rect, 0);
1059
+ }
1060
+
1061
+ /* --- Clipboard --- */
1062
+
1063
+ const char* kui_get_clipboard_text(KUIContext* ctx) {
1064
+ if (!ctx) return "";
1065
+ char* text = SDL_GetClipboardText();
1066
+ if (text) {
1067
+ ctx->clipboard_cache = text;
1068
+ SDL_free(text);
1069
+ return ctx->clipboard_cache.c_str();
1070
+ }
1071
+ return "";
1072
+ }
1073
+
1074
+ void kui_set_clipboard_text(KUIContext* ctx, const char* text) {
1075
+ (void)ctx;
1076
+ if (text) {
1077
+ SDL_SetClipboardText(text);
1078
+ }
1079
+ }
1080
+
1081
+ /* --- Utilities --- */
1082
+
1083
+ int64_t kui_current_time_millis(void) {
1084
+ auto now = std::chrono::system_clock::now();
1085
+ auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
1086
+ return ms.count();
1087
+ }
1088
+
1089
+ const char* kui_number_to_string(double value) {
1090
+ /* Thread-unsafe, but matches KUIRuntime.java behavior */
1091
+ static char buf[64];
1092
+ snprintf(buf, sizeof(buf), "%.10g", value);
1093
+ return buf;
1094
+ }
1095
+
1096
+ /* --- Math helpers --- */
1097
+
1098
+ double kui_math_cos(double radians) { return cos(radians); }
1099
+ double kui_math_sin(double radians) { return sin(radians); }
1100
+ double kui_math_sqrt(double value) { return sqrt(value); }
1101
+ double kui_math_atan2(double y, double x) { return atan2(y, x); }
1102
+ double kui_math_abs(double value) { return fabs(value); }
1103
+
1104
+ } /* extern "C" */
1105
+
1106
+
1107
+ /* ============================================================
1108
+ * CRuby Extension Wrapper
1109
+ * ============================================================ */
1110
+
1111
+ #include <ruby.h>
1112
+
1113
+ static VALUE mKonpeitoUI;
1114
+
1115
+ /* Helper: extract KUIContext* from Ruby Integer (pointer as Fixnum) */
1116
+ static KUIContext* get_ctx(VALUE handle) {
1117
+ return (KUIContext*)(uintptr_t)NUM2ULL(handle);
1118
+ }
1119
+
1120
+ /* --- Ruby wrapper functions --- */
1121
+
1122
+ static VALUE rb_kui_create_window(VALUE self, VALUE title, VALUE width, VALUE height) {
1123
+ Check_Type(title, T_STRING);
1124
+ KUIContext* ctx = kui_create_window(RSTRING_PTR(title), NUM2INT(width), NUM2INT(height));
1125
+ if (!ctx) {
1126
+ rb_raise(rb_eRuntimeError, "Failed to create window");
1127
+ return Qnil;
1128
+ }
1129
+ return ULL2NUM((uintptr_t)ctx);
1130
+ }
1131
+
1132
+ static VALUE rb_kui_destroy_window(VALUE self, VALUE handle) {
1133
+ kui_destroy_window(get_ctx(handle));
1134
+ return Qnil;
1135
+ }
1136
+
1137
+ static VALUE rb_kui_step(VALUE self, VALUE handle) {
1138
+ kui_step(get_ctx(handle));
1139
+ return Qnil;
1140
+ }
1141
+
1142
+ /* Event access */
1143
+ static VALUE rb_kui_has_event(VALUE self, VALUE handle) {
1144
+ return kui_has_event(get_ctx(handle)) ? Qtrue : Qfalse;
1145
+ }
1146
+
1147
+ static VALUE rb_kui_event_type(VALUE self, VALUE handle) {
1148
+ return INT2NUM(kui_event_type(get_ctx(handle)));
1149
+ }
1150
+
1151
+ static VALUE rb_kui_event_x(VALUE self, VALUE handle) {
1152
+ return DBL2NUM(kui_event_x(get_ctx(handle)));
1153
+ }
1154
+
1155
+ static VALUE rb_kui_event_y(VALUE self, VALUE handle) {
1156
+ return DBL2NUM(kui_event_y(get_ctx(handle)));
1157
+ }
1158
+
1159
+ static VALUE rb_kui_event_dx(VALUE self, VALUE handle) {
1160
+ return DBL2NUM(kui_event_dx(get_ctx(handle)));
1161
+ }
1162
+
1163
+ static VALUE rb_kui_event_dy(VALUE self, VALUE handle) {
1164
+ return DBL2NUM(kui_event_dy(get_ctx(handle)));
1165
+ }
1166
+
1167
+ static VALUE rb_kui_event_button(VALUE self, VALUE handle) {
1168
+ return INT2NUM(kui_event_button(get_ctx(handle)));
1169
+ }
1170
+
1171
+ static VALUE rb_kui_event_key_code(VALUE self, VALUE handle) {
1172
+ return INT2NUM(kui_event_key_code(get_ctx(handle)));
1173
+ }
1174
+
1175
+ static VALUE rb_kui_event_modifiers(VALUE self, VALUE handle) {
1176
+ return INT2NUM(kui_event_modifiers(get_ctx(handle)));
1177
+ }
1178
+
1179
+ static VALUE rb_kui_event_text(VALUE self, VALUE handle) {
1180
+ const char* text = kui_event_text(get_ctx(handle));
1181
+ return rb_utf8_str_new_cstr(text);
1182
+ }
1183
+
1184
+ static VALUE rb_kui_event_ime_sel_start(VALUE self, VALUE handle) {
1185
+ return INT2NUM(kui_event_ime_sel_start(get_ctx(handle)));
1186
+ }
1187
+
1188
+ static VALUE rb_kui_event_ime_sel_end(VALUE self, VALUE handle) {
1189
+ return INT2NUM(kui_event_ime_sel_end(get_ctx(handle)));
1190
+ }
1191
+
1192
+ static VALUE rb_kui_consume_event(VALUE self, VALUE handle) {
1193
+ kui_consume_event(get_ctx(handle));
1194
+ return Qnil;
1195
+ }
1196
+
1197
+ /* Frame management */
1198
+ static VALUE rb_kui_begin_frame(VALUE self, VALUE handle) {
1199
+ kui_begin_frame(get_ctx(handle));
1200
+ return Qnil;
1201
+ }
1202
+
1203
+ static VALUE rb_kui_end_frame(VALUE self, VALUE handle) {
1204
+ kui_end_frame(get_ctx(handle));
1205
+ return Qnil;
1206
+ }
1207
+
1208
+ /* Drawing */
1209
+ static VALUE rb_kui_clear(VALUE self, VALUE handle, VALUE color) {
1210
+ kui_clear(get_ctx(handle), (uint32_t)NUM2ULL(color));
1211
+ return Qnil;
1212
+ }
1213
+
1214
+ static VALUE rb_kui_fill_rect(VALUE self, VALUE h, VALUE x, VALUE y, VALUE w, VALUE ht, VALUE color) {
1215
+ kui_fill_rect(get_ctx(h), NUM2DBL(x), NUM2DBL(y), NUM2DBL(w), NUM2DBL(ht), (uint32_t)NUM2ULL(color));
1216
+ return Qnil;
1217
+ }
1218
+
1219
+ static VALUE rb_kui_stroke_rect(int argc, VALUE* argv, VALUE self) {
1220
+ if (argc != 7) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 7)", argc);
1221
+ kui_stroke_rect(get_ctx(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
1222
+ NUM2DBL(argv[3]), NUM2DBL(argv[4]),
1223
+ (uint32_t)NUM2ULL(argv[5]), NUM2DBL(argv[6]));
1224
+ return Qnil;
1225
+ }
1226
+
1227
+ static VALUE rb_kui_fill_round_rect(int argc, VALUE* argv, VALUE self) {
1228
+ if (argc != 7) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 7)", argc);
1229
+ kui_fill_round_rect(get_ctx(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
1230
+ NUM2DBL(argv[3]), NUM2DBL(argv[4]),
1231
+ NUM2DBL(argv[5]), (uint32_t)NUM2ULL(argv[6]));
1232
+ return Qnil;
1233
+ }
1234
+
1235
+ static VALUE rb_kui_stroke_round_rect(int argc, VALUE* argv, VALUE self) {
1236
+ if (argc != 8) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 8)", argc);
1237
+ kui_stroke_round_rect(get_ctx(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
1238
+ NUM2DBL(argv[3]), NUM2DBL(argv[4]),
1239
+ NUM2DBL(argv[5]), (uint32_t)NUM2ULL(argv[6]), NUM2DBL(argv[7]));
1240
+ return Qnil;
1241
+ }
1242
+
1243
+ static VALUE rb_kui_fill_circle(VALUE self, VALUE h, VALUE cx, VALUE cy, VALUE r, VALUE color) {
1244
+ kui_fill_circle(get_ctx(h), NUM2DBL(cx), NUM2DBL(cy), NUM2DBL(r), (uint32_t)NUM2ULL(color));
1245
+ return Qnil;
1246
+ }
1247
+
1248
+ static VALUE rb_kui_stroke_circle(int argc, VALUE* argv, VALUE self) {
1249
+ if (argc != 6) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 6)", argc);
1250
+ kui_stroke_circle(get_ctx(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
1251
+ NUM2DBL(argv[3]), (uint32_t)NUM2ULL(argv[4]), NUM2DBL(argv[5]));
1252
+ return Qnil;
1253
+ }
1254
+
1255
+ static VALUE rb_kui_draw_line(int argc, VALUE* argv, VALUE self) {
1256
+ if (argc != 7) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 7)", argc);
1257
+ kui_draw_line(get_ctx(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
1258
+ NUM2DBL(argv[3]), NUM2DBL(argv[4]),
1259
+ (uint32_t)NUM2ULL(argv[5]), NUM2DBL(argv[6]));
1260
+ return Qnil;
1261
+ }
1262
+
1263
+ static VALUE rb_kui_fill_arc(int argc, VALUE* argv, VALUE self) {
1264
+ if (argc != 7) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 7)", argc);
1265
+ kui_fill_arc(get_ctx(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
1266
+ NUM2DBL(argv[3]), NUM2DBL(argv[4]),
1267
+ NUM2DBL(argv[5]), (uint32_t)NUM2ULL(argv[6]));
1268
+ return Qnil;
1269
+ }
1270
+
1271
+ static VALUE rb_kui_stroke_arc(int argc, VALUE* argv, VALUE self) {
1272
+ if (argc != 8) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 8)", argc);
1273
+ kui_stroke_arc(get_ctx(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
1274
+ NUM2DBL(argv[3]), NUM2DBL(argv[4]),
1275
+ NUM2DBL(argv[5]), (uint32_t)NUM2ULL(argv[6]), NUM2DBL(argv[7]));
1276
+ return Qnil;
1277
+ }
1278
+
1279
+ static VALUE rb_kui_fill_triangle(int argc, VALUE* argv, VALUE self) {
1280
+ if (argc != 8) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 8)", argc);
1281
+ kui_fill_triangle(get_ctx(argv[0]), NUM2DBL(argv[1]), NUM2DBL(argv[2]),
1282
+ NUM2DBL(argv[3]), NUM2DBL(argv[4]),
1283
+ NUM2DBL(argv[5]), NUM2DBL(argv[6]),
1284
+ (uint32_t)NUM2ULL(argv[7]));
1285
+ return Qnil;
1286
+ }
1287
+
1288
+ /* Text */
1289
+ static VALUE rb_kui_draw_text(int argc, VALUE* argv, VALUE self) {
1290
+ if (argc != 7) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 7)", argc);
1291
+ Check_Type(argv[1], T_STRING);
1292
+ Check_Type(argv[4], T_STRING);
1293
+ kui_draw_text(get_ctx(argv[0]), RSTRING_PTR(argv[1]),
1294
+ NUM2DBL(argv[2]), NUM2DBL(argv[3]),
1295
+ RSTRING_PTR(argv[4]), NUM2DBL(argv[5]),
1296
+ (uint32_t)NUM2ULL(argv[6]));
1297
+ return Qnil;
1298
+ }
1299
+
1300
+ static VALUE rb_kui_draw_text_styled(int argc, VALUE* argv, VALUE self) {
1301
+ if (argc != 9) rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 9)", argc);
1302
+ Check_Type(argv[1], T_STRING);
1303
+ Check_Type(argv[4], T_STRING);
1304
+ kui_draw_text_styled(get_ctx(argv[0]), RSTRING_PTR(argv[1]),
1305
+ NUM2DBL(argv[2]), NUM2DBL(argv[3]),
1306
+ RSTRING_PTR(argv[4]), NUM2DBL(argv[5]),
1307
+ (uint32_t)NUM2ULL(argv[6]),
1308
+ NUM2INT(argv[7]), NUM2INT(argv[8]));
1309
+ return Qnil;
1310
+ }
1311
+
1312
+ /* Text measurement */
1313
+ static VALUE rb_kui_measure_text_width(VALUE self, VALUE handle, VALUE text, VALUE font_family, VALUE font_size) {
1314
+ Check_Type(text, T_STRING);
1315
+ Check_Type(font_family, T_STRING);
1316
+ return DBL2NUM(kui_measure_text_width(get_ctx(handle), RSTRING_PTR(text),
1317
+ RSTRING_PTR(font_family), NUM2DBL(font_size)));
1318
+ }
1319
+
1320
+ static VALUE rb_kui_measure_text_height(VALUE self, VALUE handle, VALUE font_family, VALUE font_size) {
1321
+ Check_Type(font_family, T_STRING);
1322
+ return DBL2NUM(kui_measure_text_height(get_ctx(handle), RSTRING_PTR(font_family), NUM2DBL(font_size)));
1323
+ }
1324
+
1325
+ static VALUE rb_kui_get_text_ascent(VALUE self, VALUE handle, VALUE font_family, VALUE font_size) {
1326
+ Check_Type(font_family, T_STRING);
1327
+ return DBL2NUM(kui_get_text_ascent(get_ctx(handle), RSTRING_PTR(font_family), NUM2DBL(font_size)));
1328
+ }
1329
+
1330
+ /* Path drawing */
1331
+ static VALUE rb_kui_begin_path(VALUE self, VALUE handle) {
1332
+ kui_begin_path(get_ctx(handle));
1333
+ return Qnil;
1334
+ }
1335
+
1336
+ static VALUE rb_kui_path_move_to(VALUE self, VALUE handle, VALUE x, VALUE y) {
1337
+ kui_path_move_to(get_ctx(handle), NUM2DBL(x), NUM2DBL(y));
1338
+ return Qnil;
1339
+ }
1340
+
1341
+ static VALUE rb_kui_path_line_to(VALUE self, VALUE handle, VALUE x, VALUE y) {
1342
+ kui_path_line_to(get_ctx(handle), NUM2DBL(x), NUM2DBL(y));
1343
+ return Qnil;
1344
+ }
1345
+
1346
+ static VALUE rb_kui_close_fill_path(VALUE self, VALUE handle, VALUE color) {
1347
+ kui_close_fill_path(get_ctx(handle), (uint32_t)NUM2ULL(color));
1348
+ return Qnil;
1349
+ }
1350
+
1351
+ static VALUE rb_kui_fill_path_fn(VALUE self, VALUE handle, VALUE color) {
1352
+ kui_fill_path(get_ctx(handle), (uint32_t)NUM2ULL(color));
1353
+ return Qnil;
1354
+ }
1355
+
1356
+ /* Canvas state */
1357
+ static VALUE rb_kui_save(VALUE self, VALUE handle) {
1358
+ kui_save(get_ctx(handle));
1359
+ return Qnil;
1360
+ }
1361
+
1362
+ static VALUE rb_kui_restore(VALUE self, VALUE handle) {
1363
+ kui_restore(get_ctx(handle));
1364
+ return Qnil;
1365
+ }
1366
+
1367
+ static VALUE rb_kui_translate(VALUE self, VALUE handle, VALUE dx, VALUE dy) {
1368
+ kui_translate(get_ctx(handle), NUM2DBL(dx), NUM2DBL(dy));
1369
+ return Qnil;
1370
+ }
1371
+
1372
+ static VALUE rb_kui_clip_rect(VALUE self, VALUE handle, VALUE x, VALUE y, VALUE w, VALUE h) {
1373
+ kui_clip_rect(get_ctx(handle), NUM2DBL(x), NUM2DBL(y), NUM2DBL(w), NUM2DBL(h));
1374
+ return Qnil;
1375
+ }
1376
+
1377
+ /* Image operations */
1378
+ static VALUE rb_kui_load_image(VALUE self, VALUE handle, VALUE path) {
1379
+ Check_Type(path, T_STRING);
1380
+ return INT2NUM(kui_load_image(get_ctx(handle), RSTRING_PTR(path)));
1381
+ }
1382
+
1383
+ static VALUE rb_kui_load_net_image(VALUE self, VALUE handle, VALUE url) {
1384
+ Check_Type(url, T_STRING);
1385
+ return INT2NUM(kui_load_net_image(get_ctx(handle), RSTRING_PTR(url)));
1386
+ }
1387
+
1388
+ static VALUE rb_kui_draw_image(VALUE self, VALUE handle, VALUE image_id, VALUE x, VALUE y, VALUE w, VALUE h) {
1389
+ kui_draw_image(get_ctx(handle), NUM2INT(image_id), NUM2DBL(x), NUM2DBL(y), NUM2DBL(w), NUM2DBL(h));
1390
+ return Qnil;
1391
+ }
1392
+
1393
+ static VALUE rb_kui_get_image_width(VALUE self, VALUE handle, VALUE image_id) {
1394
+ return DBL2NUM(kui_get_image_width(get_ctx(handle), NUM2INT(image_id)));
1395
+ }
1396
+
1397
+ static VALUE rb_kui_get_image_height(VALUE self, VALUE handle, VALUE image_id) {
1398
+ return DBL2NUM(kui_get_image_height(get_ctx(handle), NUM2INT(image_id)));
1399
+ }
1400
+
1401
+ /* Color utilities */
1402
+ static VALUE rb_kui_interpolate_color(VALUE self, VALUE c1, VALUE c2, VALUE t) {
1403
+ return ULL2NUM(kui_interpolate_color((uint32_t)NUM2ULL(c1), (uint32_t)NUM2ULL(c2), NUM2DBL(t)));
1404
+ }
1405
+
1406
+ static VALUE rb_kui_with_alpha(VALUE self, VALUE color, VALUE alpha) {
1407
+ return ULL2NUM(kui_with_alpha((uint32_t)NUM2ULL(color), NUM2INT(alpha)));
1408
+ }
1409
+
1410
+ static VALUE rb_kui_lighten_color(VALUE self, VALUE color, VALUE amount) {
1411
+ return ULL2NUM(kui_lighten_color((uint32_t)NUM2ULL(color), NUM2DBL(amount)));
1412
+ }
1413
+
1414
+ static VALUE rb_kui_darken_color(VALUE self, VALUE color, VALUE amount) {
1415
+ return ULL2NUM(kui_darken_color((uint32_t)NUM2ULL(color), NUM2DBL(amount)));
1416
+ }
1417
+
1418
+ /* Window queries */
1419
+ static VALUE rb_kui_get_width(VALUE self, VALUE handle) {
1420
+ return DBL2NUM(kui_get_width(get_ctx(handle)));
1421
+ }
1422
+
1423
+ static VALUE rb_kui_get_height(VALUE self, VALUE handle) {
1424
+ return DBL2NUM(kui_get_height(get_ctx(handle)));
1425
+ }
1426
+
1427
+ static VALUE rb_kui_get_scale(VALUE self, VALUE handle) {
1428
+ return DBL2NUM(kui_get_scale(get_ctx(handle)));
1429
+ }
1430
+
1431
+ static VALUE rb_kui_is_dark_mode(VALUE self, VALUE handle) {
1432
+ return kui_is_dark_mode(get_ctx(handle)) ? Qtrue : Qfalse;
1433
+ }
1434
+
1435
+ static VALUE rb_kui_request_frame(VALUE self, VALUE handle) {
1436
+ kui_request_frame(get_ctx(handle));
1437
+ return Qnil;
1438
+ }
1439
+
1440
+ static VALUE rb_kui_mark_dirty(VALUE self, VALUE handle) {
1441
+ kui_mark_dirty(get_ctx(handle));
1442
+ return Qnil;
1443
+ }
1444
+
1445
+ static VALUE rb_kui_needs_redraw(VALUE self, VALUE handle) {
1446
+ return kui_needs_redraw(get_ctx(handle)) ? Qtrue : Qfalse;
1447
+ }
1448
+
1449
+ static VALUE rb_kui_clear_frame_requested(VALUE self, VALUE handle) {
1450
+ kui_clear_frame_requested(get_ctx(handle));
1451
+ return Qnil;
1452
+ }
1453
+
1454
+ static VALUE rb_kui_clear_dirty(VALUE self, VALUE handle) {
1455
+ kui_clear_dirty(get_ctx(handle));
1456
+ return Qnil;
1457
+ }
1458
+
1459
+ /* IME / Text Input */
1460
+ static VALUE rb_kui_set_text_input_enabled(VALUE self, VALUE handle, VALUE enabled) {
1461
+ kui_set_text_input_enabled(get_ctx(handle), RTEST(enabled));
1462
+ return Qnil;
1463
+ }
1464
+
1465
+ static VALUE rb_kui_set_text_input_rect(VALUE self, VALUE handle, VALUE x, VALUE y, VALUE w, VALUE h) {
1466
+ kui_set_text_input_rect(get_ctx(handle), NUM2INT(x), NUM2INT(y), NUM2INT(w), NUM2INT(h));
1467
+ return Qnil;
1468
+ }
1469
+
1470
+ /* Clipboard */
1471
+ static VALUE rb_kui_get_clipboard_text(VALUE self, VALUE handle) {
1472
+ const char* text = kui_get_clipboard_text(get_ctx(handle));
1473
+ return rb_utf8_str_new_cstr(text);
1474
+ }
1475
+
1476
+ static VALUE rb_kui_set_clipboard_text(VALUE self, VALUE handle, VALUE text) {
1477
+ Check_Type(text, T_STRING);
1478
+ kui_set_clipboard_text(get_ctx(handle), RSTRING_PTR(text));
1479
+ return Qnil;
1480
+ }
1481
+
1482
+ /* Utilities */
1483
+ static VALUE rb_kui_current_time_millis(VALUE self) {
1484
+ return LL2NUM(kui_current_time_millis());
1485
+ }
1486
+
1487
+ static VALUE rb_kui_number_to_string(VALUE self, VALUE value) {
1488
+ const char* s = kui_number_to_string(NUM2DBL(value));
1489
+ return rb_utf8_str_new_cstr(s);
1490
+ }
1491
+
1492
+ /* Math helpers */
1493
+ static VALUE rb_kui_math_cos(VALUE self, VALUE radians) { return DBL2NUM(kui_math_cos(NUM2DBL(radians))); }
1494
+ static VALUE rb_kui_math_sin(VALUE self, VALUE radians) { return DBL2NUM(kui_math_sin(NUM2DBL(radians))); }
1495
+ static VALUE rb_kui_math_sqrt(VALUE self, VALUE value) { return DBL2NUM(kui_math_sqrt(NUM2DBL(value))); }
1496
+ static VALUE rb_kui_math_atan2(VALUE self, VALUE y, VALUE x) { return DBL2NUM(kui_math_atan2(NUM2DBL(y), NUM2DBL(x))); }
1497
+ static VALUE rb_kui_math_abs(VALUE self, VALUE value) { return DBL2NUM(kui_math_abs(NUM2DBL(value))); }
1498
+
1499
+
1500
+ /* ============================================================
1501
+ * Module initialization
1502
+ * ============================================================ */
1503
+
1504
+ extern "C" void Init_konpeito_ui(void) {
1505
+ mKonpeitoUI = rb_define_module("KonpeitoUI");
1506
+
1507
+ /* Window management */
1508
+ rb_define_module_function(mKonpeitoUI, "create_window", (VALUE(*)(ANYARGS))rb_kui_create_window, 3);
1509
+ rb_define_module_function(mKonpeitoUI, "destroy_window", (VALUE(*)(ANYARGS))rb_kui_destroy_window, 1);
1510
+ rb_define_module_function(mKonpeitoUI, "step", (VALUE(*)(ANYARGS))rb_kui_step, 1);
1511
+
1512
+ /* Event access */
1513
+ rb_define_module_function(mKonpeitoUI, "has_event", (VALUE(*)(ANYARGS))rb_kui_has_event, 1);
1514
+ rb_define_module_function(mKonpeitoUI, "event_type", (VALUE(*)(ANYARGS))rb_kui_event_type, 1);
1515
+ rb_define_module_function(mKonpeitoUI, "event_x", (VALUE(*)(ANYARGS))rb_kui_event_x, 1);
1516
+ rb_define_module_function(mKonpeitoUI, "event_y", (VALUE(*)(ANYARGS))rb_kui_event_y, 1);
1517
+ rb_define_module_function(mKonpeitoUI, "event_dx", (VALUE(*)(ANYARGS))rb_kui_event_dx, 1);
1518
+ rb_define_module_function(mKonpeitoUI, "event_dy", (VALUE(*)(ANYARGS))rb_kui_event_dy, 1);
1519
+ rb_define_module_function(mKonpeitoUI, "event_button", (VALUE(*)(ANYARGS))rb_kui_event_button, 1);
1520
+ rb_define_module_function(mKonpeitoUI, "event_key_code", (VALUE(*)(ANYARGS))rb_kui_event_key_code, 1);
1521
+ rb_define_module_function(mKonpeitoUI, "event_modifiers", (VALUE(*)(ANYARGS))rb_kui_event_modifiers, 1);
1522
+ rb_define_module_function(mKonpeitoUI, "event_text", (VALUE(*)(ANYARGS))rb_kui_event_text, 1);
1523
+ rb_define_module_function(mKonpeitoUI, "event_ime_sel_start", (VALUE(*)(ANYARGS))rb_kui_event_ime_sel_start, 1);
1524
+ rb_define_module_function(mKonpeitoUI, "event_ime_sel_end", (VALUE(*)(ANYARGS))rb_kui_event_ime_sel_end, 1);
1525
+ rb_define_module_function(mKonpeitoUI, "consume_event", (VALUE(*)(ANYARGS))rb_kui_consume_event, 1);
1526
+
1527
+ /* Frame management */
1528
+ rb_define_module_function(mKonpeitoUI, "begin_frame", (VALUE(*)(ANYARGS))rb_kui_begin_frame, 1);
1529
+ rb_define_module_function(mKonpeitoUI, "end_frame", (VALUE(*)(ANYARGS))rb_kui_end_frame, 1);
1530
+
1531
+ /* Drawing primitives */
1532
+ rb_define_module_function(mKonpeitoUI, "clear", (VALUE(*)(ANYARGS))rb_kui_clear, 2);
1533
+ rb_define_module_function(mKonpeitoUI, "fill_rect", (VALUE(*)(ANYARGS))rb_kui_fill_rect, 6);
1534
+ rb_define_module_function(mKonpeitoUI, "stroke_rect", (VALUE(*)(ANYARGS))rb_kui_stroke_rect, -1);
1535
+ rb_define_module_function(mKonpeitoUI, "fill_round_rect", (VALUE(*)(ANYARGS))rb_kui_fill_round_rect, -1);
1536
+ rb_define_module_function(mKonpeitoUI, "stroke_round_rect", (VALUE(*)(ANYARGS))rb_kui_stroke_round_rect, -1);
1537
+ rb_define_module_function(mKonpeitoUI, "fill_circle", (VALUE(*)(ANYARGS))rb_kui_fill_circle, 5);
1538
+ rb_define_module_function(mKonpeitoUI, "stroke_circle", (VALUE(*)(ANYARGS))rb_kui_stroke_circle, -1);
1539
+ rb_define_module_function(mKonpeitoUI, "draw_line", (VALUE(*)(ANYARGS))rb_kui_draw_line, -1);
1540
+ rb_define_module_function(mKonpeitoUI, "fill_arc", (VALUE(*)(ANYARGS))rb_kui_fill_arc, -1);
1541
+ rb_define_module_function(mKonpeitoUI, "stroke_arc", (VALUE(*)(ANYARGS))rb_kui_stroke_arc, -1);
1542
+ rb_define_module_function(mKonpeitoUI, "fill_triangle", (VALUE(*)(ANYARGS))rb_kui_fill_triangle, -1);
1543
+
1544
+ /* Text */
1545
+ rb_define_module_function(mKonpeitoUI, "draw_text", (VALUE(*)(ANYARGS))rb_kui_draw_text, -1);
1546
+ rb_define_module_function(mKonpeitoUI, "draw_text_styled", (VALUE(*)(ANYARGS))rb_kui_draw_text_styled, -1);
1547
+ rb_define_module_function(mKonpeitoUI, "measure_text_width", (VALUE(*)(ANYARGS))rb_kui_measure_text_width, 4);
1548
+ rb_define_module_function(mKonpeitoUI, "measure_text_height", (VALUE(*)(ANYARGS))rb_kui_measure_text_height, 3);
1549
+ rb_define_module_function(mKonpeitoUI, "get_text_ascent", (VALUE(*)(ANYARGS))rb_kui_get_text_ascent, 3);
1550
+
1551
+ /* Path drawing */
1552
+ rb_define_module_function(mKonpeitoUI, "begin_path", (VALUE(*)(ANYARGS))rb_kui_begin_path, 1);
1553
+ rb_define_module_function(mKonpeitoUI, "path_move_to", (VALUE(*)(ANYARGS))rb_kui_path_move_to, 3);
1554
+ rb_define_module_function(mKonpeitoUI, "path_line_to", (VALUE(*)(ANYARGS))rb_kui_path_line_to, 3);
1555
+ rb_define_module_function(mKonpeitoUI, "close_fill_path", (VALUE(*)(ANYARGS))rb_kui_close_fill_path, 2);
1556
+ rb_define_module_function(mKonpeitoUI, "fill_path", (VALUE(*)(ANYARGS))rb_kui_fill_path_fn, 2);
1557
+
1558
+ /* Canvas state */
1559
+ rb_define_module_function(mKonpeitoUI, "save", (VALUE(*)(ANYARGS))rb_kui_save, 1);
1560
+ rb_define_module_function(mKonpeitoUI, "restore", (VALUE(*)(ANYARGS))rb_kui_restore, 1);
1561
+ rb_define_module_function(mKonpeitoUI, "translate", (VALUE(*)(ANYARGS))rb_kui_translate, 3);
1562
+ rb_define_module_function(mKonpeitoUI, "clip_rect", (VALUE(*)(ANYARGS))rb_kui_clip_rect, 5);
1563
+
1564
+ /* Image operations */
1565
+ rb_define_module_function(mKonpeitoUI, "load_image", (VALUE(*)(ANYARGS))rb_kui_load_image, 2);
1566
+ rb_define_module_function(mKonpeitoUI, "load_net_image", (VALUE(*)(ANYARGS))rb_kui_load_net_image, 2);
1567
+ rb_define_module_function(mKonpeitoUI, "draw_image", (VALUE(*)(ANYARGS))rb_kui_draw_image, 6);
1568
+ rb_define_module_function(mKonpeitoUI, "get_image_width", (VALUE(*)(ANYARGS))rb_kui_get_image_width, 2);
1569
+ rb_define_module_function(mKonpeitoUI, "get_image_height", (VALUE(*)(ANYARGS))rb_kui_get_image_height, 2);
1570
+
1571
+ /* Color utilities */
1572
+ rb_define_module_function(mKonpeitoUI, "interpolate_color", (VALUE(*)(ANYARGS))rb_kui_interpolate_color, 3);
1573
+ rb_define_module_function(mKonpeitoUI, "with_alpha", (VALUE(*)(ANYARGS))rb_kui_with_alpha, 2);
1574
+ rb_define_module_function(mKonpeitoUI, "lighten_color", (VALUE(*)(ANYARGS))rb_kui_lighten_color, 2);
1575
+ rb_define_module_function(mKonpeitoUI, "darken_color", (VALUE(*)(ANYARGS))rb_kui_darken_color, 2);
1576
+
1577
+ /* Window queries */
1578
+ rb_define_module_function(mKonpeitoUI, "get_width", (VALUE(*)(ANYARGS))rb_kui_get_width, 1);
1579
+ rb_define_module_function(mKonpeitoUI, "get_height", (VALUE(*)(ANYARGS))rb_kui_get_height, 1);
1580
+ rb_define_module_function(mKonpeitoUI, "get_scale", (VALUE(*)(ANYARGS))rb_kui_get_scale, 1);
1581
+ rb_define_module_function(mKonpeitoUI, "is_dark_mode", (VALUE(*)(ANYARGS))rb_kui_is_dark_mode, 1);
1582
+ rb_define_module_function(mKonpeitoUI, "request_frame", (VALUE(*)(ANYARGS))rb_kui_request_frame, 1);
1583
+ rb_define_module_function(mKonpeitoUI, "mark_dirty", (VALUE(*)(ANYARGS))rb_kui_mark_dirty, 1);
1584
+ rb_define_module_function(mKonpeitoUI, "needs_redraw", (VALUE(*)(ANYARGS))rb_kui_needs_redraw, 1);
1585
+ rb_define_module_function(mKonpeitoUI, "clear_frame_requested", (VALUE(*)(ANYARGS))rb_kui_clear_frame_requested, 1);
1586
+ rb_define_module_function(mKonpeitoUI, "clear_dirty", (VALUE(*)(ANYARGS))rb_kui_clear_dirty, 1);
1587
+
1588
+ /* IME / Text Input */
1589
+ rb_define_module_function(mKonpeitoUI, "set_text_input_enabled", (VALUE(*)(ANYARGS))rb_kui_set_text_input_enabled, 2);
1590
+ rb_define_module_function(mKonpeitoUI, "set_text_input_rect", (VALUE(*)(ANYARGS))rb_kui_set_text_input_rect, 5);
1591
+
1592
+ /* Clipboard */
1593
+ rb_define_module_function(mKonpeitoUI, "get_clipboard_text", (VALUE(*)(ANYARGS))rb_kui_get_clipboard_text, 1);
1594
+ rb_define_module_function(mKonpeitoUI, "set_clipboard_text", (VALUE(*)(ANYARGS))rb_kui_set_clipboard_text, 2);
1595
+
1596
+ /* Utilities */
1597
+ rb_define_module_function(mKonpeitoUI, "current_time_millis", (VALUE(*)(ANYARGS))rb_kui_current_time_millis, 0);
1598
+ rb_define_module_function(mKonpeitoUI, "number_to_string", (VALUE(*)(ANYARGS))rb_kui_number_to_string, 1);
1599
+
1600
+ /* Math helpers */
1601
+ rb_define_module_function(mKonpeitoUI, "math_cos", (VALUE(*)(ANYARGS))rb_kui_math_cos, 1);
1602
+ rb_define_module_function(mKonpeitoUI, "math_sin", (VALUE(*)(ANYARGS))rb_kui_math_sin, 1);
1603
+ rb_define_module_function(mKonpeitoUI, "math_sqrt", (VALUE(*)(ANYARGS))rb_kui_math_sqrt, 1);
1604
+ rb_define_module_function(mKonpeitoUI, "math_atan2", (VALUE(*)(ANYARGS))rb_kui_math_atan2, 2);
1605
+ rb_define_module_function(mKonpeitoUI, "math_abs", (VALUE(*)(ANYARGS))rb_kui_math_abs, 1);
1606
+
1607
+ /* Event type constants */
1608
+ rb_define_const(mKonpeitoUI, "EVENT_NONE", INT2NUM(KUI_EVENT_NONE));
1609
+ rb_define_const(mKonpeitoUI, "EVENT_MOUSE_DOWN", INT2NUM(KUI_EVENT_MOUSE_DOWN));
1610
+ rb_define_const(mKonpeitoUI, "EVENT_MOUSE_UP", INT2NUM(KUI_EVENT_MOUSE_UP));
1611
+ rb_define_const(mKonpeitoUI, "EVENT_MOUSE_MOVE", INT2NUM(KUI_EVENT_MOUSE_MOVE));
1612
+ rb_define_const(mKonpeitoUI, "EVENT_MOUSE_WHEEL", INT2NUM(KUI_EVENT_MOUSE_WHEEL));
1613
+ rb_define_const(mKonpeitoUI, "EVENT_KEY_DOWN", INT2NUM(KUI_EVENT_KEY_DOWN));
1614
+ rb_define_const(mKonpeitoUI, "EVENT_KEY_UP", INT2NUM(KUI_EVENT_KEY_UP));
1615
+ rb_define_const(mKonpeitoUI, "EVENT_TEXT_INPUT", INT2NUM(KUI_EVENT_TEXT_INPUT));
1616
+ rb_define_const(mKonpeitoUI, "EVENT_RESIZE", INT2NUM(KUI_EVENT_RESIZE));
1617
+ rb_define_const(mKonpeitoUI, "EVENT_IME_PREEDIT", INT2NUM(KUI_EVENT_IME_PREEDIT));
1618
+ rb_define_const(mKonpeitoUI, "EVENT_QUIT", INT2NUM(KUI_EVENT_QUIT));
1619
+
1620
+ /* Modifier constants */
1621
+ rb_define_const(mKonpeitoUI, "MOD_SHIFT", INT2NUM(KUI_MOD_SHIFT));
1622
+ rb_define_const(mKonpeitoUI, "MOD_CONTROL", INT2NUM(KUI_MOD_CONTROL));
1623
+ rb_define_const(mKonpeitoUI, "MOD_ALT", INT2NUM(KUI_MOD_ALT));
1624
+ rb_define_const(mKonpeitoUI, "MOD_SUPER", INT2NUM(KUI_MOD_SUPER));
1625
+ }