xref: /haiku/src/preferences/network/InterfaceView.cpp (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
1 /*
2  * Copyright 2004-2015 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, <axeld@pinc-software.de>
7  *		Alexander von Gluck, kallisti5@unixzen.com
8  *		John Scipione, jscipione@gmail.com
9  */
10 
11 
12 #include "InterfaceView.h"
13 
14 #include <set>
15 
16 #include <net/if_media.h>
17 
18 #include <Button.h>
19 #include <Catalog.h>
20 #include <ControlLook.h>
21 #include <LayoutBuilder.h>
22 #include <NetworkAddress.h>
23 #include <NetworkDevice.h>
24 #include <StringForSize.h>
25 #include <StringView.h>
26 #include <TextControl.h>
27 
28 #include "MediaTypes.h"
29 #include "WirelessNetworkMenuItem.h"
30 
31 
32 static const uint32 kMsgInterfaceToggle = 'onof';
33 static const uint32 kMsgInterfaceRenegotiate = 'redo';
34 static const uint32 kMsgJoinNetwork = 'join';
35 
36 
37 #undef B_TRANSLATION_CONTEXT
38 #define B_TRANSLATION_CONTEXT "IntefaceView"
39 
40 
41 // #pragma mark - InterfaceView
42 
43 
44 InterfaceView::InterfaceView()
45 	:
46 	BGroupView(B_VERTICAL),
47 	fPulseCount(0)
48 {
49 	SetFlags(Flags() | B_PULSE_NEEDED);
50 
51 	// TODO: Small graph of throughput?
52 
53 	float minimumWidth = be_control_look->DefaultItemSpacing() * 16;
54 
55 	BStringView* statusLabel = new BStringView("status label",
56 		B_TRANSLATE("Status:"));
57 	statusLabel->SetAlignment(B_ALIGN_RIGHT);
58 	fStatusField = new BStringView("status field", "");
59 	fStatusField->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET));
60 	BStringView* macAddressLabel = new BStringView("mac address label",
61 		B_TRANSLATE("MAC address:"));
62 	macAddressLabel->SetAlignment(B_ALIGN_RIGHT);
63 	fMacAddressField = new BStringView("mac address field", "");
64 	fMacAddressField->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET));
65 	BStringView* linkSpeedLabel = new BStringView("link speed label",
66 		B_TRANSLATE("Link speed:"));
67 	linkSpeedLabel->SetAlignment(B_ALIGN_RIGHT);
68 	fLinkSpeedField = new BStringView("link speed field", "");
69 	fLinkSpeedField->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET));
70 
71 	// TODO: These metrics may be better in a BScrollView?
72 	BStringView* linkTxLabel = new BStringView("tx label",
73 		B_TRANSLATE("Sent:"));
74 	linkTxLabel->SetAlignment(B_ALIGN_RIGHT);
75 	fLinkTxField = new BStringView("tx field", "");
76 	fLinkTxField ->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET));
77 	BStringView* linkRxLabel = new BStringView("rx label",
78 		B_TRANSLATE("Received:"));
79 	linkRxLabel->SetAlignment(B_ALIGN_RIGHT);
80 	fLinkRxField = new BStringView("rx field", "");
81 	fLinkRxField ->SetExplicitMinSize(BSize(minimumWidth, B_SIZE_UNSET));
82 
83 	fNetworkMenuField = new BMenuField(B_TRANSLATE("Network:"), new BMenu(
84 		B_TRANSLATE("Choose automatically")));
85 	fNetworkMenuField->SetAlignment(B_ALIGN_RIGHT);
86 	fNetworkMenuField->Menu()->SetLabelFromMarked(true);
87 
88 	// Construct the BButtons
89 	fToggleButton = new BButton("onoff", B_TRANSLATE("Disable"),
90 		new BMessage(kMsgInterfaceToggle));
91 
92 	fRenegotiateButton = new BButton("heal", B_TRANSLATE("Renegotiate"),
93 		new BMessage(kMsgInterfaceRenegotiate));
94 	fRenegotiateButton->SetEnabled(false);
95 
96 	BLayoutBuilder::Group<>(this)
97 		.AddGrid()
98 			.Add(statusLabel, 0, 0)
99 			.Add(fStatusField, 1, 0)
100 			.Add(fNetworkMenuField->CreateLabelLayoutItem(), 0, 1)
101 			.Add(fNetworkMenuField->CreateMenuBarLayoutItem(), 1, 1)
102 			.Add(macAddressLabel, 0, 2)
103 			.Add(fMacAddressField, 1, 2)
104 			.Add(linkSpeedLabel, 0, 3)
105 			.Add(fLinkSpeedField, 1, 3)
106 			.Add(linkTxLabel, 0, 4)
107 			.Add(fLinkTxField, 1, 4)
108 			.Add(linkRxLabel, 0, 5)
109 			.Add(fLinkRxField, 1, 5)
110 		.End()
111 		.AddGlue()
112 		.AddGroup(B_HORIZONTAL)
113 			.AddGlue()
114 			.Add(fToggleButton)
115 			.Add(fRenegotiateButton)
116 		.End();
117 }
118 
119 
120 InterfaceView::~InterfaceView()
121 {
122 }
123 
124 
125 void
126 InterfaceView::SetTo(const char* name)
127 {
128 	fInterface.SetTo(name);
129 }
130 
131 
132 void
133 InterfaceView::AttachedToWindow()
134 {
135 	_Update();
136 		// Populate the fields
137 
138 	fToggleButton->SetTarget(this);
139 	fRenegotiateButton->SetTarget(this);
140 }
141 
142 
143 void
144 InterfaceView::MessageReceived(BMessage* message)
145 {
146 	switch (message->what) {
147 		case kMsgJoinNetwork:
148 		{
149 			const char* name;
150 			BNetworkAddress address;
151 			if (message->FindString("name", &name) == B_OK
152 				&& message->FindFlat("address", &address) == B_OK) {
153 				BNetworkDevice device(fInterface.Name());
154 				status_t status = device.JoinNetwork(address);
155 				if (status != B_OK) {
156 					// This does not really matter, as it's stored this way,
157 					// anyway.
158 				}
159 				// TODO: store value
160 			}
161 			break;
162 		}
163 		case kMsgInterfaceToggle:
164 		{
165 			// Disable/enable interface
166 			uint32 flags = fInterface.Flags();
167 			if ((flags & IFF_UP) != 0)
168 				flags &= ~IFF_UP;
169 			else
170 				flags |= IFF_UP;
171 
172 			if (fInterface.SetFlags(flags) == B_OK)
173 				_Update();
174 			break;
175 		}
176 
177 		case kMsgInterfaceRenegotiate:
178 		{
179 			// TODO: renegotiate addresses
180 			break;
181 		}
182 
183 		default:
184 			BView::MessageReceived(message);
185 	}
186 }
187 
188 
189 void
190 InterfaceView::Pulse()
191 {
192 	// Update the wireless network menu every 5 seconds
193 	_Update((fPulseCount++ % 5) == 0);
194 }
195 
196 
197 /*!	Populate fields with current settings.
198 */
199 status_t
200 InterfaceView::_Update(bool updateWirelessNetworks)
201 {
202 	BNetworkDevice device(fInterface.Name());
203 	bool isWireless = device.IsWireless();
204 	bool disabled = (fInterface.Flags() & IFF_UP) == 0;
205 
206 	if (fInterface.HasLink())
207 		fStatusField->SetText(B_TRANSLATE("connected"));
208 	else
209 		fStatusField->SetText(B_TRANSLATE("disconnected"));
210 
211 	BNetworkAddress hardwareAddress;
212 	if (device.GetHardwareAddress(hardwareAddress) == B_OK)
213 		fMacAddressField->SetText(hardwareAddress.ToString());
214 	else
215 		fMacAddressField->SetText("-");
216 
217 	int media = fInterface.Media();
218 	if ((media & IFM_ACTIVE) != 0)
219 		fLinkSpeedField->SetText(media_type_to_string(media));
220 	else
221 		fLinkSpeedField->SetText("-");
222 
223 	// Update Link stats
224 	ifreq_stats stats;
225 	if (fInterface.GetStats(stats) == B_OK) {
226 		char buffer[100];
227 
228 		string_for_size(stats.send.bytes, buffer, sizeof(buffer));
229 		fLinkTxField->SetText(buffer);
230 
231 		string_for_size(stats.receive.bytes, buffer, sizeof(buffer));
232 		fLinkRxField->SetText(buffer);
233 	}
234 
235 	// TODO: move the wireless info to a separate tab. We should have a
236 	// BListView of available networks, rather than a menu, to make them more
237 	// readable and easier to browse and select.
238 	if (fNetworkMenuField->IsHidden(fNetworkMenuField) && isWireless)
239 		fNetworkMenuField->Show();
240 	else if (!fNetworkMenuField->IsHidden(fNetworkMenuField) && !isWireless)
241 		fNetworkMenuField->Hide();
242 
243 	if (isWireless && updateWirelessNetworks) {
244 		// Rebuild network menu
245 		BMenu* menu = fNetworkMenuField->Menu();
246 		menu->RemoveItems(0, menu->CountItems(), true);
247 
248 		std::set<BNetworkAddress> associated;
249 		BNetworkAddress address;
250 		uint32 cookie = 0;
251 		while (device.GetNextAssociatedNetwork(cookie, address) == B_OK)
252 			associated.insert(address);
253 
254 		wireless_network network;
255 		int32 count = 0;
256 		cookie = 0;
257 		if ((fPulseCount % 15) == 0
258 				&& device.GetNextNetwork(cookie, network) != B_OK) {
259 			// We don't seem to know of any networks, and it's been long
260 			// enough since the last scan, so trigger one to try and
261 			// find some networks.
262 			device.Scan(false, false);
263 
264 			// We don't want to block for the full length of the scan, but
265 			// 50ms is often more than enough to find at least one network,
266 			// and the increase in perceived QoS to the user of not seeing
267 			// "no wireless networks" if we can avoid it is great enough
268 			// to merit such a wait. It's only just over ~4 vertical
269 			// retraces, anyway.
270 			snooze(50 * 1000);
271 		}
272 
273 		cookie = 0;
274 		while (device.GetNextNetwork(cookie, network) == B_OK) {
275 			BMessage* message = new BMessage(kMsgJoinNetwork);
276 
277 			message->AddString("device", fInterface.Name());
278 			message->AddString("name", network.name);
279 			message->AddFlat("address", &network.address);
280 
281 			BMenuItem* item = new WirelessNetworkMenuItem(network.name,
282 				network.signal_strength,
283 				network.authentication_mode, message);
284 			if (associated.find(network.address) != associated.end())
285 				item->SetMarked(true);
286 			menu->AddItem(item);
287 
288 			count++;
289 		}
290 		if (count == 0) {
291 			BMenuItem* item = new BMenuItem(
292 				B_TRANSLATE("<no wireless networks found>"), NULL);
293 			item->SetEnabled(false);
294 			menu->AddItem(item);
295 		} else {
296 			BMenuItem* item = new BMenuItem(
297 				B_TRANSLATE("Choose automatically"), NULL);
298 			if (menu->FindMarked() == NULL)
299 				item->SetMarked(true);
300 			menu->AddItem(item, 0);
301 			menu->AddItem(new BSeparatorItem(), 1);
302 		}
303 		menu->SetTargetForItems(this);
304 	}
305 
306 	//fRenegotiateButton->SetEnabled(!disabled);
307 	fToggleButton->SetLabel(disabled
308 		? B_TRANSLATE("Enable") : B_TRANSLATE("Disable"));
309 
310 	return B_OK;
311 }
312