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