bluez-profile 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: []