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
ShowMessage(char * string)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
JoyWin(BRect frame,const char * title)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
~JoyWin()127 JoyWin::~JoyWin()
128 {
129 //delete fFileTempProbeJoystick;
130 be_app_messenger.SendMessage(B_QUIT_REQUESTED);
131 }
132
133
134 void
MessageReceived(BMessage * message)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
QuitRequested()270 JoyWin::QuitRequested()
271 {
272 _ApplyChanges();
273 return BWindow::QuitRequested();
274 }
275
276
277 //---------------------- Private ---------------------------------//
278 status_t
_AddToList(BListView * list,uint32 command,const char * rootPath,BEntry * rootEntry)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
_Calibrate()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
_PerformProbe(const char * path)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
_ApplyChanges()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
_GetSettings()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
_CheckJoystickExist(const char * path)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
_ShowProbeMesage(const char * device)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
_ShowCantFindFileMessage(const char * port)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
_ShowNoCompatibleJoystickMessage()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
_ShowNoDeviceConnectedMessage(const char * joy,const char * port)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
_BuildDisabledJoysticksFile()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*
_GetSelectedItem(BListView * list)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
_FixPathToName(const char * port)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
_SelectDeselectJoystick(BListView * list,bool enable)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
_FindStringItemInList(BListView * view,PortItem * item)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
_FindFilePathForSymLink(const char * symLinkPath,PortItem * item)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
_FindStick(const char * name)548 JoyWin::_FindStick(const char* name)
549 {
550 BJoystick *stick = new BJoystick();
551 return stick->Open(name);
552 }
553
554
555 const char*
_FindSettingString(const char * name,const char * strPath)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