Skip to content

Commit cfb29f1

Browse files
authored
Manager: track and set global state (#38)
1 parent 1a104ca commit cfb29f1

5 files changed

Lines changed: 228 additions & 0 deletions

File tree

data/gschema.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<schemalist>
33
<schema path="/io/elementary/desktop/bluetooth/" id="io.elementary.desktop.bluetooth">
4+
<key type="b" name="enabled">
5+
<default>true</default>
6+
<summary>Sets the last bluetooth state to re-apply on reboot</summary>
7+
<description></description>
8+
</key>
49
<key type="b" name="sharing">
510
<default>true</default>
611
<summary>Sets registered agent obex</summary>

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ i18n = import('i18n')
1212

1313
granite_dep = dependency ('granite')
1414
gtk_dep = dependency ('gtk+-3.0')
15+
posix_dep = meson.get_compiler('vala').find_library('posix')
1516

1617
subdir('data')
1718
subdir('po')

src/Services/Manager.vala

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,23 @@ public class Bluetooth.ObjectManager : Object {
2121
public signal void status_discovering ();
2222

2323
public bool has_adapter { get; private set; default = false; }
24+
private bool is_powered { get; set; default = false; }
2425

2526
private Settings settings;
2627
private GLib.DBusObjectManagerClient object_manager;
28+
private RFKillManager rfkill;
2729

2830
construct {
2931
settings = new Settings ("io.elementary.desktop.bluetooth");
3032

3133
create_manager.begin ();
3234

35+
rfkill = new RFKillManager ();
36+
rfkill.open ();
37+
rfkill.device_added.connect (check_global_state);
38+
rfkill.device_changed.connect (check_global_state);
39+
rfkill.device_deleted.connect (check_global_state);
40+
3341
settings.changed ["sharing"].connect (() => {
3442
if (settings.get_boolean ("sharing")) {
3543
register_agent ();
@@ -38,6 +46,10 @@ public class Bluetooth.ObjectManager : Object {
3846
}
3947
});
4048

49+
settings.changed["enabled"].connect (() => {
50+
set_global_state.begin ();
51+
});
52+
4153
register_agent ();
4254
}
4355

@@ -137,7 +149,14 @@ public class Bluetooth.ObjectManager : Object {
137149
if (discovering != null) {
138150
status_discovering ();
139151
}
152+
153+
var powered = changed.lookup_value ("Powered", new VariantType ("b"));
154+
if (powered != null) {
155+
check_global_state ();
156+
}
140157
});
158+
159+
check_global_state ();
141160
}
142161
}
143162

@@ -147,6 +166,8 @@ public class Bluetooth.ObjectManager : Object {
147166
} else if (iface is Bluetooth.Adapter) {
148167
has_adapter = !get_adapters ().is_empty;
149168
}
169+
170+
check_global_state ();
150171
}
151172

152173
public Gee.LinkedList<Bluetooth.Adapter> get_adapters () requires (object_manager != null) {
@@ -234,4 +255,54 @@ public class Bluetooth.ObjectManager : Object {
234255

235256
return null;
236257
}
258+
259+
private void check_global_state () {
260+
var active = false;
261+
var adapters = get_adapters ();
262+
foreach (var adapter in adapters) {
263+
if (adapter.powered) {
264+
active = true;
265+
break;
266+
}
267+
}
268+
269+
if (active != is_powered) {
270+
is_powered = active;
271+
}
272+
273+
if (settings.get_boolean ("enabled") != is_powered) {
274+
settings.set_boolean ("enabled", is_powered);
275+
}
276+
}
277+
278+
private async void set_global_state () {
279+
var state = settings.get_boolean ("enabled");
280+
if (is_powered == state) {
281+
return;
282+
}
283+
284+
/* `is_powered` and `connected` properties will be set by the check_global state () callback when adapter or device
285+
* properties change. Do not set now so that global_state_changed signal will be emitted. */
286+
var adapters = get_adapters ();
287+
foreach (var adapter in adapters) {
288+
adapter.powered = state;
289+
}
290+
291+
if (state == false) {
292+
var devices = get_devices ();
293+
foreach (var device in devices) {
294+
if (device.connected) {
295+
try {
296+
yield device.disconnect ();
297+
} catch (Error e) {
298+
critical (e.message);
299+
}
300+
}
301+
}
302+
}
303+
304+
rfkill.set_software_lock (RFKillDeviceType.BLUETOOTH, !state);
305+
306+
check_global_state ();
307+
}
237308
}

src/Services/RFKill.vala

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright (C) 2012 Canonical Ltd.
3+
* Author: Robert Ancell <robert.ancell@canonical.com>
4+
*
5+
* This program is free software: you can redistribute it and/or modify it under
6+
* the terms of the GNU General Public License as published by the Free Software
7+
* Foundation, either version 3 of the License, or (at your option) any later
8+
* version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9+
* license.
10+
*/
11+
12+
public enum RFKillDeviceType {
13+
ALL = 0,
14+
WLAN,
15+
BLUETOOTH,
16+
UWB,
17+
WIMAX,
18+
WMAN
19+
}
20+
21+
public class RFKillDevice {
22+
public signal void changed ();
23+
24+
public bool software_lock {
25+
get { return _software_lock; }
26+
set {
27+
var event = RFKillEvent ();
28+
event.idx = idx;
29+
event.op = RFKillOperation.CHANGE;
30+
event.soft = value ? 1 : 0;
31+
if (Posix.write (manager.fd, &event, 8) != 8)
32+
return;
33+
}
34+
}
35+
36+
public bool hardware_lock { get { return _hardware_lock; } }
37+
38+
public RFKillDeviceType device_type { get { return _device_type; } }
39+
40+
internal RFKillManager manager;
41+
internal uint32 idx;
42+
internal RFKillDeviceType _device_type;
43+
internal bool _software_lock;
44+
internal bool _hardware_lock;
45+
46+
internal RFKillDevice (RFKillManager manager, uint32 idx, RFKillDeviceType device_type, bool software_lock, bool hardware_lock) {
47+
this.manager = manager;
48+
this.idx = idx;
49+
_device_type = device_type;
50+
_software_lock = software_lock;
51+
_hardware_lock = hardware_lock;
52+
}
53+
}
54+
55+
public class RFKillManager : Object {
56+
public signal void device_added (RFKillDevice device);
57+
public signal void device_changed (RFKillDevice device);
58+
public signal void device_deleted (RFKillDevice device);
59+
60+
public RFKillManager () {
61+
_devices = new List<RFKillDevice> ();
62+
}
63+
64+
public void open () {
65+
fd = Posix.open ("/dev/rfkill", Posix.O_RDWR);
66+
Posix.fcntl (fd, Posix.F_SETFL, Posix.O_NONBLOCK);
67+
68+
/* Read initial state */
69+
while (read_event ());
70+
71+
/* Monitor for events */
72+
var channel = new IOChannel.unix_new (fd);
73+
channel.add_watch (IOCondition.IN | IOCondition.HUP | IOCondition.ERR, () => { return read_event (); });
74+
}
75+
76+
public List<RFKillDevice> get_devices () {
77+
var devices = new List<RFKillDevice> ();
78+
foreach (var device in _devices)
79+
devices.append (device);
80+
return devices;
81+
}
82+
83+
public void set_software_lock (RFKillDeviceType type, bool lock_enabled) {
84+
var event = RFKillEvent ();
85+
event.type = type;
86+
event.op = RFKillOperation.CHANGE_ALL;
87+
event.soft = lock_enabled ? 1 : 0;
88+
if (Posix.write (fd, &event, 8) != 8)
89+
return;
90+
}
91+
92+
internal int fd = -1;
93+
private List<RFKillDevice> _devices;
94+
95+
private bool read_event () {
96+
var event = RFKillEvent ();
97+
if (Posix.read (fd, &event, 8) != 8)
98+
return false;
99+
100+
switch (event.op) {
101+
case RFKillOperation.ADD:
102+
var device = new RFKillDevice (this, event.idx, (RFKillDeviceType) event.type, event.soft != 0, event.hard != 0);
103+
_devices.append (device);
104+
device_added (device);
105+
break;
106+
case RFKillOperation.DELETE:
107+
var device = get_device (event.idx);
108+
if (device != null) {
109+
_devices.remove (device);
110+
device_deleted (device);
111+
}
112+
break;
113+
case RFKillOperation.CHANGE:
114+
var device = get_device (event.idx);
115+
if (device != null) {
116+
device._software_lock = event.soft != 0;
117+
device._hardware_lock = event.hard != 0;
118+
device.changed ();
119+
device_changed (device);
120+
}
121+
break;
122+
}
123+
return true;
124+
}
125+
126+
private RFKillDevice? get_device (uint32 idx) {
127+
foreach (var device in _devices) {
128+
if (device.idx == idx)
129+
return device;
130+
}
131+
132+
return null;
133+
}
134+
}
135+
136+
private struct RFKillEvent {
137+
uint32 idx;
138+
uint8 type;
139+
uint8 op;
140+
uint8 soft;
141+
uint8 hard;
142+
}
143+
144+
private enum RFKillOperation {
145+
ADD = 0,
146+
DELETE,
147+
CHANGE,
148+
CHANGE_ALL
149+
}

src/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ executable(
55
'Services' / 'Device.vala',
66
'Services' / 'Manager.vala',
77
'Services' / 'ObexAgent.vala',
8+
'Services' / 'RFKill.vala',
89
'Services' / 'Session.vala',
910
'Services' / 'Transfer.vala',
1011
'Dialog' / 'BtReceiver.vala',
@@ -14,6 +15,7 @@ executable(
1415
dependencies : [
1516
granite_dep,
1617
gtk_dep,
18+
posix_dep
1719
],
1820
install : true
1921
)

0 commit comments

Comments
 (0)