bluez-profile 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/LICENCE +22 -0
  3. data/README.md +189 -0
  4. data/ext/bluez/extconf.rb +13 -0
  5. data/ext/bluez/profile.c +448 -0
  6. metadata +50 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 768299afaa1258a583149f1ff896b6522159c49c2d0090d57491f144a9cfa94b
4
+ data.tar.gz: d69e4bb1204ceb41f3ab4710a5bfb7948407533455f54c8d7b45c375f62f67b7
5
+ SHA512:
6
+ metadata.gz: 9e48632a5208e5f08973bbad6fde0ee347edf685ddff1dd2ab69db1e1edfac3f909390fe8809158bd2911945d23fcb0ebb75818676f87369fd012e130e85a4d4
7
+ data.tar.gz: fbd115fee66256f7115f63f11a82830e96c72387eb1528101a2562b73aad34535ead60b722afd65d906a2ce117deb6308364366e05f73e927fcaae8dd6de50cc
data/LICENCE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2021 Clive Andrews
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # bluez Profile API bindings
2
+
3
+ this gem allows easy creation of bluetooth services.
4
+ For example create a serial link over to another device using bluetooth.
5
+
6
+ https://github.com/pauloborges/bluez/blob/master/doc/profile-api.txt
7
+
8
+
9
+ Clive Andrews 2021
10
+
11
+ ## dependencies
12
+
13
+ * Linux/Unix type system
14
+ * bluez 5
15
+ * glib-2 (dev)
16
+
17
+ ## prerequisites
18
+
19
+ your bluetooth devices must be paired and trusted outside of this gem
20
+ in order for them to communicate.
21
+
22
+ use eg `bluetoothctl`
23
+
24
+ ## API
25
+
26
+ example code:
27
+
28
+ class MyProfile < Bluez::Profile
29
+
30
+ # This method gets called when the service daemon
31
+ # unregisters the profile. A profile can use it to do
32
+ # cleanup tasks.
33
+
34
+ def release
35
+
36
+ end
37
+
38
+ # This method gets called when a new service level
39
+ # connection has been made and authorized.
40
+
41
+ def connection(device, fd, fd_properties)
42
+
43
+ end
44
+
45
+ # This method gets called when a profile gets
46
+ # disconnected.
47
+ #
48
+ # The file descriptor is no longer owned by the service
49
+ # daemon and the profile implementation needs to take
50
+ # care of cleaning up all connections.
51
+ #
52
+ # If multiple file descriptors are indicated via
53
+ # NewConnection, it is expected that all of them
54
+ # are disconnected before returning from this
55
+ # method call.
56
+
57
+ def disconnection(device)
58
+
59
+ end
60
+
61
+ end
62
+
63
+ path = '/my/dbus/path'
64
+ uuid = '1101'
65
+
66
+ options = {
67
+ name: 'my profile',
68
+ channel: 3
69
+ connect: false
70
+ }
71
+
72
+ profile = MyProfile.new(path, uuid, options)
73
+ profile.run # enters loop and blocks here.
74
+
75
+ profile.stop # stops the loop
76
+
77
+
78
+ path: string
79
+
80
+ the dbus object path of the profile.
81
+ eg: "/serial/special/profile"
82
+
83
+ uuid: string
84
+
85
+ the dbus profile uuid
86
+
87
+
88
+ Available options:
89
+
90
+ name: string
91
+
92
+ Human readable name for the profile
93
+
94
+ service: string
95
+
96
+ The primary service class UUID
97
+ (if different from the actual
98
+ profile UUID)
99
+
100
+ role: string
101
+
102
+ For asymmetric profiles that do not
103
+ have UUIDs available to uniquely
104
+ identify each side this
105
+ parameter allows specifying the
106
+ precise local role.
107
+
108
+ Possible values:
109
+
110
+ Bluez::Profile::Client
111
+ Bluez::Profile::Server
112
+
113
+ channel: int
114
+
115
+ RFCOMM channel number that is used
116
+ for client and server UUIDs.
117
+
118
+ If applicable it will be used in the
119
+ SDP record as well.
120
+
121
+ psm: int
122
+
123
+ PSM number that is used for client
124
+ and server UUIDs.
125
+
126
+ If applicable it will be used in the
127
+ SDP record as well.
128
+
129
+ authentication: boolean
130
+
131
+ Pairing is required before connections
132
+ will be established. No devices will
133
+ be connected if not paired.
134
+
135
+ authorization: boolean
136
+
137
+ Request authorization before any
138
+ connection will be established.
139
+
140
+ connect: boolean
141
+
142
+ In case of a client UUID this will
143
+ force connection of the RFCOMM or
144
+ L2CAP channels when a remote device
145
+ is connected.
146
+
147
+ record: string
148
+
149
+ Provide a manual SDP record.
150
+
151
+ version: int
152
+
153
+ Profile version (for SDP record)
154
+
155
+ features: int
156
+
157
+ Profile features (for SDP record)
158
+
159
+ ## server
160
+
161
+ If a channel number is already in use your service may not be registered.
162
+ Check that your services have been registered correctly with eg:
163
+
164
+ sudo sdptool browse local
165
+
166
+ ## examples
167
+
168
+ See the `/examples` folder.
169
+
170
+ ## issues
171
+
172
+ The server side of things seems to work fine.
173
+
174
+ run several servers over the same serial service using different
175
+ channel numbers
176
+
177
+ on the client ( paired and trusted) connect each channel eg :
178
+
179
+ sudo rfcomm connect /dev/rfcomm0 remoteaddr channel1
180
+ sudo rfcomm connect /dev/rfcomm1 remoteaddr channel2
181
+ sudo rfcomm connect /dev/rfcomm2 remoteaddr channel3
182
+
183
+ this works perfectly. each device file allows interaction with the correct server.
184
+
185
+ *BUT* if on the client a profile is registered with bluez using this gem
186
+ and a connection made to the server then things get in a muddle and output
187
+ for the one channel gets sent to the wrong profile !!
188
+
189
+ any ideas ??
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'mkmf'
3
+
4
+ pkg_config("glib-2.0")
5
+ pkg_config("gio-2.0")
6
+ pkg_config("gio-unix-2.0")
7
+
8
+ find_header("gio/gio.h")
9
+
10
+ find_library('gio-2.0','g_bus_watch_name')
11
+
12
+
13
+ create_makefile('bluez/profile')
@@ -0,0 +1,448 @@
1
+ // this ruby binding allows a profile to be registered
2
+ // with the bluez stack.
3
+ //
4
+ // (c) C. Andrews 2021
5
+ //
6
+ // References:
7
+ //
8
+ // https://github.com/pauloborges/bluez/blob/master/doc/profile-api.txt
9
+ // https://github.com/tonyespy/bluez5-spp-example
10
+ // https://github.com/bratsche/glib/blob/master/gio/tests/gdbus-example-server.c
11
+
12
+
13
+ #include <gio/gio.h>
14
+ #include <gio/gunixfdlist.h>
15
+ #include <stdlib.h>
16
+ #include <ruby.h>
17
+
18
+ #define BLUEZ_BUS_NAME "org.bluez"
19
+ #define BLUEZ_BUS_PATH "/org/bluez"
20
+ #define BLUEZ_INTERFACE_DEVICE1 "org.bluez.Device1"
21
+ #define BLUEZ_INTERFACE_PROFILE1 "org.bluez.Profile1"
22
+ #define BLUEZ_INTERFACE_PROFILEMANAGER1 "org.bluez.ProfileManager1"
23
+
24
+ static VALUE mBluez;
25
+ static VALUE cProfile;
26
+ static VALUE eProfileError;
27
+
28
+ typedef struct t_profile_data{
29
+ GDBusConnection* conn;
30
+ GDBusProxy* proxy;
31
+ GDBusNodeInfo* introspection_data;
32
+ guint id;
33
+ GMainLoop* loop;
34
+ int running;
35
+ int fd;
36
+ } t_profile_data;
37
+
38
+
39
+ /* Introspection data for the service we are exporting */
40
+ static const gchar introspection_xml[] =
41
+ "<node>"
42
+ " <interface name='org.bluez.Profile1'>"
43
+ " <method name='Release'>"
44
+ " </method>"
45
+ " <method name='NewConnection'>"
46
+ " <arg type='o' name='device' direction='in'/>"
47
+ " <arg type='h' name='fd' direction='in'/>"
48
+ " <arg type='a{sv}' name='fd_properties' direction='in'/>"
49
+ " </method>"
50
+ " <method name='RequestDisconnection'>"
51
+ " <arg type='o' name='device' direction='in'/>"
52
+ " </method>"
53
+ " </interface>"
54
+ "</node>";
55
+
56
+ // free the c data structure
57
+
58
+ static void profile_free_data(t_profile_data* p){
59
+ if (p->id !=0 ) g_dbus_connection_unregister_object(p->conn, p->id);
60
+ if (p->introspection_data !=NULL ) g_dbus_node_info_unref (p->introspection_data);
61
+ if (p->proxy !=NULL ) g_object_unref (p->proxy);
62
+ if (p->conn !=NULL ) g_object_unref (p->conn);
63
+ if (p->loop !=NULL ) g_main_loop_unref(p->loop);
64
+ p->introspection_data = NULL;
65
+ p->proxy = NULL;
66
+ p->conn = NULL;
67
+ p->loop = NULL;
68
+ free(p);
69
+ }
70
+
71
+ // allocate the c data structure
72
+
73
+ static VALUE profile_alloc_data(VALUE self){
74
+ VALUE obj;
75
+ t_profile_data* data = malloc(sizeof(t_profile_data));
76
+ data->conn = NULL;
77
+ data->proxy = NULL;
78
+ data->introspection_data = NULL;
79
+ data->loop = NULL;
80
+ obj = Data_Wrap_Struct(self, 0, profile_free_data, data);
81
+ return obj;
82
+ }
83
+
84
+ // handle object method calls ----------------------------------------------
85
+ static void
86
+ handle_method_call (GDBusConnection *connection,
87
+ const gchar *sender,
88
+ const gchar *object_path,
89
+ const gchar *interface_name,
90
+ const gchar *method_name,
91
+ GVariant *parameters,
92
+ GDBusMethodInvocation *invocation,
93
+ gpointer user_data)
94
+ {
95
+
96
+ VALUE self;
97
+ GDBusMessage *message;
98
+ GUnixFDList *fd_list;
99
+ GError *error = NULL;
100
+ gchar* path;
101
+ int fd_idx;
102
+ int fd;
103
+ GVariantIter *list;
104
+
105
+ self = (VALUE) user_data;
106
+
107
+ if (g_strcmp0 (method_name, "Release") == 0)
108
+ {
109
+ //printf("Release Called\n");
110
+ rb_funcall(self, rb_intern("release"), 0 );
111
+ }
112
+ else if (g_strcmp0 (method_name, "NewConnection") == 0)
113
+ // in_signature="oha{sv}"
114
+ {
115
+ g_variant_get (parameters, "(oha{sv})", &path, &fd_idx, &list);
116
+ message = g_dbus_method_invocation_get_message (invocation);
117
+ fd_list = g_dbus_message_get_unix_fd_list (message);
118
+ fd = g_unix_fd_list_get (fd_list, fd_idx, &error);
119
+ if (error!=NULL){
120
+ rb_raise(eProfileError, "invalid file descriptor");
121
+ return;
122
+ }
123
+ rb_funcall(self, rb_intern("connection"), 2,rb_str_new2(path), INT2NUM(fd) );
124
+ }
125
+ else if (g_strcmp0 (method_name, "RequestDisconnection") == 0)
126
+ // in_signature="o"
127
+ {
128
+ //printf("RequestDisconnection Called\n");
129
+ g_variant_get (parameters, "o", &path);
130
+ rb_funcall(self, rb_intern("disconnection"), 1,rb_str_new2(path) );
131
+ }
132
+
133
+ }
134
+
135
+ // static GVariant *
136
+ // handle_get_property (GDBusConnection *connection,
137
+ // const gchar *sender,
138
+ // const gchar *object_path,
139
+ // const gchar *interface_name,
140
+ // const gchar *property_name,
141
+ // GError **error,
142
+ // gpointer user_data)
143
+ // {
144
+ // return NULL;
145
+ // }
146
+ //
147
+ // static gboolean
148
+ // handle_set_property (GDBusConnection *connection,
149
+ // const gchar *sender,
150
+ // const gchar *object_path,
151
+ // const gchar *interface_name,
152
+ // const gchar *property_name,
153
+ // GVariant *value,
154
+ // GError **error,
155
+ // gpointer user_data)
156
+ // {
157
+ // return TRUE;
158
+ // }
159
+ //
160
+
161
+ static const GDBusInterfaceVTable vtable =
162
+ {
163
+ handle_method_call,
164
+ NULL,
165
+ NULL
166
+ };
167
+
168
+ // register our profile object with dbus --------------------------------------
169
+
170
+ static guint register_object(VALUE self, t_profile_data* data, const gchar* path){
171
+ guint id;
172
+ GError *error = NULL;
173
+
174
+ id = g_dbus_connection_register_object (
175
+ data->conn,
176
+ path,
177
+ data->introspection_data->interfaces[0],
178
+ &vtable,
179
+ (gpointer) self, //user_data,
180
+ NULL, //user_data_free_func,
181
+ &error
182
+ );
183
+
184
+ return id;
185
+ }
186
+
187
+ // register our profile with bluez --------------------------------------------
188
+
189
+ int register_profile(VALUE path, VALUE uuid, VALUE options, GDBusProxy *proxy){
190
+
191
+ GVariant *profile;
192
+ GVariantBuilder profile_builder;
193
+ GError *error = NULL;
194
+ VALUE val;
195
+
196
+ //printf("register_profile called!\n");
197
+
198
+ g_variant_builder_init(&profile_builder, G_VARIANT_TYPE("(osa{sv})"));
199
+
200
+ g_variant_builder_add (&profile_builder, "o",StringValueCStr(path));
201
+
202
+ g_variant_builder_add (&profile_builder, "s", StringValueCStr(uuid));
203
+
204
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("a{sv}"));
205
+
206
+ // Name
207
+
208
+ val = rb_hash_aref(options, ID2SYM(rb_intern("name")));
209
+ if (val != Qnil){
210
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
211
+ g_variant_builder_add (&profile_builder, "s", "Name");
212
+ g_variant_builder_add (&profile_builder, "v",
213
+ g_variant_new_string(StringValueCStr(val) ));
214
+ g_variant_builder_close(&profile_builder);
215
+ }
216
+
217
+ // Service
218
+
219
+ val = rb_hash_aref(options, ID2SYM(rb_intern("service")));
220
+ if (val != Qnil){
221
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
222
+ g_variant_builder_add (&profile_builder, "s", "Service");
223
+ g_variant_builder_add (&profile_builder, "v",
224
+ g_variant_new_string(StringValueCStr(val) ));
225
+ g_variant_builder_close(&profile_builder);
226
+ }
227
+
228
+ // Role
229
+
230
+ val = rb_hash_aref(options, ID2SYM(rb_intern("role")));
231
+ if (val != Qnil){
232
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
233
+ g_variant_builder_add (&profile_builder, "s", "Role");
234
+ g_variant_builder_add (&profile_builder, "v",
235
+ g_variant_new_string(StringValueCStr(val) ));
236
+ g_variant_builder_close(&profile_builder);
237
+ }
238
+
239
+ // Channel
240
+
241
+ val = rb_hash_aref(options, ID2SYM(rb_intern("channel")));
242
+ if (val != Qnil){
243
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
244
+ g_variant_builder_add (&profile_builder, "s", "Channel");
245
+ g_variant_builder_add (&profile_builder, "v", g_variant_new_uint16(FIX2INT(val)));
246
+ g_variant_builder_close(&profile_builder);
247
+ }
248
+
249
+ // PSM
250
+
251
+ val = rb_hash_aref(options, ID2SYM(rb_intern("psm")));
252
+ if (val != Qnil){
253
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
254
+ g_variant_builder_add (&profile_builder, "s", "PSM");
255
+ g_variant_builder_add (&profile_builder, "v", g_variant_new_uint16(FIX2INT(val)));
256
+ g_variant_builder_close(&profile_builder);
257
+ }
258
+
259
+ // RequireAuthentication
260
+
261
+ val = rb_hash_aref(options, ID2SYM(rb_intern("authentication")));
262
+ if (val != Qnil){
263
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
264
+ g_variant_builder_add (&profile_builder, "s", "RequireAuthentication");
265
+ g_variant_builder_add (&profile_builder, "v", g_variant_new_boolean(RTEST(val) ));
266
+ g_variant_builder_close(&profile_builder);
267
+ }
268
+
269
+ // RequireAuthorization
270
+
271
+ val = rb_hash_aref(options, ID2SYM(rb_intern("authorization")));
272
+ if (val != Qnil){
273
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
274
+ g_variant_builder_add (&profile_builder, "s", "RequireAuthorization");
275
+ g_variant_builder_add (&profile_builder, "v", g_variant_new_boolean(RTEST(val) ));
276
+ g_variant_builder_close(&profile_builder);
277
+ }
278
+
279
+ // AutoConnect
280
+
281
+ val = rb_hash_aref(options, ID2SYM(rb_intern("connect")));
282
+ if (val != Qnil){
283
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
284
+ g_variant_builder_add (&profile_builder, "s", "AutoConnect");
285
+ g_variant_builder_add (&profile_builder, "v", g_variant_new_boolean(RTEST(val) ));
286
+ g_variant_builder_close(&profile_builder);
287
+ }
288
+
289
+ // ServiceRecord
290
+
291
+ val = rb_hash_aref(options, ID2SYM(rb_intern("record")));
292
+ if (val != Qnil){
293
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
294
+ g_variant_builder_add (&profile_builder, "s", "ServiceRecord");
295
+ g_variant_builder_add (&profile_builder, "v",
296
+ g_variant_new_string(StringValueCStr(val) ));
297
+ g_variant_builder_close(&profile_builder);
298
+ }
299
+
300
+ // Version
301
+
302
+ val = rb_hash_aref(options, ID2SYM(rb_intern("version")));
303
+ if (val != Qnil){
304
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
305
+ g_variant_builder_add (&profile_builder, "s", "Version");
306
+ g_variant_builder_add (&profile_builder, "v", g_variant_new_uint16(FIX2INT(val)));
307
+ g_variant_builder_close(&profile_builder);
308
+ }
309
+
310
+ // Features
311
+
312
+ val = rb_hash_aref(options, ID2SYM(rb_intern("features")));
313
+ if (val != Qnil){
314
+ g_variant_builder_open(&profile_builder, G_VARIANT_TYPE("{sv}"));
315
+ g_variant_builder_add (&profile_builder, "s", "Features");
316
+ g_variant_builder_add (&profile_builder, "v", g_variant_new_uint16(FIX2INT(val)));
317
+ g_variant_builder_close(&profile_builder);
318
+ }
319
+
320
+ g_variant_builder_close(&profile_builder);
321
+ profile = g_variant_builder_end(&profile_builder);
322
+
323
+ g_dbus_proxy_call_sync (proxy,
324
+ "RegisterProfile",
325
+ profile,
326
+ G_DBUS_CALL_FLAGS_NONE,
327
+ -1,
328
+ NULL,
329
+ &error);
330
+
331
+ if (error!=NULL){
332
+ return 0;
333
+ }
334
+
335
+ return 1;
336
+ }
337
+
338
+
339
+ // initialize our profile here. ------------------------------------------------
340
+
341
+ static VALUE profile_initialize(VALUE self, VALUE path, VALUE uuid, VALUE options){
342
+ t_profile_data* data = NULL;
343
+ GError *error = NULL;
344
+ int ok;
345
+ const gchar * cpath = StringValueCStr(path);
346
+ Data_Get_Struct(self, t_profile_data, data);
347
+
348
+ // validate the path
349
+
350
+ if (!g_variant_is_object_path(cpath)) {
351
+ rb_raise(eProfileError, "invalid object path");
352
+ return Qnil;
353
+ }
354
+
355
+ data->loop = g_main_loop_new (NULL, FALSE);
356
+ data->running = FALSE;
357
+
358
+ data->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
359
+ if (data->introspection_data==NULL){
360
+ rb_raise(eProfileError, "error generating introspection data");
361
+ return Qnil;
362
+ }
363
+
364
+ // connect to dbus system bus
365
+ data->conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
366
+ if (error!=NULL){
367
+ rb_raise(eProfileError, "error connecting to dbus");
368
+ return Qnil;
369
+ }
370
+
371
+ // obtain a proxy to bluez profile manager.
372
+
373
+ data->proxy = g_dbus_proxy_new_sync (data->conn,
374
+ G_DBUS_PROXY_FLAGS_NONE,
375
+ NULL,/* GDBusInterfaceInfo */
376
+ BLUEZ_BUS_NAME,/* name */
377
+ BLUEZ_BUS_PATH,/* object path */
378
+ BLUEZ_INTERFACE_PROFILEMANAGER1,/* interface */
379
+ NULL,/* GCancellable */
380
+ &error);
381
+
382
+ if (error!=NULL){
383
+ rb_raise(eProfileError, "error connecting to bluez profile manager");
384
+ return Qnil;
385
+ }
386
+
387
+ // register our profile object with dbus
388
+
389
+ data->id = register_object(self, data, cpath);
390
+
391
+ if (data->id==0){
392
+ rb_raise(eProfileError, "error registering profile object");
393
+ return Qnil;
394
+ }
395
+
396
+ // and register the profile with the profile manager.
397
+
398
+ ok = register_profile(path, uuid, options, data->proxy);
399
+
400
+ if (ok==0){
401
+ rb_raise(eProfileError, "error register profile with bluez");
402
+ return Qnil;
403
+ }
404
+
405
+ return self;
406
+ }
407
+
408
+ // run the event loop ----------------------------------------------------------
409
+
410
+ static VALUE profile_run(VALUE self){
411
+ t_profile_data* data = NULL;
412
+ GMainContext * context = g_main_context_default();
413
+ Data_Get_Struct(self, t_profile_data, data);
414
+ if (!data->running){
415
+ data->running = TRUE;
416
+ while(data->running){
417
+ g_main_context_iteration (context, FALSE);
418
+ rb_thread_schedule();
419
+ }
420
+ }
421
+ return self;
422
+ }
423
+
424
+ // stop the event loop ---------------------------------------------------------
425
+
426
+ static VALUE profile_stop(VALUE self){
427
+ t_profile_data* data = NULL;
428
+ Data_Get_Struct(self, t_profile_data, data);
429
+ data->running = FALSE;
430
+ return self;
431
+ }
432
+
433
+ //=============================== ruby interface definition ====================
434
+
435
+ void Init_profile() {
436
+
437
+ mBluez = rb_define_module("Bluez");
438
+ cProfile = rb_define_class_under(mBluez, "Profile", rb_cObject);
439
+ eProfileError = rb_define_class_under(cProfile, "Error",rb_eStandardError);
440
+
441
+ rb_define_const(cProfile, "Client", rb_str_new_cstr("client"));
442
+ rb_define_const(cProfile, "Server", rb_str_new_cstr("server"));
443
+
444
+ rb_define_alloc_func(cProfile, profile_alloc_data);
445
+ rb_define_method(cProfile, "initialize", profile_initialize, 3);
446
+ rb_define_method(cProfile, "run", profile_run, 0);
447
+ rb_define_method(cProfile, "stop", profile_stop, 0);
448
+ }
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bluez-profile
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Clive Andrews
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-08-21 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Create Custom Bluez Bluetooth Profiles
14
+ email:
15
+ - gems@realitybites.eu
16
+ executables: []
17
+ extensions:
18
+ - ext/bluez/extconf.rb
19
+ extra_rdoc_files:
20
+ - README.md
21
+ - LICENCE
22
+ files:
23
+ - LICENCE
24
+ - README.md
25
+ - ext/bluez/extconf.rb
26
+ - ext/bluez/profile.c
27
+ homepage: https://github.com/realbite/bluez-profile
28
+ licenses:
29
+ - MIT
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - ext
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 1.9.2
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubygems_version: 3.1.4
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: binding to Bluez Profile API
50
+ test_files: []