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
48static int
49ShowMessage(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
56JoyWin::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
127JoyWin::~JoyWin()
128{
129	//delete fFileTempProbeJoystick;
130	be_app_messenger.SendMessage(B_QUIT_REQUESTED);
131}
132
133
134void
135JoyWin::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
269bool
270JoyWin::QuitRequested()
271{
272	_ApplyChanges();
273	return BWindow::QuitRequested();
274}
275
276
277//---------------------- Private ---------------------------------//
278status_t
279JoyWin::_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
307status_t
308JoyWin::_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
319status_t
320JoyWin::_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
357status_t
358JoyWin::_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
367status_t
368JoyWin::_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
376status_t
377JoyWin::_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
392status_t
393JoyWin::_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
413status_t
414JoyWin::_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
432void
433JoyWin::_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
444void
445JoyWin::_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
457BString
458JoyWin::_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
474PortItem*
475JoyWin::_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
487BString
488JoyWin::_FixPathToName(const char* port)
489{
490	BString temp(port);
491	temp = temp.Remove(0, temp.FindLast('/') + 1) ;
492	return temp;
493}
494
495
496void
497JoyWin::_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
513int32
514JoyWin::_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
529BString
530JoyWin::_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
547status_t
548JoyWin::_FindStick(const char* name)
549{
550	BJoystick *stick = new BJoystick();
551	return stick->Open(name);
552}
553
554
555const char*
556JoyWin::_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
598bool
599JoyWin::_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