1 /* 2 * Copyright 2004-2005, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marcus Overhagen, Axel Dörfler 7 * Jérôme Duval 8 */ 9 10 11 #include <Autolock.h> 12 #include <Deskbar.h> 13 #include <Directory.h> 14 #include <Entry.h> 15 #include <FindDirectory.h> 16 #include <Path.h> 17 #include <Roster.h> 18 #include <String.h> 19 20 #include <image.h> 21 #include <stdio.h> 22 #include <string.h> 23 24 #include "AddOnManager.h" 25 #include "InputServer.h" 26 #include "InputServerTypes.h" 27 #include "MethodReplicant.h" 28 29 30 class AddOnManager::InputServerMonitorHandler : public AddOnMonitorHandler { 31 public: 32 InputServerMonitorHandler(AddOnManager* manager) 33 { 34 fManager = manager; 35 } 36 37 virtual void 38 AddOnCreated(const add_on_entry_info * entry_info) 39 { 40 } 41 42 virtual void 43 AddOnEnabled(const add_on_entry_info * entry_info) 44 { 45 CALLED(); 46 entry_ref ref; 47 make_entry_ref(entry_info->dir_nref.device, entry_info->dir_nref.node, 48 entry_info->name, &ref); 49 BEntry entry(&ref, false); 50 fManager->RegisterAddOn(entry); 51 } 52 53 virtual void 54 AddOnDisabled(const add_on_entry_info * entry_info) 55 { 56 CALLED(); 57 entry_ref ref; 58 make_entry_ref(entry_info->dir_nref.device, entry_info->dir_nref.node, 59 entry_info->name, &ref); 60 BEntry entry(&ref, false); 61 fManager->UnregisterAddOn(entry); 62 } 63 64 virtual void 65 AddOnRemoved(const add_on_entry_info * entry_info) 66 { 67 } 68 69 private: 70 AddOnManager* fManager; 71 }; 72 73 74 // #pragma mark - 75 76 77 AddOnManager::AddOnManager(bool safeMode) 78 : BLooper("addon_manager"), 79 fLock("add-on manager"), 80 fSafeMode(safeMode) 81 { 82 Run(); 83 } 84 85 86 AddOnManager::~AddOnManager() 87 { 88 } 89 90 91 void 92 AddOnManager::LoadState() 93 { 94 RegisterAddOns(); 95 } 96 97 98 void 99 AddOnManager::SaveState() 100 { 101 CALLED(); 102 UnregisterAddOns(); 103 } 104 105 106 status_t 107 AddOnManager::RegisterAddOn(BEntry &entry) 108 { 109 BPath path(&entry); 110 111 entry_ref ref; 112 status_t status = entry.GetRef(&ref); 113 if (status < B_OK) 114 return status; 115 116 PRINT(("AddOnManager::RegisterAddOn(): trying to load \"%s\"\n", path.Path())); 117 118 image_id addon_image = load_add_on(path.Path()); 119 120 if (addon_image < B_OK) { 121 PRINT(("load addon %s failed\n", path.Path())); 122 return addon_image; 123 } 124 125 BString pathString = path.Path(); 126 127 if (pathString.FindFirst("input_server/devices")>0) { 128 BInputServerDevice *(*instantiate_func)(); 129 130 if (get_image_symbol(addon_image, "instantiate_input_device", 131 B_SYMBOL_TYPE_TEXT, (void **)&instantiate_func) < B_OK) { 132 PRINTERR(("AddOnManager::RegisterAddOn(): can't find instantiate_input_device in \"%s\"\n", 133 path.Path())); 134 goto exit_error; 135 } 136 137 BInputServerDevice *isd = (*instantiate_func)(); 138 if (isd == NULL) { 139 PRINTERR(("AddOnManager::RegisterAddOn(): instantiate_input_device in \"%s\" returned NULL\n", 140 path.Path())); 141 goto exit_error; 142 } 143 status_t status = isd->InitCheck(); 144 if (status != B_OK) { 145 PRINTERR(("AddOnManager::RegisterAddOn(): BInputServerDevice.InitCheck in \"%s\" returned %s\n", 146 path.Path(), strerror(status))); 147 delete isd; 148 goto exit_error; 149 } 150 151 RegisterDevice(isd, ref, addon_image); 152 } else if (pathString.FindFirst("input_server/filters")>0) { 153 BInputServerFilter *(*instantiate_func)(); 154 155 if (get_image_symbol(addon_image, "instantiate_input_filter", 156 B_SYMBOL_TYPE_TEXT, (void **)&instantiate_func) < B_OK) { 157 PRINTERR(("AddOnManager::RegisterAddOn(): can't find instantiate_input_filter in \"%s\"\n", 158 path.Path())); 159 goto exit_error; 160 } 161 162 BInputServerFilter *isf = (*instantiate_func)(); 163 if (isf == NULL) { 164 PRINTERR(("AddOnManager::RegisterAddOn(): instantiate_input_filter in \"%s\" returned NULL\n", 165 path.Path())); 166 goto exit_error; 167 } 168 status_t status = isf->InitCheck(); 169 if (status != B_OK) { 170 PRINTERR(("AddOnManager::RegisterAddOn(): BInputServerFilter.InitCheck in \"%s\" returned %s\n", 171 path.Path(), strerror(status))); 172 delete isf; 173 goto exit_error; 174 } 175 176 RegisterFilter(isf, ref, addon_image); 177 } else if (pathString.FindFirst("input_server/methods")>0) { 178 BInputServerMethod *(*instantiate_func)(); 179 180 if (get_image_symbol(addon_image, "instantiate_input_method", 181 B_SYMBOL_TYPE_TEXT, (void **)&instantiate_func) < B_OK) { 182 PRINTERR(("AddOnManager::RegisterAddOn(): can't find instantiate_input_method in \"%s\"\n", 183 path.Path())); 184 goto exit_error; 185 } 186 187 BInputServerMethod *ism = (*instantiate_func)(); 188 if (ism == NULL) { 189 PRINTERR(("AddOnManager::RegisterAddOn(): instantiate_input_method in \"%s\" returned NULL\n", 190 path.Path())); 191 goto exit_error; 192 } 193 status_t status = ism->InitCheck(); 194 if (status != B_OK) { 195 PRINTERR(("AddOnManager::RegisterAddOn(): BInputServerMethod.InitCheck in \"%s\" returned %s\n", 196 path.Path(), strerror(status))); 197 delete ism; 198 goto exit_error; 199 } 200 201 RegisterMethod(ism, ref, addon_image); 202 } else { 203 PRINTERR(("AddOnManager::RegisterAddOn(): addon type not found for \"%s\" \n", path.Path())); 204 goto exit_error; 205 } 206 207 return B_OK; 208 209 exit_error: 210 unload_add_on(addon_image); 211 return status; 212 } 213 214 215 status_t 216 AddOnManager::UnregisterAddOn(BEntry &entry) 217 { 218 BPath path(&entry); 219 220 entry_ref ref; 221 status_t status = entry.GetRef(&ref); 222 if (status < B_OK) 223 return status; 224 225 PRINT(("AddOnManager::UnregisterAddOn(): trying to unload \"%s\"\n", path.Path())); 226 227 BEntry parent; 228 entry.GetParent(&parent); 229 BPath parentPath(&parent); 230 BString pathString = parentPath.Path(); 231 232 BAutolock locker(fLock); 233 234 if (pathString.FindFirst("input_server/devices")>0) { 235 device_info *pinfo; 236 for (fDeviceList.Rewind(); fDeviceList.GetNext(&pinfo);) { 237 if (!strcmp(pinfo->ref.name, ref.name)) { 238 gInputServer->StartStopDevices(*pinfo->isd, false); 239 delete pinfo->isd; 240 if (pinfo->addon_image >= B_OK) 241 unload_add_on(pinfo->addon_image); 242 fDeviceList.RemoveCurrent(); 243 break; 244 } 245 } 246 } else if (pathString.FindFirst("input_server/filters")>0) { 247 filter_info *pinfo; 248 for (fFilterList.Rewind(); fFilterList.GetNext(&pinfo);) { 249 if (!strcmp(pinfo->ref.name, ref.name)) { 250 delete pinfo->isf; 251 if (pinfo->addon_image >= B_OK) 252 unload_add_on(pinfo->addon_image); 253 fFilterList.RemoveCurrent(); 254 break; 255 } 256 } 257 } else if (pathString.FindFirst("input_server/methods")>0) { 258 method_info *pinfo; 259 for (fMethodList.Rewind(); fMethodList.GetNext(&pinfo);) { 260 if (!strcmp(pinfo->ref.name, ref.name)) { 261 delete pinfo->ism; 262 if (pinfo->addon_image >= B_OK) 263 unload_add_on(pinfo->addon_image); 264 fMethodList.RemoveCurrent(); 265 break; 266 } 267 } 268 269 if (fMethodList.CountItems()<=0) { 270 // we remove the method replicant 271 BDeskbar().RemoveItem(REPLICANT_CTL_NAME); 272 gInputServer->SetMethodReplicant(NULL); 273 } else { 274 BMessage msg(IS_REMOVE_METHOD); 275 msg.AddInt32("cookie", (uint32)pinfo->ism); 276 if (gInputServer->MethodReplicant()) 277 gInputServer->MethodReplicant()->SendMessage(&msg); 278 } 279 } 280 281 return B_OK; 282 } 283 284 285 void 286 AddOnManager::RegisterAddOns() 287 { 288 CALLED(); 289 status_t err; 290 291 fHandler = new InputServerMonitorHandler(this); 292 fAddOnMonitor = new AddOnMonitor(fHandler); 293 294 #ifndef APPSERVER_TEST_MODE 295 err = fAddOnMonitor->InitCheck(); 296 if (err != B_OK) { 297 PRINTERR(("AddOnManager::RegisterAddOns(): fAddOnMonitor->InitCheck() returned %s\n", 298 strerror(err))); 299 return; 300 } 301 302 const directory_which directories[] = { 303 B_USER_ADDONS_DIRECTORY, 304 B_COMMON_ADDONS_DIRECTORY, 305 B_BEOS_ADDONS_DIRECTORY 306 }; 307 const char subDirectories[][24] = { 308 "input_server/devices", 309 "input_server/filters", 310 "input_server/methods" 311 }; 312 313 node_ref nref; 314 BDirectory directory; 315 BPath path; 316 // when safemode, only B_BEOS_ADDONS_DIRECTORY is used 317 for (uint32 i = fSafeMode ? 2 : 0 ; i < sizeof(directories) / sizeof(directory_which) ; i++) 318 for (uint32 j = 0 ; j < sizeof(subDirectories) / sizeof(char[24]) ; j++) { 319 if ((find_directory(directories[i], &path) == B_OK) 320 && (path.Append(subDirectories[j]) == B_OK) 321 && (directory.SetTo(path.Path()) == B_OK) 322 && (directory.GetNodeRef(&nref) == B_OK)) { 323 fHandler->AddDirectory(&nref); 324 } 325 } 326 #else 327 BEntry entry("/boot/home/svnhaiku/trunk/tests/servers/input/view_input_device/input_server/devices/ViewInputDevice"); 328 RegisterAddOn(entry); 329 #endif 330 } 331 332 333 void 334 AddOnManager::UnregisterAddOns() 335 { 336 BMessenger messenger(fAddOnMonitor); 337 messenger.SendMessage(B_QUIT_REQUESTED); 338 int32 exit_value; 339 wait_for_thread(fAddOnMonitor->Thread(), &exit_value); 340 delete fHandler; 341 342 BAutolock locker(fLock); 343 344 // we have to stop manually the addons because the monitor doesn't disable them on exit 345 346 { 347 device_info *pinfo; 348 for (fDeviceList.Rewind(); fDeviceList.GetNext(&pinfo);) { 349 gInputServer->StartStopDevices(*pinfo->isd, false); 350 delete pinfo->isd; 351 if (pinfo->addon_image >= B_OK) 352 unload_add_on(pinfo->addon_image); 353 fDeviceList.RemoveCurrent(); 354 } 355 } 356 357 { 358 filter_info *pinfo; 359 for (fFilterList.Rewind(); fFilterList.GetNext(&pinfo);) { 360 delete pinfo->isf; 361 if (pinfo->addon_image >= B_OK) 362 unload_add_on(pinfo->addon_image); 363 fFilterList.RemoveCurrent(); 364 } 365 } 366 367 { 368 method_info *pinfo; 369 for (fMethodList.Rewind(); fMethodList.GetNext(&pinfo);) { 370 delete pinfo->ism; 371 if (pinfo->addon_image >= B_OK) 372 unload_add_on(pinfo->addon_image); 373 fMethodList.RemoveCurrent(); 374 } 375 } 376 } 377 378 379 void 380 AddOnManager::RegisterDevice(BInputServerDevice* device, const entry_ref& ref, 381 image_id addonImage) 382 { 383 BAutolock locker(fLock); 384 385 device_info *pinfo; 386 for (fDeviceList.Rewind(); fDeviceList.GetNext(&pinfo);) { 387 if (!strcmp(pinfo->ref.name, ref.name)) { 388 // we already know this device 389 return; 390 } 391 } 392 393 PRINT(("AddOnManager::RegisterDevice, name %s\n", ref.name)); 394 395 device_info info; 396 info.ref = ref; 397 info.addon_image = addonImage; 398 info.isd = device; 399 400 fDeviceList.Insert(info); 401 } 402 403 404 void 405 AddOnManager::RegisterFilter(BInputServerFilter* filter, const entry_ref& ref, 406 image_id addonImage) 407 { 408 BAutolock locker(fLock); 409 410 filter_info *pinfo; 411 for (fFilterList.Rewind(); fFilterList.GetNext(&pinfo);) { 412 if (!strcmp(pinfo->ref.name, ref.name)) { 413 // we already know this ref 414 return; 415 } 416 } 417 418 PRINT(("%s, name %s\n", __PRETTY_FUNCTION__, ref.name)); 419 420 filter_info info; 421 info.ref = ref; 422 info.addon_image = addonImage; 423 info.isf = filter; 424 425 fFilterList.Insert(info); 426 427 BAutolock lock2(InputServer::gInputFilterListLocker); 428 429 InputServer::gInputFilterList.AddItem(filter); 430 } 431 432 433 void 434 AddOnManager::RegisterMethod(BInputServerMethod* method, const entry_ref& ref, 435 image_id addonImage) 436 { 437 BAutolock locker(fLock); 438 439 method_info *pinfo; 440 for (fMethodList.Rewind(); fMethodList.GetNext(&pinfo);) { 441 if (!strcmp(pinfo->ref.name, ref.name)) { 442 // we already know this ref 443 return; 444 } 445 } 446 447 PRINT(("%s, name %s\n", __PRETTY_FUNCTION__, ref.name)); 448 449 method_info info; 450 info.ref = ref; 451 info.addon_image = addonImage; 452 info.ism = method; 453 454 fMethodList.Insert(info); 455 456 BAutolock lock2(InputServer::gInputMethodListLocker); 457 InputServer::gInputMethodList.AddItem(method); 458 459 if (gInputServer->MethodReplicant() == NULL) { 460 LoadReplicant(); 461 462 if (gInputServer->MethodReplicant()) { 463 _BMethodAddOn_ *addon = InputServer::gKeymapMethod.fOwner; 464 addon->AddMethod(); 465 } 466 } 467 468 if (gInputServer->MethodReplicant()) { 469 _BMethodAddOn_ *addon = method->fOwner; 470 addon->AddMethod(); 471 } 472 } 473 474 475 void 476 AddOnManager::LoadReplicant() 477 { 478 CALLED(); 479 app_info info; 480 be_app->GetAppInfo(&info); 481 482 status_t err = BDeskbar().AddItem(&info.ref); 483 if (err != B_OK) { 484 PRINTERR(("Deskbar refuses to add method replicant: %s\n", strerror(err))); 485 } 486 BMessage request(B_GET_PROPERTY); 487 BMessenger to; 488 BMessenger status; 489 490 request.AddSpecifier("Messenger"); 491 request.AddSpecifier("Shelf"); 492 493 // In the Deskbar the Shelf is in the View "Status" in Window "Deskbar" 494 request.AddSpecifier("View", "Status"); 495 request.AddSpecifier("Window", "Deskbar"); 496 to = BMessenger("application/x-vnd.Be-TSKB", -1); 497 498 BMessage reply; 499 500 if ((to.SendMessage(&request, &reply) == B_OK) 501 && (reply.FindMessenger("result", &status) == B_OK)) { 502 // enum replicant in Status view 503 int32 index = 0; 504 int32 uid; 505 while ((uid = GetReplicantAt(status, index++)) >= B_OK) { 506 BMessage rep_info; 507 if (GetReplicantName(status, uid, &rep_info) != B_OK) { 508 continue; 509 } 510 const char *name; 511 if ((rep_info.FindString("result", &name) == B_OK) 512 && (strcmp(name, REPLICANT_CTL_NAME)==0)) { 513 BMessage rep_view; 514 if (GetReplicantView(status, uid, &rep_view)==0) { 515 BMessenger result; 516 if (rep_view.FindMessenger("result", &result) == B_OK) { 517 gInputServer->SetMethodReplicant(new BMessenger(result)); 518 } 519 } 520 } 521 } 522 } 523 } 524 525 526 int32 527 AddOnManager::GetReplicantAt(BMessenger target, int32 index) const 528 { 529 /* 530 So here we want to get the Unique ID of the replicant at the given index 531 in the target Shelf. 532 */ 533 534 BMessage request(B_GET_PROPERTY);// We're getting the ID property 535 BMessage reply; 536 status_t err; 537 538 request.AddSpecifier("ID");// want the ID 539 request.AddSpecifier("Replicant", index);// of the index'th replicant 540 541 if ((err = target.SendMessage(&request, &reply)) != B_OK) 542 return err; 543 544 int32 uid; 545 if ((err = reply.FindInt32("result", &uid)) != B_OK) 546 return err; 547 548 return uid; 549 } 550 551 552 status_t 553 AddOnManager::GetReplicantName(BMessenger target, int32 uid, BMessage *reply) const 554 { 555 /* 556 We send a message to the target shelf, asking it for the Name of the 557 replicant with the given unique id. 558 */ 559 560 BMessage request(B_GET_PROPERTY); 561 BMessage uid_specifier(B_ID_SPECIFIER);// specifying via ID 562 status_t err; 563 status_t e; 564 565 request.AddSpecifier("Name");// ask for the Name of the replicant 566 567 // IDs are specified using code like the following 3 lines: 568 uid_specifier.AddInt32("id", uid); 569 uid_specifier.AddString("property", "Replicant"); 570 request.AddSpecifier(&uid_specifier); 571 572 if ((err = target.SendMessage(&request, reply)) != B_OK) 573 return err; 574 575 if (((err = reply->FindInt32("error", &e)) != B_OK) || (e != B_OK)) 576 return err ? err : e; 577 578 return B_OK; 579 } 580 581 582 status_t 583 AddOnManager::GetReplicantView(BMessenger target, int32 uid, 584 BMessage* reply) const 585 { 586 /* 587 We send a message to the target shelf, asking it for the Name of the 588 replicant with the given unique id. 589 */ 590 591 BMessage request(B_GET_PROPERTY); 592 BMessage uid_specifier(B_ID_SPECIFIER);// specifying via ID 593 status_t err; 594 status_t e; 595 596 request.AddSpecifier("View"); 597 // ask for the Name of the replicant 598 599 // IDs are specified using code like the following 3 lines: 600 uid_specifier.AddInt32("id", uid); 601 uid_specifier.AddString("property", "Replicant"); 602 request.AddSpecifier(&uid_specifier); 603 604 if ((err = target.SendMessage(&request, reply)) != B_OK) 605 return err; 606 607 if (((err = reply->FindInt32("error", &e)) != B_OK) || (e != B_OK)) 608 return err ? err : e; 609 610 return B_OK; 611 } 612 613 614 void 615 AddOnManager::MessageReceived(BMessage *message) 616 { 617 CALLED(); 618 619 BMessage reply; 620 status_t status; 621 622 PRINT(("%s what:%c%c%c%c\n", __PRETTY_FUNCTION__, message->what >> 24, 623 message->what >> 16, message->what >> 8, message->what)); 624 625 switch (message->what) { 626 case IS_FIND_DEVICES: 627 status = HandleFindDevices(message, &reply); 628 break; 629 case IS_WATCH_DEVICES: 630 status = HandleWatchDevices(message, &reply); 631 break; 632 case IS_IS_DEVICE_RUNNING: 633 status = HandleIsDeviceRunning(message, &reply); 634 break; 635 case IS_START_DEVICE: 636 status = HandleStartStopDevices(message, &reply); 637 break; 638 case IS_STOP_DEVICE: 639 status = HandleStartStopDevices(message, &reply); 640 break; 641 case IS_CONTROL_DEVICES: 642 status = HandleControlDevices(message, &reply); 643 break; 644 case SYSTEM_SHUTTING_DOWN: 645 status = HandleSystemShuttingDown(message, &reply); 646 break; 647 case IS_METHOD_REGISTER: 648 status = HandleMethodReplicant(message, &reply); 649 break; 650 651 default: 652 return; 653 } 654 655 reply.AddInt32("status", status); 656 message->SendReply(&reply); 657 } 658 659 660 status_t 661 AddOnManager::HandleStartStopDevices(BMessage* message, 662 BMessage* reply) 663 { 664 const char *name = NULL; 665 int32 type = 0; 666 if (!((message->FindInt32("type", &type) != B_OK) 667 ^ (message->FindString("device", &name) != B_OK))) 668 return B_ERROR; 669 670 return gInputServer->StartStopDevices(name, (input_device_type)type, 671 message->what == IS_START_DEVICE); 672 } 673 674 675 status_t 676 AddOnManager::HandleFindDevices(BMessage* message, BMessage* reply) 677 { 678 CALLED(); 679 const char *name = NULL; 680 input_device_type type; 681 if (message->FindString("device", &name) == B_OK) { 682 if (gInputServer->GetDeviceInfo(name, &type) != B_OK) 683 return B_NAME_NOT_FOUND; 684 reply->AddString("device", name); 685 reply->AddInt32("type", type); 686 } else { 687 gInputServer->GetDeviceInfos(reply); 688 } 689 return B_OK; 690 } 691 692 693 status_t 694 AddOnManager::HandleWatchDevices(BMessage* message, BMessage* reply) 695 { 696 // TODO 697 return B_OK; 698 } 699 700 701 status_t 702 AddOnManager::HandleIsDeviceRunning(BMessage* message, BMessage* reply) 703 { 704 const char *name; 705 bool running; 706 if (message->FindString("device", &name) != B_OK 707 || gInputServer->GetDeviceInfo(name, NULL, &running) != B_OK) 708 return B_NAME_NOT_FOUND; 709 710 return running ? B_OK : B_ERROR; 711 } 712 713 714 status_t 715 AddOnManager::HandleControlDevices(BMessage* message, BMessage* reply) 716 { 717 CALLED(); 718 const char *name = NULL; 719 int32 type = 0; 720 if (!((message->FindInt32("type", &type) != B_OK) 721 ^ (message->FindString("device", &name) != B_OK))) 722 return B_ERROR; 723 724 uint32 code = 0; 725 BMessage controlMessage; 726 bool hasMessage = true; 727 if (message->FindInt32("code", (int32*)&code) != B_OK) 728 return B_ERROR; 729 if (message->FindMessage("message", &controlMessage) != B_OK) 730 hasMessage = false; 731 732 return gInputServer->ControlDevices(name, (input_device_type)type, 733 code, hasMessage ? &controlMessage : NULL); 734 } 735 736 737 status_t 738 AddOnManager::HandleSystemShuttingDown(BMessage* message, 739 BMessage* reply) 740 { 741 CALLED(); 742 743 // TODO 744 return B_OK; 745 } 746 747 748 status_t 749 AddOnManager::HandleMethodReplicant(BMessage* message, BMessage* reply) 750 { 751 CALLED(); 752 LoadReplicant(); 753 754 BAutolock lock(InputServer::gInputMethodListLocker); 755 756 if (gInputServer->MethodReplicant()) { 757 _BMethodAddOn_ *addon = InputServer::gKeymapMethod.fOwner; 758 addon->AddMethod(); 759 760 for (int32 i=0; i<InputServer::gInputMethodList.CountItems(); i++) { 761 BInputServerMethod *method = 762 (BInputServerMethod *)InputServer::gInputMethodList.ItemAt(i); 763 _BMethodAddOn_ *addon = method->fOwner; 764 addon->AddMethod(); 765 } 766 } 767 768 return B_OK; 769 } 770