xref: /haiku/src/preferences/joysticks/JoyWin.cpp (revision 899e0ef82b5624ace2ccfa5f5a58c8ebee54aaef)
1 /*
2  * Copyright 2007-2009 Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Oliver Ruiz Dorantes	oliver.ruiz.dorantes_at_gmail.com
7  *		Ryan Leavengood			leavengood@gmail.com
8  *		Fredrik Modéen 			fredrik@modeen.se
9  */
10 
11 #include "JoyWin.h"
12 #include "MessageWin.h"
13 #include "CalibWin.h"
14 #include "Global.h"
15 #include "PortItem.h"
16 //#include "FileReadWrite.h"
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 
21 #include <Box.h>
22 #include <Button.h>
23 #include <CheckBox.h>
24 #include <ListView.h>
25 #include <ListItem.h>
26 #include <ScrollView.h>
27 #include <String.h>
28 #include <StringView.h>
29 #include <Application.h>
30 #include <View.h>
31 #include <Path.h>
32 #include <Entry.h>
33 #include <Directory.h>
34 #include <Alert.h>
35 #include <File.h>
36 #include <SymLink.h>
37 #include <Messenger.h>
38 #include <Directory.h>
39 #include <Joystick.h>
40 #include <FindDirectory.h>
41 #include <Joystick.h>
42 
43 #define JOYSTICKPATH "/dev/joystick/"
44 #define JOYSTICKFILEPATH "/boot/system/data/joysticks/"
45 #define JOYSTICKFILESETTINGS "/boot/home/config/settings/joysticks/"
46 #define SELECTGAMEPORTFIRST "Select a game port first"
47 
48 static int
49 ShowMessage(char* string)
50 {
51 	BAlert *alert = new BAlert("Message", string, "OK");
52 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
53 	return alert->Go();
54 }
55 
56 JoyWin::JoyWin(BRect frame, const char *title)
57 	: BWindow(frame, title, B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
58 		B_NOT_ZOOMABLE), fSystemUsedSelect(false),
59 		fJoystick(new BJoystick)
60 {
61 	fProbeButton = new BButton(BRect(15.00, 260.00, 115.00, 285.00),
62 		"ProbeButton", "Probe", new BMessage(PROBE));
63 
64 	fCalibrateButton = new BButton(BRect(270.00, 260.00, 370.00, 285.00),
65 		"CalibrateButton", "Calibrate", new BMessage(CALIBRATE));
66 
67 	fGamePortL = new BListView(BRect(15.00, 30.00, 145.00, 250.00),
68 		"gamePort");
69 	fGamePortL->SetSelectionMessage(new BMessage(PORT_SELECTED));
70 	fGamePortL->SetInvocationMessage(new BMessage(PORT_INVOKE));
71 
72 	fConControllerL = new BListView(BRect(175.00,30.00,370.00,250.00),
73 		"conController");
74 	fConControllerL->SetSelectionMessage(new BMessage(JOY_SELECTED));
75 	fConControllerL->SetInvocationMessage(new BMessage(JOY_INVOKE));
76 
77 	fGamePortS = new BStringView(BRect(15, 5, 160, 25), "gpString",
78 		"Game port");
79 	fGamePortS->SetFont(be_bold_font);
80 	fConControllerS = new BStringView(BRect(170, 5, 330, 25), "ccString",
81 		"Connected controller");
82 
83 	fConControllerS->SetFont(be_bold_font);
84 
85 	fCheckbox = new BCheckBox(BRect(131.00, 260.00, 227.00, 280.00),
86 		"Disabled", "Disabled", new BMessage(DISABLEPORT));
87 	BBox *box = new BBox( Bounds(),"box", B_FOLLOW_ALL,
88 		B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE,
89 		B_PLAIN_BORDER);
90 
91 	box->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
92 
93 	// Add listViews with their scrolls
94 	box->AddChild(new BScrollView("PortScroll", fGamePortL,
95 		B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, B_WILL_DRAW, false, true));
96 
97 	box->AddChild(new BScrollView("ConScroll", fConControllerL, B_FOLLOW_ALL,
98 		B_WILL_DRAW, false, true));
99 
100 	// Adding object
101 	box->AddChild(fCheckbox);
102 	box->AddChild(fGamePortS);
103 	box->AddChild(fConControllerS);
104 	box->AddChild(fProbeButton);
105 	box->AddChild(fCalibrateButton);
106 	AddChild(box);
107 
108 	SetSizeLimits(400, 600, Bounds().Height(), Bounds().Height());
109 
110 	/* Add all the devices */
111 	int32 nr = fJoystick->CountDevices();
112 	for (int32 i = 0; i < nr;i++) {
113 		//BString str(path.Path());
114 		char buf[B_OS_NAME_LENGTH];
115 		fJoystick->GetDeviceName(i, buf, B_OS_NAME_LENGTH);
116 		fGamePortL->AddItem(new PortItem(buf));
117 	}
118 	fGamePortL->Select(0);
119 
120 	/* Add the joysticks specifications */
121 	_AddToList(fConControllerL, JOY_SELECTED, JOYSTICKFILEPATH);
122 
123 	_GetSettings();
124 }
125 
126 
127 JoyWin::~JoyWin()
128 {
129 	//delete fFileTempProbeJoystick;
130 	be_app_messenger.SendMessage(B_QUIT_REQUESTED);
131 }
132 
133 
134 void
135 JoyWin::MessageReceived(BMessage *message)
136 {
137 //	message->PrintToStream();
138 	switch(message->what)
139 	{
140 		case DISABLEPORT:
141 		break;
142 		{
143 			PortItem *item = _GetSelectedItem(fGamePortL);
144 			if (item != NULL) {
145 				//ToDo: item->SetEnabled(true);
146 				//don't work as you can't select a item that are disabled
147 				if(fCheckbox->Value()) {
148 					item->SetEnabled(false);
149 					_SelectDeselectJoystick(fConControllerL, false);
150 				} else {
151 					item->SetEnabled(true);
152 					_SelectDeselectJoystick(fConControllerL, true);
153 					_PerformProbe(item->Text());
154 				}
155 			} //else
156 				//printf("We have a null value\n");
157 		break;
158 		}
159 
160 		case PORT_SELECTED:
161 		{
162 			PortItem *item = _GetSelectedItem(fGamePortL);
163 			if (item != NULL) {
164 				fSystemUsedSelect = true;
165 				if (item->IsEnabled()) {
166 					//printf("SetEnabled = false\n");
167 					fCheckbox->SetValue(false);
168 					_SelectDeselectJoystick(fConControllerL, true);
169 				} else {
170 					//printf("SetEnabled = true\n");
171 					fCheckbox->SetValue(true);
172 					_SelectDeselectJoystick(fConControllerL, false);
173 				}
174 
175 				if (_CheckJoystickExist(item->Text()) == B_ERROR) {
176 					if (_ShowCantFindFileMessage(item->Text()) == B_OK) {
177 						_PerformProbe(item->Text());
178 					}
179 				} else {
180 					BString str(_FindFilePathForSymLink(JOYSTICKFILESETTINGS,
181 						item));
182 					if (str != NULL) {
183 						BString str(_FixPathToName(str.String()));
184 						int32 id = _FindStringItemInList(fConControllerL,
185 									new PortItem(str.String()));
186 						if (id > -1) {
187 							fConControllerL->Select(id);
188 							item->SetJoystickName(BString(str.String()));
189 						}
190 					}
191 				}
192 			} else {
193 				fConControllerL->DeselectAll();
194 				ShowMessage((char*)SELECTGAMEPORTFIRST);
195 			}
196 		break;
197 		}
198 
199 		case PROBE:
200 		case PORT_INVOKE:
201 		{
202 			PortItem *item = _GetSelectedItem(fGamePortL);
203 			if (item != NULL) {
204 				//printf("invoke.. inte null\n");
205 				_PerformProbe(item->Text());
206 			} else
207 				ShowMessage((char*)SELECTGAMEPORTFIRST);
208 		break;
209 		}
210 
211 		case JOY_SELECTED:
212 		{
213 			if (!fSystemUsedSelect) {
214 				PortItem *controllerName = _GetSelectedItem(fConControllerL);
215 				PortItem *portname = _GetSelectedItem(fGamePortL);
216 				if (portname != NULL && controllerName != NULL) {
217 					portname->SetJoystickName(BString(controllerName->Text()));
218 
219 					BString str = portname->GetOldJoystickName();
220 					if (str != NULL) {
221 						BString strOldFile(JOYSTICKFILESETTINGS);
222 						strOldFile.Append(portname->Text());
223 						BEntry entry(strOldFile.String());
224 						entry.Remove();
225 					}
226 					BString strLinkPlace(JOYSTICKFILESETTINGS);
227 					strLinkPlace.Append(portname->Text());
228 
229 					BString strLinkTo(JOYSTICKFILEPATH);
230 					strLinkTo.Append(controllerName->Text());
231 
232 					BDirectory *dir = new BDirectory();
233 					dir->CreateSymLink(strLinkPlace.String(),
234 						strLinkTo.String(), NULL);
235 				} else
236 					ShowMessage((char*)SELECTGAMEPORTFIRST);
237 			}
238 
239 			fSystemUsedSelect = false;
240 		break;
241 		}
242 
243 		case CALIBRATE:
244 		case JOY_INVOKE:
245 		{
246 			PortItem *controllerName = _GetSelectedItem(fConControllerL);
247 			PortItem *portname = _GetSelectedItem(fGamePortL);
248 			if (portname != NULL) {
249 				if (controllerName == NULL)
250 					_ShowNoDeviceConnectedMessage("known", portname->Text());
251 				else {
252 					_ShowNoDeviceConnectedMessage(controllerName->Text(), portname->Text());
253 					/*
254 					ToDo:
255 					Check for a device, and show calibrate window if so
256 					*/
257 				}
258 			} else
259 				ShowMessage((char*)SELECTGAMEPORTFIRST);
260 		break;
261 		}
262 		default:
263 			BWindow::MessageReceived(message);
264 			break;
265 	}
266 }
267 
268 
269 bool
270 JoyWin::QuitRequested()
271 {
272 	_ApplyChanges();
273 	return BWindow::QuitRequested();
274 }
275 
276 
277 //---------------------- Private ---------------------------------//
278 status_t
279 JoyWin::_AddToList(BListView *list, uint32 command, const char* rootPath,
280 	BEntry *rootEntry)
281 {
282 	BDirectory root;
283 
284 	if ( rootEntry != NULL )
285 		root.SetTo( rootEntry );
286 	else if ( rootPath != NULL )
287 		root.SetTo( rootPath );
288 	else
289 		return B_ERROR;
290 
291 	BEntry entry;
292 	while ((root.GetNextEntry(&entry)) > B_ERROR ) {
293 		if (entry.IsDirectory()) {
294 			_AddToList(list, command, rootPath, &entry);
295 		} else {
296 			BPath path;
297 			entry.GetPath(&path);
298 			BString str(path.Path());
299 			str.RemoveFirst(rootPath);
300 			list->AddItem(new PortItem(str.String()));
301 		}
302 	}
303 	return B_OK;
304 }
305 
306 
307 status_t
308 JoyWin::_Calibrate()
309 {
310 	CalibWin* calibw;
311 	BRect rect(100, 100, 500, 400);
312 	calibw = new CalibWin(rect, "Calibrate", B_DOCUMENT_WINDOW_LOOK,
313 		B_NORMAL_WINDOW_FEEL, B_NOT_RESIZABLE | B_NOT_ZOOMABLE);
314 	calibw->Show();
315 	return B_OK;
316 }
317 
318 
319 status_t
320 JoyWin::_PerformProbe(const char* path)
321 {
322 	status_t err = B_ERROR;
323 	err = _ShowProbeMesage(path);
324 	if (err != B_OK) {
325 		return err;
326 	}
327 
328 	MessageWin* mesgw = new MessageWin(Frame(),"Probing", B_MODAL_WINDOW_LOOK,
329 		B_MODAL_APP_WINDOW_FEEL, B_NOT_RESIZABLE | B_NOT_ZOOMABLE);
330 
331 	mesgw->Show();
332 	int32 number = fConControllerL->CountItems();
333 	PortItem *item;
334 	for (int i = 0; i < number; i++) {
335 		// Do a search in JOYSTICKFILEPATH with item->Text() find the string
336 		// that starts with "gadget" (tex gadget = "GamePad Pro") remove
337 		// spacing and the "=" ty to open this one, if failed move to next and
338 		// try to open.. list those that suxesfully work
339 		fConControllerL->Select(i);
340 		int32 selected = fConControllerL->CurrentSelection();
341 		item = dynamic_cast<PortItem*>(fConControllerL->ItemAt(selected));
342 		BString str("Looking for: ");
343 		str << item->Text() << " in port " << path;
344 		mesgw->SetText(str.String());
345 		_FindSettingString(item->Text(), JOYSTICKFILEPATH);
346 		//Need a check to find joysticks (don't know how right now so show a
347 		// don't find message)
348 	}
349 	mesgw->Hide();
350 
351 	//Need a if !found then show this message. else list joysticks.
352 	_ShowNoCompatibleJoystickMessage();
353 	return B_OK;
354 }
355 
356 
357 status_t
358 JoyWin::_ApplyChanges()
359 {
360 	BString str = _BuildDisabledJoysticksFile();
361 	//ToDo; Save the string as the file "disabled_joysticks" under settings
362 	//   (/boot/home/config/settings/disabled_joysticks)
363 	return B_OK;
364 }
365 
366 
367 status_t
368 JoyWin::_GetSettings()
369 {
370 	// ToDo; Read the file "disabled_joysticks" and make the port with the
371 	// same name disabled (/boot/home/config/settings/disabled_joysticks)
372 	return B_OK;
373 }
374 
375 
376 status_t
377 JoyWin::_CheckJoystickExist(const char* path)
378 {
379 	BString str(JOYSTICKFILESETTINGS);
380 	str << path;
381 
382 	BFile file;
383 	status_t status = file.SetTo(str.String(), B_READ_ONLY | B_FAIL_IF_EXISTS);
384 
385 	if (status == B_FILE_EXISTS || status == B_OK)
386 		return B_OK;
387 	else
388 		return B_ERROR;
389 }
390 
391 
392 status_t
393 JoyWin::_ShowProbeMesage(const char* device)
394 {
395 	BString str("An attempt will be made to probe the port '");
396 	str << device << "' to try to figure out what kind of joystick (if any) ";
397 	str << "are attached. There is a small chance this process might cause ";
398 	str << "your machine to lock up and require a reboot. Make sure you have ";
399 	str << "saved changes in all open applications before you start probing.";
400 
401 	BAlert *alert = new BAlert("test1", str.String(), "Probe", "Cancel");
402 	alert->SetShortcut(1, B_ESCAPE);
403 	int32 bindex = alert->Go();
404 
405 	if (bindex == 0)
406 		return B_OK;
407 	else
408 		return B_ERROR;
409 }
410 
411 
412 //Used when a files/joysticks are no were to be found
413 status_t
414 JoyWin::_ShowCantFindFileMessage(const char* port)
415 {
416 	BString str("The file '");
417 	str <<  _FixPathToName(port).String() << "' used by '" << port;
418 	str << "' cannot be found.\n Do you want to ";
419 	str << "try auto-detecting a joystick for this port?";
420 
421 	BAlert *alert = new BAlert("test1", str.String(), "Stop", "Probe");
422 	alert->SetShortcut(1, B_ENTER);
423 	int32 bindex = alert->Go();
424 
425 	if (bindex == 1)
426 		return B_OK;
427 	else
428 		return B_ERROR;
429 }
430 
431 
432 void
433 JoyWin::_ShowNoCompatibleJoystickMessage()
434 {
435 	BString str("There were no compatible joysticks detected on this game");
436 	str << " port. Try another port, or ask the manufacturer of your joystick";
437 	str << " for a driver designed for Haiku or BeOS.";
438 
439 	BAlert *alert = new BAlert("test1", str.String(), "OK");
440 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
441 	alert->Go();
442 }
443 
444 void
445 JoyWin::_ShowNoDeviceConnectedMessage(const char* joy, const char* port)
446 {
447 	BString str("There does not appear to be a ");
448 	str << joy << " device connected to the port '" << port << "'.";
449 
450 	BAlert *alert = new BAlert("test1", str.String(), "Stop");
451 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
452 	alert->Go();
453 }
454 
455 
456 // Use this function to get a string of disabled ports
457 BString
458 JoyWin::_BuildDisabledJoysticksFile()
459 {
460 	BString temp("# This is a list of disabled joystick devices.");
461 	temp << "# Do not include the /dev/joystick/ part of the device name.";
462 
463 	int32 number = fGamePortL->CountItems();
464 	PortItem *item;
465 	for (int i = 0; i < number; i++) {
466 		item = dynamic_cast<PortItem*>(fGamePortL->ItemAt(i));
467 		if (!item->IsEnabled())
468 			temp << "disable = \"" <<  item->Text() << "\"";
469 	}
470 	return temp;
471 }
472 
473 
474 PortItem*
475 JoyWin::_GetSelectedItem(BListView* list)
476 {
477 	int32 id = list->CurrentSelection();
478 	if (id > -1) {
479 		//PortItem *item = dynamic_cast<PortItem*>(list->ItemAt(id));
480 		return dynamic_cast<PortItem*>(list->ItemAt(id));
481 	} else {
482 		return NULL;
483 	}
484 }
485 
486 
487 BString
488 JoyWin::_FixPathToName(const char* port)
489 {
490 	BString temp(port);
491 	temp = temp.Remove(0, temp.FindLast('/') + 1) ;
492 	return temp;
493 }
494 
495 
496 void
497 JoyWin::_SelectDeselectJoystick(BListView* list, bool enable)
498 {
499 	list->DeselectAll();
500 	int32 number = fGamePortL->CountItems();
501 	PortItem *item;
502 	for (int i = 0; i < number; i++) {
503 		item = dynamic_cast<PortItem*>(list->ItemAt(i));
504 		if (!item) {
505 			printf("%s: PortItem at %d is null!\n", __func__, i);
506 			continue;
507 		}
508 		item->SetEnabled(enable);
509 	}
510 }
511 
512 
513 int32
514 JoyWin::_FindStringItemInList(BListView *view, PortItem *item)
515 {
516 	PortItem *strItem = NULL;
517 	int32 number = view->CountItems();
518 	for (int32 i = 0; i < number; i++) {
519 		strItem = dynamic_cast<PortItem*>(view->ItemAt(i));
520 		if (!strcmp(strItem->Text(), item->Text())) {
521 			return i;
522 		}
523 	}
524 	delete strItem;
525 	return -1;
526 }
527 
528 
529 BString
530 JoyWin::_FindFilePathForSymLink(const char* symLinkPath, PortItem *item)
531 {
532 	BPath path(symLinkPath);
533 	path.Append(item->Text());
534 	BEntry entry(path.Path());
535 	if (entry.IsSymLink()) {
536 		BSymLink symLink(&entry);
537 		BDirectory parent;
538 		entry.GetParent(&parent);
539 		symLink.MakeLinkedPath(&parent, &path);
540 		BString str(path.Path());
541 		return str;
542 	}
543 	return NULL;
544 }
545 
546 
547 status_t
548 JoyWin::_FindStick(const char* name)
549 {
550 	BJoystick *stick = new BJoystick();
551 	return stick->Open(name);
552 }
553 
554 
555 const char*
556 JoyWin::_FindSettingString(const char* name, const char* strPath)
557 {
558 	//Make a BJoystick try open it
559 	BString str;
560 
561 	BPath path(strPath);
562 	path.Append(name);
563 	fFileTempProbeJoystick = new BFile(path.Path(), B_READ_ONLY);
564 
565 	//status_t err = find_directory(B_SYSTEM_ETC_DIRECTORY, &path);
566 //	if (err == B_OK) {
567 		//BString str(path.Path());
568 		//str << "/joysticks/" << name;
569 		//printf("path'%s'\n", str.String());
570 		//err = file->SetTo(strPath, B_READ_ONLY);
571 		status_t err = fFileTempProbeJoystick->InitCheck();
572 		if (err == B_OK) {
573 			//FileReadWrite frw(fFileTempProbeJoystick);
574 			//printf("Get going\n");
575 			//printf("Opening file = %s\n", path.Path());
576 			//while (frw.Next(str)) {
577 				//printf("In While loop\n");
578 			//	printf("getline %s \n", str.String());
579 				//Test to open joystick with x number
580 			//}
581 			/*while (_GetLine(str)) {
582 				//printf("In While loop\n");
583 				printf("getline %s \n", str.String());
584 				//Test to open joystick with x number
585 			}*/
586 			return "";
587 		} else
588 			printf("BFile.SetTo error: %s, Path = %s\n", strerror(err), str.String());
589 //	} else
590 //		printf("find_directory error: %s\n", strerror(err));
591 
592 //	delete file;
593 	return "";
594 }
595 
596 /*
597 //Function to get a line from a file
598 bool
599 JoyWin::_GetLine(BString& string)
600 {
601 	// Fill up the buffer with the first chunk of code
602 	if (fPositionInBuffer == 0)
603 		fAmtRead = fFileTempProbeJoystick->Read(&fBuffer, sizeof(fBuffer));
604 	while (fAmtRead > 0) {
605 		while (fPositionInBuffer < fAmtRead) {
606 			// Return true if we hit a newline or the end of the file
607 			if (fBuffer[fPositionInBuffer] == '\n') {
608 				fPositionInBuffer++;
609 				//Convert begin
610 				int32 state = 0;
611 				int32 bufferLen = string.Length();
612 				int32 destBufferLen = bufferLen;
613 				char destination[destBufferLen];
614 //				if (fSourceEncoding)
615 //					convert_to_utf8(fSourceEncoding, string.String(), &bufferLen, destination, &destBufferLen, &state);
616 				string = destination;
617 				return true;
618 			}
619 			string += fBuffer[fPositionInBuffer];
620 			fPositionInBuffer++;
621 		}
622 
623 		// Once the buffer runs out, grab some more and start again
624 		fAmtRead = fFileTempProbeJoystick->Read(&fBuffer, sizeof(fBuffer));
625 		fPositionInBuffer = 0;
626 	}
627 	return false;
628 }
629 */
630