xref: /haiku/src/preferences/network/InterfaceView.cpp (revision 4abe18063a98eec087e9e3a88276453ca0979dc4)
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 		if (isWireless) {
208 			// TODO!
209 			BString network = "---";
210 			network.Prepend(" (");
211 			network.Prepend(B_TRANSLATE("connected"));
212 			network.Append(")");
213 			fStatusField->SetText(network.String());
214 		} else {
215 			fStatusField->SetText(B_TRANSLATE("connected"));
216 		}
217 	} else
218 		fStatusField->SetText(B_TRANSLATE("disconnected"));
219 
220 	BNetworkAddress hardwareAddress;
221 	if (device.GetHardwareAddress(hardwareAddress) == B_OK)
222 		fMacAddressField->SetText(hardwareAddress.ToString());
223 	else
224 		fMacAddressField->SetText("-");
225 
226 	int media = fInterface.Media();
227 	if ((media & IFM_ACTIVE) != 0)
228 		fLinkSpeedField->SetText(media_type_to_string(media));
229 	else
230 		fLinkSpeedField->SetText("-");
231 
232 	// Update Link stats
233 	ifreq_stats stats;
234 	if (fInterface.GetStats(stats) == B_OK) {
235 		char buffer[100];
236 
237 		string_for_size(stats.send.bytes, buffer, sizeof(buffer));
238 		fLinkTxField->SetText(buffer);
239 
240 		string_for_size(stats.receive.bytes, buffer, sizeof(buffer));
241 		fLinkRxField->SetText(buffer);
242 	}
243 
244 	// TODO: move the wireless info to a separate tab. We should have a
245 	// BListView of available networks, rather than a menu, to make them more
246 	// readable and easier to browse and select.
247 	if (fNetworkMenuField->IsHidden(fNetworkMenuField) && isWireless)
248 		fNetworkMenuField->Show();
249 	else if (!fNetworkMenuField->IsHidden(fNetworkMenuField) && !isWireless)
250 		fNetworkMenuField->Hide();
251 
252 	if (isWireless && updateWirelessNetworks) {
253 		// Rebuild network menu
254 		BMenu* menu = fNetworkMenuField->Menu();
255 		menu->RemoveItems(0, menu->CountItems(), true);
256 
257 		std::set<BNetworkAddress> associated;
258 		BNetworkAddress address;
259 		uint32 cookie = 0;
260 		while (device.GetNextAssociatedNetwork(cookie, address) == B_OK)
261 			associated.insert(address);
262 
263 		wireless_network network;
264 		int32 count = 0;
265 		cookie = 0;
266 		if ((fPulseCount % 15) == 0
267 				&& device.GetNextNetwork(cookie, network) != B_OK) {
268 			// We don't seem to know of any networks, and it's been long
269 			// enough since the last scan, so trigger one to try and
270 			// find some networks.
271 			device.Scan(false, false);
272 
273 			// We don't want to block for the full length of the scan, but
274 			// 50ms is often more than enough to find at least one network,
275 			// and the increase in perceived QoS to the user of not seeing
276 			// "no wireless networks" if we can avoid it is great enough
277 			// to merit such a wait. It's only just over ~4 vertical
278 			// retraces, anyway.
279 			snooze(50 * 1000);
280 		}
281 
282 		cookie = 0;
283 		while (device.GetNextNetwork(cookie, network) == B_OK) {
284 			BMessage* message = new BMessage(kMsgJoinNetwork);
285 
286 			message->AddString("device", fInterface.Name());
287 			message->AddString("name", network.name);
288 			message->AddFlat("address", &network.address);
289 
290 			BMenuItem* item = new WirelessNetworkMenuItem(network.name,
291 				network.signal_strength,
292 				network.authentication_mode, message);
293 			if (associated.find(network.address) != associated.end())
294 				item->SetMarked(true);
295 			menu->AddItem(item);
296 
297 			count++;
298 		}
299 		if (count == 0) {
300 			BMenuItem* item = new BMenuItem(
301 				B_TRANSLATE("<no wireless networks found>"), NULL);
302 			item->SetEnabled(false);
303 			menu->AddItem(item);
304 		} else {
305 			BMenuItem* item = new BMenuItem(
306 				B_TRANSLATE("Choose automatically"), NULL);
307 			if (menu->FindMarked() == NULL)
308 				item->SetMarked(true);
309 			menu->AddItem(item, 0);
310 			menu->AddItem(new BSeparatorItem(), 1);
311 		}
312 		menu->SetTargetForItems(this);
313 	}
314 
315 	//fRenegotiateButton->SetEnabled(!disabled);
316 	fToggleButton->SetLabel(disabled
317 		? B_TRANSLATE("Enable") : B_TRANSLATE("Disable"));
318 
319 	return B_OK;
320 }
321