2 * Copyright 2004-2013 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
9 * John Scipione, jscipione@gmail.com
13 //! Manager for input_server add-ons (devices, filters, methods)
16 #include "AddOnManager.h"
23 #include <Directory.h>
30 #include <PathMonitor.h>
32 #include "InputServer.h"
33 #include "InputServerTypes.h"
34 #include "MethodReplicant.h"
38 //#define TRACE_ADD_ON_MONITOR
39 #ifdef TRACE_ADD_ON_MONITOR
40 # define TRACE(x...) debug_printf(x)
41 # define ERROR(x...) debug_printf(x)
43 # define TRACE(x...) ;
44 // TODO: probably better to the syslog
45 # define ERROR(x...) debug_printf(x)
50 class AddOnManager::MonitorHandler
: public AddOnMonitorHandler
{
52 MonitorHandler(AddOnManager
* manager
)
57 virtual void AddOnEnabled(const add_on_entry_info
* entryInfo
)
61 make_entry_ref(entryInfo
->dir_nref
.device
, entryInfo
->dir_nref
.node
,
62 entryInfo
->name
, &ref
);
63 BEntry
entry(&ref
, false);
65 fManager
->_RegisterAddOn(entry
);
68 virtual void AddOnDisabled(const add_on_entry_info
* entryInfo
)
72 make_entry_ref(entryInfo
->dir_nref
.device
, entryInfo
->dir_nref
.node
,
73 entryInfo
->name
, &ref
);
74 BEntry
entry(&ref
, false);
76 fManager
->_UnregisterAddOn(entry
);
80 AddOnManager
* fManager
;
88 instantiate_add_on(image_id image
, const char* path
, const char* type
)
90 T
* (*instantiateFunction
)();
92 BString functionName
= "instantiate_input_";
95 if (get_image_symbol(image
, functionName
.String(), B_SYMBOL_TYPE_TEXT
,
96 (void**)&instantiateFunction
) < B_OK
) {
97 ERROR("AddOnManager::_RegisterAddOn(): can't find %s() in \"%s\"\n",
98 functionName
.String(), path
);
102 T
* addOn
= (*instantiateFunction
)();
104 ERROR("AddOnManager::_RegisterAddOn(): %s() in \"%s\" returned "
105 "NULL\n", functionName
.String(), path
);
109 status_t status
= addOn
->InitCheck();
110 if (status
!= B_OK
) {
111 ERROR("AddOnManager::_RegisterAddOn(): InitCheck() in \"%s\" "
112 "returned %s\n", path
, strerror(status
));
121 // #pragma mark - AddOnManager
124 AddOnManager::AddOnManager()
127 fHandler(new(std::nothrow
) MonitorHandler(this))
129 SetHandler(fHandler
);
133 AddOnManager::~AddOnManager()
140 AddOnManager::MessageReceived(BMessage
* message
)
147 TRACE("%s what: %.4s\n", __PRETTY_FUNCTION__
, (char*)&message
->what
);
149 switch (message
->what
) {
150 case IS_FIND_DEVICES
:
151 status
= _HandleFindDevices(message
, &reply
);
153 case IS_WATCH_DEVICES
:
154 status
= _HandleWatchDevices(message
, &reply
);
156 case IS_IS_DEVICE_RUNNING
:
157 status
= _HandleIsDeviceRunning(message
, &reply
);
159 case IS_START_DEVICE
:
160 status
= _HandleStartStopDevices(message
, &reply
);
163 status
= _HandleStartStopDevices(message
, &reply
);
165 case IS_CONTROL_DEVICES
:
166 status
= _HandleControlDevices(message
, &reply
);
168 case SYSTEM_SHUTTING_DOWN
:
169 status
= _HandleSystemShuttingDown(message
, &reply
);
171 case IS_METHOD_REGISTER
:
172 status
= _HandleMethodReplicant(message
, &reply
);
176 _HandleDeviceMonitor(message
);
180 AddOnMonitor::MessageReceived(message
);
184 reply
.AddInt32("status", status
);
185 message
->SendReply(&reply
);
190 AddOnManager::LoadState()
197 AddOnManager::SaveState()
205 AddOnManager::StartMonitoringDevice(DeviceAddOn
* addOn
, const char* device
)
210 if (device
[0] != '/')
214 TRACE("AddOnMonitor::StartMonitoringDevice(%s)\n", path
.String());
217 status_t status
= _AddDevicePath(addOn
, path
.String(), newPath
);
218 if (status
== B_OK
&& newPath
) {
219 status
= BPathMonitor::StartWatching(path
.String(),
220 B_WATCH_FILES_ONLY
| B_WATCH_RECURSIVELY
, this);
221 if (status
!= B_OK
) {
223 _RemoveDevicePath(addOn
, path
.String(), lastPath
);
232 AddOnManager::StopMonitoringDevice(DeviceAddOn
* addOn
, const char *device
)
237 if (device
[0] != '/')
241 TRACE("AddOnMonitor::StopMonitoringDevice(%s)\n", path
.String());
244 status_t status
= _RemoveDevicePath(addOn
, path
.String(), lastPath
);
245 if (status
== B_OK
&& lastPath
)
246 BPathMonitor::StopWatching(path
.String(), this);
256 AddOnManager::_RegisterAddOns()
259 BAutolock
locker(this);
261 fHandler
->AddAddOnDirectories("input_server/devices");
262 fHandler
->AddAddOnDirectories("input_server/filters");
263 fHandler
->AddAddOnDirectories("input_server/methods");
268 AddOnManager::_UnregisterAddOns()
270 BAutolock
locker(this);
272 // We have to stop manually the add-ons because the monitor doesn't
273 // disable them on exit
275 while (device_info
* info
= fDeviceList
.RemoveItemAt(0)) {
276 gInputServer
->StartStopDevices(*info
->add_on
, false);
280 // TODO: what about the filters/methods lists in the input_server?
282 while (filter_info
* info
= fFilterList
.RemoveItemAt(0)) {
286 while (method_info
* info
= fMethodList
.RemoveItemAt(0)) {
293 AddOnManager::_IsDevice(const char* path
) const
295 return strstr(path
, "input_server/devices") != 0;
300 AddOnManager::_IsFilter(const char* path
) const
302 return strstr(path
, "input_server/filters") != 0;
307 AddOnManager::_IsMethod(const char* path
) const
309 return strstr(path
, "input_server/methods") != 0;
314 AddOnManager::_RegisterAddOn(BEntry
& entry
)
319 status_t status
= entry
.GetRef(&ref
);
323 TRACE("AddOnManager::RegisterAddOn(): trying to load \"%s\"\n",
326 image_id image
= load_add_on(path
.Path());
328 ERROR("load addon %s failed\n", path
.Path());
334 if (_IsDevice(path
.Path())) {
335 BInputServerDevice
* device
= instantiate_add_on
<BInputServerDevice
>(
336 image
, path
.Path(), "device");
338 status
= _RegisterDevice(device
, ref
, image
);
339 } else if (_IsFilter(path
.Path())) {
340 BInputServerFilter
* filter
= instantiate_add_on
<BInputServerFilter
>(
341 image
, path
.Path(), "filter");
343 status
= _RegisterFilter(filter
, ref
, image
);
344 } else if (_IsMethod(path
.Path())) {
345 BInputServerMethod
* method
= instantiate_add_on
<BInputServerMethod
>(
346 image
, path
.Path(), "method");
348 status
= _RegisterMethod(method
, ref
, image
);
350 ERROR("AddOnManager::_RegisterAddOn(): addon type not found for "
351 "\"%s\" \n", path
.Path());
355 unload_add_on(image
);
362 AddOnManager::_UnregisterAddOn(BEntry
& entry
)
367 status_t status
= entry
.GetRef(&ref
);
371 TRACE("AddOnManager::UnregisterAddOn(): trying to unload \"%s\"\n",
376 if (_IsDevice(path
.Path())) {
377 for (int32 i
= fDeviceList
.CountItems(); i
-- > 0;) {
378 device_info
* info
= fDeviceList
.ItemAt(i
);
379 if (!strcmp(info
->ref
.name
, ref
.name
)) {
380 gInputServer
->StartStopDevices(*info
->add_on
, false);
381 delete fDeviceList
.RemoveItemAt(i
);
385 } else if (_IsFilter(path
.Path())) {
386 for (int32 i
= fFilterList
.CountItems(); i
-- > 0;) {
387 filter_info
* info
= fFilterList
.ItemAt(i
);
388 if (!strcmp(info
->ref
.name
, ref
.name
)) {
389 BAutolock
locker(InputServer::gInputFilterListLocker
);
390 InputServer::gInputFilterList
.RemoveItem(info
->add_on
);
391 delete fFilterList
.RemoveItemAt(i
);
395 } else if (_IsMethod(path
.Path())) {
396 BInputServerMethod
* method
= NULL
;
398 for (int32 i
= fMethodList
.CountItems(); i
-- > 0;) {
399 method_info
* info
= fMethodList
.ItemAt(i
);
400 if (!strcmp(info
->ref
.name
, ref
.name
)) {
401 BAutolock
locker(InputServer::gInputMethodListLocker
);
402 InputServer::gInputMethodList
.RemoveItem(info
->add_on
);
403 method
= info
->add_on
;
404 // this will only be used as a cookie, and not referenced
406 delete fMethodList
.RemoveItemAt(i
);
411 if (fMethodList
.CountItems() <= 0) {
412 // we remove the method replicant
413 BDeskbar().RemoveItem(REPLICANT_CTL_NAME
);
414 gInputServer
->SetMethodReplicant(NULL
);
415 } else if (method
!= NULL
) {
416 BMessage
msg(IS_REMOVE_METHOD
);
417 msg
.AddInt32("cookie", method
->fOwner
->Cookie());
418 if (gInputServer
->MethodReplicant())
419 gInputServer
->MethodReplicant()->SendMessage(&msg
);
427 //! Takes over ownership of the \a device, regardless of success.
429 AddOnManager::_RegisterDevice(BInputServerDevice
* device
, const entry_ref
& ref
,
432 BAutolock
locker(this);
434 for (int32 i
= fDeviceList
.CountItems(); i
-- > 0;) {
435 device_info
* info
= fDeviceList
.ItemAt(i
);
436 if (!strcmp(info
->ref
.name
, ref
.name
)) {
437 // we already know this device
439 return B_NAME_IN_USE
;
443 TRACE("AddOnManager::RegisterDevice, name %s\n", ref
.name
);
445 device_info
* info
= new(std::nothrow
) device_info
;
452 info
->add_on
= device
;
454 if (!fDeviceList
.AddItem(info
)) {
459 info
->image
= addOnImage
;
465 //! Takes over ownership of the \a filter, regardless of success.
467 AddOnManager::_RegisterFilter(BInputServerFilter
* filter
, const entry_ref
& ref
,
472 for (int32 i
= fFilterList
.CountItems(); i
-- > 0;) {
473 filter_info
* info
= fFilterList
.ItemAt(i
);
474 if (strcmp(info
->ref
.name
, ref
.name
) == 0) {
475 // we already know this ref
477 return B_NAME_IN_USE
;
481 TRACE("%s, name %s\n", __PRETTY_FUNCTION__
, ref
.name
);
483 filter_info
* info
= new(std::nothrow
) filter_info
;
490 info
->add_on
= filter
;
492 if (!fFilterList
.AddItem(info
)) {
497 BAutolock
locker(InputServer::gInputFilterListLocker
);
498 if (!InputServer::gInputFilterList
.AddItem(filter
)) {
499 fFilterList
.RemoveItem(info
, false);
504 info
->image
= addOnImage
;
510 //! Takes over ownership of the \a method, regardless of success.
512 AddOnManager::_RegisterMethod(BInputServerMethod
* method
, const entry_ref
& ref
,
517 for (int32 i
= fMethodList
.CountItems(); i
-- > 0;) {
518 method_info
* info
= fMethodList
.ItemAt(i
);
519 if (!strcmp(info
->ref
.name
, ref
.name
)) {
520 // we already know this ref
522 return B_NAME_IN_USE
;
526 TRACE("%s, name %s\n", __PRETTY_FUNCTION__
, ref
.name
);
528 method_info
* info
= new(std::nothrow
) method_info
;
535 info
->add_on
= method
;
537 if (!fMethodList
.AddItem(info
)) {
542 BAutolock
locker(InputServer::gInputMethodListLocker
);
543 if (!InputServer::gInputMethodList
.AddItem(method
)) {
544 fMethodList
.RemoveItem(info
);
549 info
->image
= addOnImage
;
551 if (gInputServer
->MethodReplicant() == NULL
) {
554 if (gInputServer
->MethodReplicant()) {
555 _BMethodAddOn_
*addon
= InputServer::gKeymapMethod
.fOwner
;
560 if (gInputServer
->MethodReplicant() != NULL
) {
561 _BMethodAddOn_
*addon
= method
->fOwner
;
573 AddOnManager::_UnloadReplicant()
575 BDeskbar().RemoveItem(REPLICANT_CTL_NAME
);
580 AddOnManager::_LoadReplicant()
584 be_app
->GetAppInfo(&info
);
586 status_t err
= BDeskbar().AddItem(&info
.ref
);
588 ERROR("Deskbar refuses to add method replicant: %s\n", strerror(err
));
590 BMessage
request(B_GET_PROPERTY
);
594 request
.AddSpecifier("Messenger");
595 request
.AddSpecifier("Shelf");
597 // In the Deskbar the Shelf is in the View "Status" in Window "Deskbar"
598 request
.AddSpecifier("View", "Status");
599 request
.AddSpecifier("Window", "Deskbar");
600 to
= BMessenger("application/x-vnd.Be-TSKB", -1);
604 if (to
.SendMessage(&request
, &reply
) == B_OK
605 && reply
.FindMessenger("result", &status
) == B_OK
) {
606 // enum replicant in Status view
609 while ((uid
= _GetReplicantAt(status
, index
++)) >= B_OK
) {
610 BMessage replicantInfo
;
611 if (_GetReplicantName(status
, uid
, &replicantInfo
) != B_OK
)
615 if (replicantInfo
.FindString("result", &name
) == B_OK
616 && !strcmp(name
, REPLICANT_CTL_NAME
)) {
618 if (_GetReplicantView(status
, uid
, &replicant
) == B_OK
) {
620 if (replicant
.FindMessenger("result", &result
) == B_OK
) {
621 gInputServer
->SetMethodReplicant(new BMessenger(result
));
628 if (!gInputServer
->MethodReplicant()) {
629 ERROR("LoadReplicant(): Method replicant not found!\n");
635 AddOnManager::_GetReplicantAt(BMessenger target
, int32 index
) const
637 // So here we want to get the Unique ID of the replicant at the given index
638 // in the target Shelf.
640 BMessage
request(B_GET_PROPERTY
);// We're getting the ID property
644 request
.AddSpecifier("ID");// want the ID
645 request
.AddSpecifier("Replicant", index
);// of the index'th replicant
647 if ((err
= target
.SendMessage(&request
, &reply
)) != B_OK
)
651 if ((err
= reply
.FindInt32("result", &uid
)) != B_OK
)
659 AddOnManager::_GetReplicantName(BMessenger target
, int32 uid
,
660 BMessage
* reply
) const
662 // We send a message to the target shelf, asking it for the Name of the
663 // replicant with the given unique id.
665 BMessage
request(B_GET_PROPERTY
);
666 BMessage
uid_specifier(B_ID_SPECIFIER
);// specifying via ID
670 request
.AddSpecifier("Name");// ask for the Name of the replicant
672 // IDs are specified using code like the following 3 lines:
673 uid_specifier
.AddInt32("id", uid
);
674 uid_specifier
.AddString("property", "Replicant");
675 request
.AddSpecifier(&uid_specifier
);
677 if ((err
= target
.SendMessage(&request
, reply
)) != B_OK
)
680 if (((err
= reply
->FindInt32("error", &e
)) != B_OK
) || (e
!= B_OK
))
681 return err
? err
: e
;
688 AddOnManager::_GetReplicantView(BMessenger target
, int32 uid
,
689 BMessage
* reply
) const
691 // We send a message to the target shelf, asking it for the Name of the
692 // replicant with the given unique id.
694 BMessage
request(B_GET_PROPERTY
);
695 BMessage
uid_specifier(B_ID_SPECIFIER
);
700 request
.AddSpecifier("View");
701 // ask for the Name of the replicant
703 // IDs are specified using code like the following 3 lines:
704 uid_specifier
.AddInt32("id", uid
);
705 uid_specifier
.AddString("property", "Replicant");
706 request
.AddSpecifier(&uid_specifier
);
708 if ((err
= target
.SendMessage(&request
, reply
)) != B_OK
)
711 if (((err
= reply
->FindInt32("error", &e
)) != B_OK
) || (e
!= B_OK
))
712 return err
? err
: e
;
719 AddOnManager::_HandleStartStopDevices(BMessage
* message
, BMessage
* reply
)
721 const char *name
= NULL
;
723 if (!((message
->FindInt32("type", &type
) != B_OK
)
724 ^ (message
->FindString("device", &name
) != B_OK
)))
727 return gInputServer
->StartStopDevices(name
, (input_device_type
)type
,
728 message
->what
== IS_START_DEVICE
);
733 AddOnManager::_HandleFindDevices(BMessage
* message
, BMessage
* reply
)
736 const char *name
= NULL
;
737 input_device_type type
;
738 if (message
->FindString("device", &name
) == B_OK
) {
739 if (gInputServer
->GetDeviceInfo(name
, &type
) != B_OK
)
740 return B_NAME_NOT_FOUND
;
741 reply
->AddString("device", name
);
742 reply
->AddInt32("type", type
);
744 gInputServer
->GetDeviceInfos(reply
);
751 AddOnManager::_HandleWatchDevices(BMessage
* message
, BMessage
* reply
)
759 AddOnManager::_HandleIsDeviceRunning(BMessage
* message
, BMessage
* reply
)
763 if (message
->FindString("device", &name
) != B_OK
764 || gInputServer
->GetDeviceInfo(name
, NULL
, &running
) != B_OK
)
765 return B_NAME_NOT_FOUND
;
767 return running
? B_OK
: B_ERROR
;
772 AddOnManager::_HandleControlDevices(BMessage
* message
, BMessage
* reply
)
775 const char *name
= NULL
;
777 if (!((message
->FindInt32("type", &type
) != B_OK
)
778 ^ (message
->FindString("device", &name
) != B_OK
)))
782 BMessage controlMessage
;
783 bool hasMessage
= true;
784 if (message
->FindInt32("code", (int32
*)&code
) != B_OK
)
786 if (message
->FindMessage("message", &controlMessage
) != B_OK
)
789 return gInputServer
->ControlDevices(name
, (input_device_type
)type
,
790 code
, hasMessage
? &controlMessage
: NULL
);
795 AddOnManager::_HandleSystemShuttingDown(BMessage
* message
, BMessage
* reply
)
799 for (int32 i
= 0; i
< fDeviceList
.CountItems(); i
++) {
800 device_info
* info
= fDeviceList
.ItemAt(i
);
801 info
->add_on
->SystemShuttingDown();
809 AddOnManager::_HandleMethodReplicant(BMessage
* message
, BMessage
* reply
)
813 if (InputServer::gInputMethodList
.CountItems() == 0) {
820 BAutolock
lock(InputServer::gInputMethodListLocker
);
822 if (gInputServer
->MethodReplicant()) {
823 _BMethodAddOn_
* addon
= InputServer::gKeymapMethod
.fOwner
;
826 for (int32 i
= 0; i
< InputServer::gInputMethodList
.CountItems(); i
++) {
827 BInputServerMethod
* method
828 = (BInputServerMethod
*)InputServer::gInputMethodList
.ItemAt(i
);
830 _BMethodAddOn_
* addon
= method
->fOwner
;
840 AddOnManager::_HandleDeviceMonitor(BMessage
* message
)
843 if (message
->FindInt32("opcode", &opcode
) != B_OK
)
847 case B_ENTRY_CREATED
:
848 case B_ENTRY_REMOVED
:
851 const char* watchedPath
;
852 if (message
->FindString("watched_path", &watchedPath
) != B_OK
853 || message
->FindString("path", &path
) != B_OK
) {
856 sprintf(string
, "message does not contain all fields - "
857 "watched_path: %d, path: %d\n",
858 message
->HasString("watched_path"),
859 message
->HasString("path"));
865 // Notify all watching devices
867 for (int32 i
= 0; i
< fDeviceAddOns
.CountItems(); i
++) {
868 DeviceAddOn
* addOn
= fDeviceAddOns
.ItemAt(i
);
869 if (!addOn
->HasPath(watchedPath
))
872 addOn
->Device()->Control(NULL
, NULL
, B_NODE_MONITOR
, message
);
881 AddOnManager::_AddDevicePath(DeviceAddOn
* addOn
, const char* path
,
884 newPath
= !fDevicePaths
.HasPath(path
);
886 status_t status
= fDevicePaths
.AddPath(path
);
887 if (status
== B_OK
) {
888 status
= addOn
->AddPath(path
);
889 if (status
== B_OK
) {
890 if (!fDeviceAddOns
.HasItem(addOn
)
891 && !fDeviceAddOns
.AddItem(addOn
)) {
892 addOn
->RemovePath(path
);
893 status
= B_NO_MEMORY
;
896 fDevicePaths
.RemovePath(path
);
904 AddOnManager::_RemoveDevicePath(DeviceAddOn
* addOn
, const char* path
,
907 if (!fDevicePaths
.HasPath(path
) || !addOn
->HasPath(path
))
908 return B_ENTRY_NOT_FOUND
;
910 fDevicePaths
.RemovePath(path
);
912 lastPath
= !fDevicePaths
.HasPath(path
);
914 addOn
->RemovePath(path
);
915 if (addOn
->CountPaths() == 0)
916 fDeviceAddOns
.RemoveItem(addOn
);