bubblezone 0.0.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 464eb72e138a345fabe04d3afd3b9df88cf45dc46d2acdef5549851e65599e78
4
- data.tar.gz: 8fa05abaa960d03625b415ba0a64628c9794a637282f490ac5978c674cc1c74a
3
+ metadata.gz: 233090f01a0a7b63b5a2fe7cafa1cea8b714fe846ad6dac9ee852238b80692ee
4
+ data.tar.gz: 6e8084cbcb55dd14712c61e8d994a68fe7d9e538d71497dae7da2dc2389c5d17
5
5
  SHA512:
6
- metadata.gz: df0548ca34f9c95b7e5fdd0618230d2ef58593fa06aa9edfe49a05a087b5736d42070f85162bf07c3620071e3dad46ba68c8ed556c7e4a41bf73a4cbeddeb377
7
- data.tar.gz: f4658720eb81685489105e20308a36d8b548df273f720d9c9683fd7e6dce51f71081235355ec1a00cdf46ebd21dbe3916ac1ebfc13451266d1d5f6682d34233c
6
+ metadata.gz: 8df820e13416ce757560d86d7d6cf5ab14da8c6a698db44c6620ab2e07b423f90235586a8cd218a44badf26ad008f824d7475067b06cad8df489fdd4cb3869b4
7
+ data.tar.gz: 89f04858df8e4fe9a14d0594c2e68dc661c16fd00bf5a6242941c7d44d1faed8e7ac6b9f654b03d0088139d6157d59dc40f59ee15e00e7552beef451312da2de
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Marco Roth
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -1,39 +1,370 @@
1
- # Bubblezone
1
+ <div align="center">
2
+ <h1>Bubblezone for Ruby</h1>
3
+ <h4>Helper utility for <a href="https://github.com/marcoroth/bubbletea-ruby">Bubble Tea</a>, allowing easy mouse event tracking in terminal applications.</h4>
2
4
 
3
- TODO: Delete this and the text below, and describe your gem
5
+ <p>
6
+ <a href="https://rubygems.org/gems/bubblezone"><img alt="Gem Version" src="https://img.shields.io/gem/v/bubblezone"></a>
7
+ <a href="https://github.com/marcoroth/bubblezone-ruby/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/marcoroth/bubblezone-ruby"></a>
8
+ </p>
4
9
 
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/bubblezone`. To experiment with that code, run `bin/console` for an interactive prompt.
10
+ <p>Ruby bindings for <a href="https://github.com/lrstanley/bubblezone">lrstanley/bubblezone</a>.<br/>Track clickable regions in terminal UIs. Built for use with <a href="https://github.com/marcoroth/bubbletea-ruby">Bubble Tea</a> and <a href="https://github.com/marcoroth/lipgloss-ruby">Lipgloss</a>.</p>
11
+ </div>
6
12
 
7
13
  ## Installation
8
14
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
15
+ **Add to your Gemfile:**
10
16
 
11
- Install the gem and add to the application's Gemfile by executing:
12
-
13
- ```bash
14
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
17
+ ```ruby
18
+ gem "bubblezone"
15
19
  ```
16
20
 
17
- If bundler is not being used to manage dependencies, install the gem by executing:
21
+ **Or install directly:**
18
22
 
19
23
  ```bash
20
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
24
+ gem install bubblezone
21
25
  ```
22
26
 
23
27
  ## Usage
24
28
 
25
- TODO: Write usage instructions here
29
+ ### Basic Zone Marking
30
+
31
+ **Initialize the global zone manager:**
32
+
33
+ ```ruby
34
+ require "bubblezone"
35
+
36
+ Bubblezone.new_global
37
+ ```
38
+
39
+ **Mark a region with an ID:**
40
+
41
+ ```ruby
42
+ button = Bubblezone.mark("my_button", "Click Me")
43
+ ```
44
+
45
+ **Build your layout and scan to register zones:**
46
+
47
+ ```ruby
48
+ layout = "Header\n#{button}\nFooter"
49
+ output = Bubblezone.scan(layout)
50
+ puts output
51
+ ```
52
+
53
+ **Output:**
54
+
55
+ ```
56
+ Header
57
+ Click Me
58
+ Footer
59
+ ```
60
+
61
+ ### Getting Zone Information
62
+
63
+ **Get zone info by ID:**
64
+
65
+ ```ruby
66
+ zone = Bubblezone.get("my_button")
67
+ ```
68
+
69
+ **Check zone bounds:**
70
+
71
+ ```ruby
72
+ if zone
73
+ puts "Zone bounds: (#{zone.start_x}, #{zone.start_y}) to (#{zone.end_x}, #{zone.end_y})"
74
+ end
75
+ ```
76
+
77
+ ### Checking Mouse Coordinates
78
+
79
+ **Get coordinates from mouse event:**
80
+
81
+ ```ruby
82
+ x, y = message.x, message.y
83
+ ```
84
+
85
+ **Check if coordinates are within a zone:**
86
+
87
+ ```ruby
88
+ zone = Bubblezone.get("my_button")
89
+ if zone&.in_bounds?(x, y)
90
+ puts "Button clicked!"
91
+ end
92
+ ```
93
+
94
+ ### Iterating Over Zones
95
+
96
+ **Iterate over all zones containing the coordinates:**
97
+
98
+ ```ruby
99
+ Bubblezone.each_in_bounds(x, y) do |id, zone|
100
+ puts "Hit zone: #{id}"
101
+ end
102
+ ```
103
+
104
+ **Check if any zone contains the coordinates:**
105
+
106
+ ```ruby
107
+ if Bubblezone.any_in_bounds?(x, y)
108
+ puts "Something was clicked"
109
+ end
110
+ ```
111
+
112
+ **Get the first matching zone:**
113
+
114
+ ```ruby
115
+ result = Bubblezone.find_in_bounds(x, y)
116
+ if result
117
+ id, zone = result
118
+ puts "First hit: #{id}"
119
+ end
120
+ ```
121
+
122
+ ### Zone Prefixes
123
+
124
+ **Prevent ID conflicts between components:**
125
+
126
+ ```ruby
127
+ class MyComponent
128
+ def initialize
129
+ @prefix = Bubblezone.new_prefix
130
+ end
131
+
132
+ def view
133
+ items = ["Apple", "Banana", "Cherry"]
134
+ items.map.with_index do |item, i|
135
+ Bubblezone.mark("#{@prefix}#{i}", item)
136
+ end.join("\n")
137
+ end
138
+ end
139
+ ```
140
+
141
+ ### Manager Instances
142
+
143
+ **Create a dedicated manager:**
144
+
145
+ ```ruby
146
+ manager = Bubblezone::Manager.new
147
+ ```
148
+
149
+ **Use the same API as the global manager:**
150
+
151
+ ```ruby
152
+ marked = manager.mark("zone_id", "Content")
153
+ output = manager.scan(marked)
154
+ zone = manager.get("zone_id")
155
+ ```
156
+
157
+ **Iterate zones:**
158
+
159
+ ```ruby
160
+ manager.each_in_bounds(x, y) { |id, zone| ... }
161
+ ```
162
+
163
+ **Clean up when done:**
164
+
165
+ ```ruby
166
+ manager.close
167
+ ```
168
+
169
+ ### Integration with Bubbletea
170
+
171
+ **Handle mouse clicks in a Bubbletea model:**
172
+
173
+ ```ruby
174
+ require "bubbletea"
175
+ require "bubblezone"
176
+
177
+ Bubblezone.new_global
178
+
179
+ class ClickableApp
180
+ include Bubbletea::Model
181
+
182
+ ITEMS = ["Option A", "Option B", "Option C"]
183
+
184
+ def initialize
185
+ @selected = nil
186
+ @prefix = Bubblezone.new_prefix
187
+ end
188
+
189
+ def init
190
+ [self, nil]
191
+ end
192
+
193
+ def update(message)
194
+ case message
195
+ when Bubbletea::MouseMessage
196
+ if message.release? && (message.left? || message.button == 0)
197
+ result = Bubblezone.find_in_bounds(message.x, message.y)
198
+ if result
199
+ id, _zone = result
200
+ @selected = id.sub(@prefix, "").to_i
201
+ end
202
+ end
203
+ [self, nil]
204
+ when Bubbletea::KeyMessage
205
+ return [self, Bubbletea.quit] if message.to_s == "q"
206
+ [self, nil]
207
+ else
208
+ [self, nil]
209
+ end
210
+ end
211
+
212
+ def view
213
+ lines = ITEMS.map.with_index do |item, i|
214
+ marker = i == @selected ? "[x]" : "[ ]"
215
+ content = "#{marker} #{item}"
216
+ Bubblezone.mark("#{@prefix}#{i}", content)
217
+ end
218
+
219
+ Bubblezone.scan(lines.join("\n"))
220
+ end
221
+ end
222
+
223
+ Bubbletea.run(ClickableApp.new, alt_screen: true, mouse_cell_motion: true)
224
+ ```
225
+
226
+ ### Integration with Lipgloss
227
+
228
+ **Style your content first:**
229
+
230
+ ```ruby
231
+ require "lipgloss"
232
+ require "bubblezone"
233
+
234
+ Bubblezone.new_global
235
+
236
+ button_style = Lipgloss::Style.new
237
+ .background("#7D56F4")
238
+ .foreground("#FFFFFF")
239
+ .padding(0, 3)
240
+ ```
241
+
242
+ **Mark the fully styled content:**
243
+
244
+ ```ruby
245
+ styled_button = button_style.render("Click Me")
246
+ clickable_button = Bubblezone.mark("btn", styled_button)
247
+ ```
248
+
249
+ **Scan to register zones:**
250
+
251
+ ```ruby
252
+ output = Bubblezone.scan(clickable_button)
253
+ ```
254
+
255
+ ## API Reference
256
+
257
+ ### Module Methods (Global Manager)
258
+
259
+ | Method | Description |
260
+ |--------|-------------|
261
+ | `Bubblezone.new_global` | Initialize the global zone manager |
262
+ | `Bubblezone.close` | Close the global manager |
263
+ | `Bubblezone.enabled?` | Check if zone tracking is enabled |
264
+ | `Bubblezone.enabled = bool` | Enable/disable zone tracking |
265
+ | `Bubblezone.new_prefix` | Generate a unique zone ID prefix |
266
+ | `Bubblezone.mark(id, text)` | Wrap text with zone markers |
267
+ | `Bubblezone.scan(text)` | Parse zones and strip markers |
268
+ | `Bubblezone.get(id)` | Get ZoneInfo for an ID (or nil) |
269
+ | `Bubblezone.clear(id)` | Remove a stored zone |
270
+ | `Bubblezone.clear_all` | Remove all stored zones |
271
+ | `Bubblezone.zone_ids` | Get array of all tracked zone IDs |
272
+
273
+ ### Iteration Methods
274
+
275
+ | Method | Description |
276
+ |--------|-------------|
277
+ | `Bubblezone.each_in_bounds(x, y) { \|id, zone\| }` | Yield each zone containing coordinates |
278
+ | `Bubblezone.any_in_bounds?(x, y)` | Check if any zone contains coordinates |
279
+ | `Bubblezone.find_in_bounds(x, y)` | Get first `[id, zone]` pair, or nil |
280
+
281
+ ### Manager Class
282
+
283
+ | Method | Description |
284
+ |--------|-------------|
285
+ | `Manager.new` | Create a new zone manager |
286
+ | `#close` | Close the manager |
287
+ | `#enabled?` / `#enabled=` | Get/set enabled state |
288
+ | `#new_prefix` | Generate unique prefix |
289
+ | `#mark(id, text)` | Mark text with zone |
290
+ | `#scan(text)` | Parse and strip markers |
291
+ | `#get(id)` | Get zone info |
292
+ | `#clear(id)` | Clear specific zone |
293
+ | `#clear_all` | Clear all zones |
294
+ | `#zone_ids` | Get all zone IDs |
295
+ | `#each_in_bounds(x, y)` | Iterate matching zones |
296
+ | `#any_in_bounds?(x, y)` | Check for any match |
297
+ | `#find_in_bounds(x, y)` | Get first match |
298
+
299
+ ### ZoneInfo Class
300
+
301
+ | Method | Description |
302
+ |--------|-------------|
303
+ | `#start_x`, `#start_y` | Zone start coordinates |
304
+ | `#end_x`, `#end_y` | Zone end coordinates |
305
+ | `#in_bounds?(x, y)` | Check if coordinates are within zone |
306
+ | `#zero?` | Check if zone has no position data |
307
+ | `#pos(x, y)` | Get relative position within zone |
308
+
309
+ ## Important Notes
310
+
311
+ ### Zone Processing is Asynchronous
312
+
313
+ The Go bubblezone library processes zones asynchronously. After calling `scan`, there may be a brief delay before zones are available via `get`. In interactive applications with Bubbletea, this is typically not an issue as mouse events occur after rendering.
314
+
315
+ ### Coordinate Systems
316
+
317
+ When using `alt_screen: true` with Bubbletea, mouse coordinates are relative to (0, 0) at the top-left of the screen, matching zone coordinates exactly. Without alt screen, you may need to account for terminal scroll position.
318
+
319
+ ### Order of Operations
320
+
321
+ 1. Style your content with Lipgloss
322
+ 2. Mark the styled content with `Bubblezone.mark`
323
+ 3. Build your complete layout
324
+ 4. Call `Bubblezone.scan` on the final output
325
+ 5. Handle mouse events using `get` or `find_in_bounds`
26
326
 
27
327
  ## Development
28
328
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
329
+ **Requirements:**
330
+ - Go 1.23+
331
+ - Ruby 3.2+
30
332
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
333
+ **Install dependencies:**
334
+
335
+ ```bash
336
+ bundle install
337
+ ```
338
+
339
+ **Build the Go library and compile the extension:**
340
+
341
+ ```bash
342
+ bundle exec rake compile
343
+ ```
344
+
345
+ **Run tests:**
346
+
347
+ ```bash
348
+ bundle exec rake test
349
+ ```
350
+
351
+ **Run demos:**
352
+
353
+ ```bash
354
+ ./demo/clickable_alt
355
+ ./demo/clickable_list
356
+ ./demo/clickable_simple
357
+ ./demo/full_layout
358
+ ```
32
359
 
33
360
  ## Contributing
34
361
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/marcoroth/bubblezone. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/marcoroth/bubblezone/blob/main/CODE_OF_CONDUCT.md).
362
+ Bug reports and pull requests are welcome on GitHub at https://github.com/marcoroth/bubblezone-ruby.
363
+
364
+ ## License
365
+
366
+ The gem is available as open source under the terms of the MIT License.
36
367
 
37
- ## Code of Conduct
368
+ ## Acknowledgments
38
369
 
39
- Everyone interacting in the Bubblezone project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/marcoroth/bubblezone/blob/main/CODE_OF_CONDUCT.md).
370
+ This gem wraps [lrstanley/bubblezone](https://github.com/lrstanley/bubblezone), which provides zone tracking for terminal UIs and builds on the excellent [Charm](https://charm.sh) ecosystem, including [lipgloss](https://github.com/marcoroth/lipgloss-ruby) and [bubbletea](https://github.com/marcoroth/bubbletea-ruby).
data/bubblezone.gemspec CHANGED
@@ -33,5 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
34
34
  spec.require_paths = ["lib"]
35
35
 
36
+ spec.extensions = ["ext/bubblezone/extconf.rb"]
37
+
36
38
  spec.add_dependency "rake-compiler", "~> 1.2"
37
39
  end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ extension_name = "bubblezone"
6
+
7
+ def detect_platform
8
+ cpu = RbConfig::CONFIG["host_cpu"]
9
+ os = RbConfig::CONFIG["host_os"]
10
+
11
+ arch = case cpu
12
+ when /aarch64|arm64/ then "arm64"
13
+ when /x86_64|amd64/ then "amd64"
14
+ when /arm/ then "arm"
15
+ when /i[3-6]86/ then "386"
16
+ else cpu
17
+ end
18
+
19
+ goos = case os
20
+ when /darwin/ then "darwin"
21
+ when /mswin|mingw/ then "windows"
22
+ else "linux"
23
+ end
24
+
25
+ "#{goos}_#{arch}"
26
+ end
27
+
28
+ platform = detect_platform
29
+ go_lib_dir = File.expand_path("../../go/build/#{platform}", __dir__)
30
+
31
+ puts "Looking for Go library in: #{go_lib_dir}"
32
+
33
+ unless File.exist?(File.join(go_lib_dir, "libbubblezone.a"))
34
+ abort <<~ERROR
35
+ Could not find libbubblezone.a for platform #{platform}
36
+
37
+ Please build the Go archive first:
38
+ cd go && go build -buildmode=c-archive -o build/#{platform}/libbubblezone.a .
39
+
40
+ Or run:
41
+ bundle exec rake go:build
42
+ ERROR
43
+ end
44
+
45
+ $LDFLAGS << " -L#{go_lib_dir}"
46
+ $INCFLAGS << " -I#{go_lib_dir}"
47
+
48
+ $LOCAL_LIBS << " #{go_lib_dir}/libbubblezone.a"
49
+
50
+ case RbConfig::CONFIG["host_os"]
51
+ when /darwin/
52
+ $LDFLAGS << " -framework CoreFoundation -framework Security -framework SystemConfiguration"
53
+ $LDFLAGS << " -lresolv"
54
+ when /linux/
55
+ $LDFLAGS << " -lpthread -lm -ldl"
56
+ $LDFLAGS << " -lresolv" if find_library("resolv", "res_query")
57
+ end
58
+
59
+ $srcs = [
60
+ "extension.c",
61
+ "manager.c",
62
+ "zone_info.c",
63
+ ]
64
+
65
+ create_makefile("#{extension_name}/#{extension_name}")
@@ -0,0 +1,107 @@
1
+ #include "extension.h"
2
+
3
+ VALUE mBubblezone;
4
+ VALUE cManager;
5
+ VALUE cZoneInfo;
6
+
7
+ static VALUE bubblezone_new_global_rb(VALUE self) {
8
+ bubblezone_new_global();
9
+ return Qnil;
10
+ }
11
+
12
+ static VALUE bubblezone_global_close_rb(VALUE self) {
13
+ bubblezone_global_close();
14
+ return Qnil;
15
+ }
16
+
17
+ static VALUE bubblezone_global_set_enabled_rb(VALUE self, VALUE enabled) {
18
+ bubblezone_global_set_enabled(RTEST(enabled) ? 1 : 0);
19
+ return enabled;
20
+ }
21
+
22
+ static VALUE bubblezone_global_enabled_rb(VALUE self) {
23
+ return bubblezone_global_enabled() ? Qtrue : Qfalse;
24
+ }
25
+
26
+ static VALUE bubblezone_global_new_prefix_rb(VALUE self) {
27
+ char *prefix = bubblezone_global_new_prefix();
28
+ VALUE rb_prefix = rb_utf8_str_new_cstr(prefix);
29
+ bubblezone_free(prefix);
30
+
31
+ return rb_prefix;
32
+ }
33
+
34
+ static VALUE bubblezone_global_mark_rb(VALUE self, VALUE zone_id, VALUE text) {
35
+ Check_Type(zone_id, T_STRING);
36
+ Check_Type(text, T_STRING);
37
+
38
+ char *result = bubblezone_global_mark(StringValueCStr(zone_id), StringValueCStr(text));
39
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
40
+ bubblezone_free(result);
41
+
42
+ return rb_result;
43
+ }
44
+
45
+ static VALUE bubblezone_global_clear_rb(VALUE self, VALUE zone_id) {
46
+ Check_Type(zone_id, T_STRING);
47
+ bubblezone_global_clear(StringValueCStr(zone_id));
48
+ return Qnil;
49
+ }
50
+
51
+ static VALUE bubblezone_global_get_rb(VALUE self, VALUE zone_id) {
52
+ Check_Type(zone_id, T_STRING);
53
+
54
+ unsigned long long handle = bubblezone_global_get(StringValueCStr(zone_id));
55
+
56
+ if (handle == 0) {
57
+ return Qnil;
58
+ }
59
+
60
+ return zone_info_wrap(cZoneInfo, handle);
61
+ }
62
+
63
+ static VALUE bubblezone_global_scan_rb(VALUE self, VALUE text) {
64
+ Check_Type(text, T_STRING);
65
+
66
+ char *result = bubblezone_global_scan(StringValueCStr(text));
67
+ VALUE rb_result = rb_utf8_str_new_cstr(result);
68
+ bubblezone_free(result);
69
+
70
+ return rb_result;
71
+ }
72
+
73
+ static VALUE bubblezone_upstream_version_rb(VALUE self) {
74
+ char *version = bubblezone_upstream_version();
75
+ VALUE rb_version = rb_utf8_str_new_cstr(version);
76
+ bubblezone_free(version);
77
+
78
+ return rb_version;
79
+ }
80
+
81
+ static VALUE bubblezone_version_rb(VALUE self) {
82
+ VALUE gem_version = rb_const_get(self, rb_intern("VERSION"));
83
+ VALUE upstream_version = bubblezone_upstream_version_rb(self);
84
+ VALUE format_string = rb_utf8_str_new_cstr("bubblezone v%s (upstream %s) [Go native extension]");
85
+
86
+ return rb_funcall(rb_mKernel, rb_intern("sprintf"), 3, format_string, gem_version, upstream_version);
87
+ }
88
+
89
+ __attribute__((__visibility__("default"))) void Init_bubblezone(void) {
90
+ mBubblezone = rb_define_module("Bubblezone");
91
+
92
+ Init_bubblezone_manager();
93
+ Init_bubblezone_zone_info();
94
+
95
+ rb_define_singleton_method(mBubblezone, "new_global", bubblezone_new_global_rb, 0);
96
+ rb_define_singleton_method(mBubblezone, "close", bubblezone_global_close_rb, 0);
97
+ rb_define_singleton_method(mBubblezone, "enabled=", bubblezone_global_set_enabled_rb, 1);
98
+ rb_define_singleton_method(mBubblezone, "enabled?", bubblezone_global_enabled_rb, 0);
99
+ rb_define_singleton_method(mBubblezone, "new_prefix", bubblezone_global_new_prefix_rb, 0);
100
+ rb_define_singleton_method(mBubblezone, "mark", bubblezone_global_mark_rb, 2);
101
+ rb_define_singleton_method(mBubblezone, "clear", bubblezone_global_clear_rb, 1);
102
+ rb_define_singleton_method(mBubblezone, "get", bubblezone_global_get_rb, 1);
103
+ rb_define_singleton_method(mBubblezone, "scan", bubblezone_global_scan_rb, 1);
104
+
105
+ rb_define_singleton_method(mBubblezone, "upstream_version", bubblezone_upstream_version_rb, 0);
106
+ rb_define_singleton_method(mBubblezone, "version", bubblezone_version_rb, 0);
107
+ }
@@ -0,0 +1,36 @@
1
+ #ifndef BUBBLEZONE_EXTENSION_H
2
+ #define BUBBLEZONE_EXTENSION_H
3
+
4
+ #include <ruby.h>
5
+ #include "libbubblezone.h"
6
+
7
+ extern VALUE mBubblezone;
8
+ extern VALUE cManager;
9
+ extern VALUE cZoneInfo;
10
+
11
+ typedef struct {
12
+ unsigned long long handle;
13
+ } bubblezone_manager_t;
14
+
15
+ typedef struct {
16
+ unsigned long long handle;
17
+ } bubblezone_zone_info_t;
18
+
19
+ #define GET_MANAGER(self, manager) \
20
+ bubblezone_manager_t *manager; \
21
+ TypedData_Get_Struct(self, bubblezone_manager_t, &manager_type, manager)
22
+
23
+ #define GET_ZONE_INFO(self, zone_info) \
24
+ bubblezone_zone_info_t *zone_info; \
25
+ TypedData_Get_Struct(self, bubblezone_zone_info_t, &zone_info_type, zone_info)
26
+
27
+ extern const rb_data_type_t manager_type;
28
+ extern const rb_data_type_t zone_info_type;
29
+
30
+ VALUE manager_wrap(VALUE klass, unsigned long long handle);
31
+ VALUE zone_info_wrap(VALUE klass, unsigned long long handle);
32
+
33
+ void Init_bubblezone_manager(void);
34
+ void Init_bubblezone_zone_info(void);
35
+
36
+ #endif