/*
* Copyright 2004-2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Jérôme Duval
* Marcus Overhagen
* John Scipione, jscipione@gmail.com
*/
//! Manager for input_server add-ons (devices, filters, methods)
#include "AddOnManager.h"
#include <stdio.h>
#include <string.h>
#include <Autolock.h>
#include <Deskbar.h>
#include <Directory.h>
#include <Entry.h>
#include <image.h>
#include <Path.h>
#include <Roster.h>
#include <String.h>
#include <PathMonitor.h>
#include "InputServer.h"
#include "InputServerTypes.h"
#include "MethodReplicant.h"
#undef TRACE
//#define TRACE_ADD_ON_MONITOR
#ifdef TRACE_ADD_ON_MONITOR
# define TRACE(x...) debug_printf(x)
# define ERROR(x...) debug_printf(x)
#else
# define TRACE(x...) ;
// TODO: probably better to the syslog
# define ERROR(x...) debug_printf(x)
#endif
class AddOnManager::MonitorHandler : public AddOnMonitorHandler {
public:
MonitorHandler(AddOnManager* manager)
{
fManager = manager;
}
virtual void AddOnEnabled(const add_on_entry_info* entryInfo)
{
CALLED();
entry_ref ref;
make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node,
entryInfo->name, &ref);
BEntry entry(&ref, false);
fManager->_RegisterAddOn(entry);
}
virtual void AddOnDisabled(const add_on_entry_info* entryInfo)
{
CALLED();
entry_ref ref;
make_entry_ref(entryInfo->dir_nref.device, entryInfo->dir_nref.node,
entryInfo->name, &ref);
BEntry entry(&ref, false);
fManager->_UnregisterAddOn(entry);
}
private:
AddOnManager* fManager;
};
// #pragma mark -
template<class T> T*
instantiate_add_on(image_id image, const char* path, const char* type)
{
T* (*instantiateFunction)();
BString functionName = "instantiate_input_";
functionName += type;
if (get_image_symbol(image, functionName.String(), B_SYMBOL_TYPE_TEXT,
(void**)&instantiateFunction) < B_OK) {
ERROR("AddOnManager::_RegisterAddOn(): can't find %s() in \"%s\"\n",
functionName.String(), path);
return NULL;
}
T* addOn = (*instantiateFunction)();
if (addOn == NULL) {
ERROR("AddOnManager::_RegisterAddOn(): %s() in \"%s\" returned "
"NULL\n", functionName.String(), path);
return NULL;
}
status_t status = addOn->InitCheck();
if (status != B_OK) {
ERROR("AddOnManager::_RegisterAddOn(): InitCheck() in \"%s\" "
"returned %s\n", path, strerror(status));
delete addOn;
return NULL;
}
return addOn;
}
// #pragma mark - AddOnManager
AddOnManager::AddOnManager()
:
AddOnMonitor(),
fHandler(new(std::nothrow) MonitorHandler(this))
{
SetHandler(fHandler);
}
AddOnManager::~AddOnManager()
{
delete fHandler;
}
void
AddOnManager::MessageReceived(BMessage* message)
{
CALLED();
BMessage reply;
status_t status;
TRACE("%s what: %.4s\n", __PRETTY_FUNCTION__, (char*)&message->what);
switch (message->what) {
case IS_FIND_DEVICES:
status = _HandleFindDevices(message, &reply);
break;
case IS_WATCH_DEVICES:
status = _HandleWatchDevices(message, &reply);
break;
case IS_IS_DEVICE_RUNNING:
status = _HandleIsDeviceRunning(message, &reply);
break;
case IS_START_DEVICE:
status = _HandleStartStopDevices(message, &reply);
break;
case IS_STOP_DEVICE:
status = _HandleStartStopDevices(message, &reply);
break;
case IS_CONTROL_DEVICES:
status = _HandleControlDevices(message, &reply);
break;
case SYSTEM_SHUTTING_DOWN:
status = _HandleSystemShuttingDown(message, &reply);
break;
case IS_METHOD_REGISTER:
status = _HandleMethodReplicant(message, &reply);
break;
case B_PATH_MONITOR:
_HandleDeviceMonitor(message);
return;
default:
AddOnMonitor::MessageReceived(message);
return;
}
reply.AddInt32("status", status);
message->SendReply(&reply);
}
void
AddOnManager::LoadState()
{
_RegisterAddOns();
}
void
AddOnManager::SaveState()
{
CALLED();
_UnregisterAddOns();
}
status_t
AddOnManager::StartMonitoringDevice(DeviceAddOn* addOn, const char* device)
{
CALLED();
BString path;
if (device[0] != '/')
path = "/dev/";
path += device;
TRACE("AddOnMonitor::StartMonitoringDevice(%s)\n", path.String());
bool newPath;
status_t status = _AddDevicePath(addOn, path.String(), newPath);
if (status == B_OK && newPath) {
status = BPathMonitor::StartWatching(path.String(),
B_WATCH_FILES_ONLY | B_WATCH_RECURSIVELY, this);
if (status != B_OK) {
bool lastPath;
_RemoveDevicePath(addOn, path.String(), lastPath);
}
}
return status;
}
status_t
AddOnManager::StopMonitoringDevice(DeviceAddOn* addOn, const char *device)
{
CALLED();
BString path;
if (device[0] != '/')
path = "/dev/";
path += device;
TRACE("AddOnMonitor::StopMonitoringDevice(%s)\n", path.String());
bool lastPath;
status_t status = _RemoveDevicePath(addOn, path.String(), lastPath);
if (status == B_OK && lastPath)
BPathMonitor::StopWatching(path.String(), this);
return status;
}
// #pragma mark -
void
AddOnManager::_RegisterAddOns()
{
CALLED();
BAutolock locker(this);
fHandler->AddAddOnDirectories("input_server/devices");
fHandler->AddAddOnDirectories("input_server/filters");
fHandler->AddAddOnDirectories("input_server/methods");
}
void
AddOnManager::_UnregisterAddOns()
{
BAutolock locker(this);
// We have to stop manually the add-ons because the monitor doesn't
// disable them on exit
while (device_info* info = fDeviceList.RemoveItemAt(0)) {
gInputServer->StartStopDevices(*info->add_on, false);
delete info;
}
// TODO: what about the filters/methods lists in the input_server?
while (filter_info* info = fFilterList.RemoveItemAt(0)) {
delete info;
}
while (method_info* info = fMethodList.RemoveItemAt(0)) {
delete info;
}
}
bool
AddOnManager::_IsDevice(const char* path) const
{
return strstr(path, "input_server/devices") != 0;
}
bool
AddOnManager::_IsFilter(const char* path) const
{
return strstr(path, "input_server/filters") != 0;
}
bool
AddOnManager::_IsMethod(const char* path) const
{
return strstr(path, "input_server/methods") != 0;
}
status_t
AddOnManager::_RegisterAddOn(BEntry& entry)
{
BPath path(&entry);
entry_ref ref;
status_t status = entry.GetRef(&ref);
if (status < B_OK)
return status;
TRACE("AddOnManager::RegisterAddOn(): trying to load \"%s\"\n",
path.Path());
image_id image = load_add_on(path.Path());
if (image < B_OK) {
ERROR("load addon %s failed\n", path.Path());
return image;
}
status = B_ERROR;
if (_IsDevice(path.Path())) {
BInputServerDevice* device = instantiate_add_on<BInputServerDevice>(
image, path.Path(), "device");
if (device != NULL)
status = _RegisterDevice(device, ref, image);
} else if (_IsFilter(path.Path())) {
BInputServerFilter* filter = instantiate_add_on<BInputServerFilter>(
image, path.Path(), "filter");
if (filter != NULL)
status = _RegisterFilter(filter, ref, image);
} else if (_IsMethod(path.Path())) {
BInputServerMethod* method = instantiate_add_on<BInputServerMethod>(
image, path.Path(), "method");
if (method != NULL)
status = _RegisterMethod(method, ref, image);
} else {
ERROR("AddOnManager::_RegisterAddOn(): addon type not found for "
"\"%s\" \n", path.Path());
}
if (status != B_OK)
unload_add_on(image);
return status;
}
status_t
AddOnManager::_UnregisterAddOn(BEntry& entry)
{
BPath path(&entry);
entry_ref ref;
status_t status = entry.GetRef(&ref);
if (status < B_OK)
return status;
TRACE("AddOnManager::UnregisterAddOn(): trying to unload \"%s\"\n",
path.Path());
BAutolock _(this);
if (_IsDevice(path.Path())) {
for (int32 i = fDeviceList.CountItems(); i-- > 0;) {
device_info* info = fDeviceList.ItemAt(i);
if (!strcmp(info->ref.name, ref.name)) {
gInputServer->StartStopDevices(*info->add_on, false);
delete fDeviceList.RemoveItemAt(i);
break;
}
}
} else if (_IsFilter(path.Path())) {
for (int32 i = fFilterList.CountItems(); i-- > 0;) {
filter_info* info = fFilterList.ItemAt(i);
if (!strcmp(info->ref.name, ref.name)) {
BAutolock locker(InputServer::gInputFilterListLocker);
InputServer::gInputFilterList.RemoveItem(info->add_on);
delete fFilterList.RemoveItemAt(i);
break;
}
}
} else if (_IsMethod(path.Path())) {
BInputServerMethod* method = NULL;
for (int32 i = fMethodList.CountItems(); i-- > 0;) {
method_info* info = fMethodList.ItemAt(i);
if (!strcmp(info->ref.name, ref.name)) {
BAutolock locker(InputServer::gInputMethodListLocker);
InputServer::gInputMethodList.RemoveItem(info->add_on);
method = info->add_on;
// this will only be used as a cookie, and not referenced
// anymore
delete fMethodList.RemoveItemAt(i);
break;
}
}
if (fMethodList.CountItems() <= 0) {
// we remove the method replicant
BDeskbar().RemoveItem(REPLICANT_CTL_NAME);
gInputServer->SetMethodReplicant(NULL);
} else if (method != NULL) {
BMessage msg(IS_REMOVE_METHOD);
msg.AddInt32("cookie", method->fOwner->Cookie());
if (gInputServer->MethodReplicant())
gInputServer->MethodReplicant()->SendMessage(&msg);
}
}
return B_OK;
}
//! Takes over ownership of the \a device, regardless of success.
status_t
AddOnManager::_RegisterDevice(BInputServerDevice* device, const entry_ref& ref,
image_id addOnImage)
{
BAutolock locker(this);
for (int32 i = fDeviceList.CountItems(); i-- > 0;) {
device_info* info = fDeviceList.ItemAt(i);
if (!strcmp(info->ref.name, ref.name)) {
// we already know this device
delete device;
return B_NAME_IN_USE;
}
}
TRACE("AddOnManager::RegisterDevice, name %s\n", ref.name);
device_info* info = new(std::nothrow) device_info;
if (info == NULL) {
delete device;
return B_NO_MEMORY;
}
info->ref = ref;
info->add_on = device;
if (!fDeviceList.AddItem(info)) {
delete info;
return B_NO_MEMORY;
}
info->image = addOnImage;
return B_OK;
}
//! Takes over ownership of the \a filter, regardless of success.
status_t
AddOnManager::_RegisterFilter(BInputServerFilter* filter, const entry_ref& ref,
image_id addOnImage)
{
BAutolock _(this);
for (int32 i = fFilterList.CountItems(); i-- > 0;) {
filter_info* info = fFilterList.ItemAt(i);
if (strcmp(info->ref.name, ref.name) == 0) {
// we already know this ref
delete filter;
return B_NAME_IN_USE;
}
}
TRACE("%s, name %s\n", __PRETTY_FUNCTION__, ref.name);
filter_info* info = new(std::nothrow) filter_info;
if (info == NULL) {
delete filter;
return B_NO_MEMORY;
}
info->ref = ref;
info->add_on = filter;
if (!fFilterList.AddItem(info)) {
delete info;
return B_NO_MEMORY;
}
BAutolock locker(InputServer::gInputFilterListLocker);
if (!InputServer::gInputFilterList.AddItem(filter)) {
fFilterList.RemoveItem(info, false);
delete info;
return B_NO_MEMORY;
}
info->image = addOnImage;
return B_OK;
}
//! Takes over ownership of the \a method, regardless of success.
status_t
AddOnManager::_RegisterMethod(BInputServerMethod* method, const entry_ref& ref,
image_id addOnImage)
{
BAutolock _(this);
for (int32 i = fMethodList.CountItems(); i-- > 0;) {
method_info* info = fMethodList.ItemAt(i);
if (!strcmp(info->ref.name, ref.name)) {
// we already know this ref
delete method;
return B_NAME_IN_USE;
}
}
TRACE("%s, name %s\n", __PRETTY_FUNCTION__, ref.name);
method_info* info = new(std::nothrow) method_info;
if (info == NULL) {
delete method;
return B_NO_MEMORY;
}
info->ref = ref;
info->add_on = method;
if (!fMethodList.AddItem(info)) {
delete info;
return B_NO_MEMORY;
}
BAutolock locker(InputServer::gInputMethodListLocker);
if (!InputServer::gInputMethodList.AddItem(method)) {
fMethodList.RemoveItem(info);
delete info;
return B_NO_MEMORY;
}
info->image = addOnImage;
if (gInputServer->MethodReplicant() == NULL) {
_LoadReplicant();
if (gInputServer->MethodReplicant()) {
_BMethodAddOn_ *addon = InputServer::gKeymapMethod.fOwner;
addon->AddMethod();
}
}
if (gInputServer->MethodReplicant() != NULL) {
_BMethodAddOn_ *addon = method->fOwner;
addon->AddMethod();
}
return B_OK;
}
// #pragma mark -
void
AddOnManager::_UnloadReplicant()
{
BDeskbar().RemoveItem(REPLICANT_CTL_NAME);
}
void
AddOnManager::_LoadReplicant()
{
CALLED();
app_info info;
be_app->GetAppInfo(&info);
status_t err = BDeskbar().AddItem(&info.ref);
if (err != B_OK)
ERROR("Deskbar refuses to add method replicant: %s\n", strerror(err));
BMessage request(B_GET_PROPERTY);
BMessenger to;
BMessenger status;
request.AddSpecifier("Messenger");
request.AddSpecifier("Shelf");
// In the Deskbar the Shelf is in the View "Status" in Window "Deskbar"
request.AddSpecifier("View", "Status");
request.AddSpecifier("Window", "Deskbar");
to = BMessenger("application/x-vnd.Be-TSKB", -1);
BMessage reply;
if (to.SendMessage(&request, &reply) == B_OK
&& reply.FindMessenger("result", &status) == B_OK) {
// enum replicant in Status view
int32 index = 0;
int32 uid;
while ((uid = _GetReplicantAt(status, index++)) >= B_OK) {
BMessage replicantInfo;
if (_GetReplicantName(status, uid, &replicantInfo) != B_OK)
continue;
const char *name;
if (replicantInfo.FindString("result", &name) == B_OK
&& !strcmp(name, REPLICANT_CTL_NAME)) {
BMessage replicant;
if (_GetReplicantView(status, uid, &replicant) == B_OK) {
BMessenger result;
if (replicant.FindMessenger("result", &result) == B_OK) {
gInputServer->SetMethodReplicant(new BMessenger(result));
}
}
}
}
}
if (!gInputServer->MethodReplicant()) {
ERROR("LoadReplicant(): Method replicant not found!\n");
}
}
int32
AddOnManager::_GetReplicantAt(BMessenger target, int32 index) const
{
// So here we want to get the Unique ID of the replicant at the given index
// in the target Shelf.
BMessage request(B_GET_PROPERTY);// We're getting the ID property
BMessage reply;
status_t err;
request.AddSpecifier("ID");// want the ID
request.AddSpecifier("Replicant", index);// of the index'th replicant
if ((err = target.SendMessage(&request, &reply)) != B_OK)
return err;
int32 uid;
if ((err = reply.FindInt32("result", &uid)) != B_OK)
return err;
return uid;
}
status_t
AddOnManager::_GetReplicantName(BMessenger target, int32 uid,
BMessage* reply) const
{
// We send a message to the target shelf, asking it for the Name of the
// replicant with the given unique id.
BMessage request(B_GET_PROPERTY);
BMessage uid_specifier(B_ID_SPECIFIER);// specifying via ID
status_t err;
status_t e;
request.AddSpecifier("Name");// ask for the Name of the replicant
// IDs are specified using code like the following 3 lines:
uid_specifier.AddInt32("id", uid);
uid_specifier.AddString("property", "Replicant");
request.AddSpecifier(&uid_specifier);
if ((err = target.SendMessage(&request, reply)) != B_OK)
return err;
if (((err = reply->FindInt32("error", &e)) != B_OK) || (e != B_OK))
return err ? err : e;
return B_OK;
}
status_t
AddOnManager::_GetReplicantView(BMessenger target, int32 uid,
BMessage* reply) const
{
// We send a message to the target shelf, asking it for the Name of the
// replicant with the given unique id.
BMessage request(B_GET_PROPERTY);
BMessage uid_specifier(B_ID_SPECIFIER);
// specifying via ID
status_t err;
status_t e;
request.AddSpecifier("View");
// ask for the Name of the replicant
// IDs are specified using code like the following 3 lines:
uid_specifier.AddInt32("id", uid);
uid_specifier.AddString("property", "Replicant");
request.AddSpecifier(&uid_specifier);
if ((err = target.SendMessage(&request, reply)) != B_OK)
return err;
if (((err = reply->FindInt32("error", &e)) != B_OK) || (e != B_OK))
return err ? err : e;
return B_OK;
}
status_t
AddOnManager::_HandleStartStopDevices(BMessage* message, BMessage* reply)
{
const char *name = NULL;
int32 type = 0;
if (!((message->FindInt32("type", &type) != B_OK)
^ (message->FindString("device", &name) != B_OK)))
return B_ERROR;
return gInputServer->StartStopDevices(name, (input_device_type)type,
message->what == IS_START_DEVICE);
}
status_t
AddOnManager::_HandleFindDevices(BMessage* message, BMessage* reply)
{
CALLED();
const char *name = NULL;
input_device_type type;
if (message->FindString("device", &name) == B_OK) {
if (gInputServer->GetDeviceInfo(name, &type) != B_OK)
return B_NAME_NOT_FOUND;
reply->AddString("device", name);
reply->AddInt32("type", type);
} else {
gInputServer->GetDeviceInfos(reply);
}
return B_OK;
}
status_t
AddOnManager::_HandleWatchDevices(BMessage* message, BMessage* reply)
{
// TODO
return B_OK;
}
status_t
AddOnManager::_HandleIsDeviceRunning(BMessage* message, BMessage* reply)
{
const char* name;
bool running;
if (message->FindString("device", &name) != B_OK
|| gInputServer->GetDeviceInfo(name, NULL, &running) != B_OK)
return B_NAME_NOT_FOUND;
return running ? B_OK : B_ERROR;
}
status_t
AddOnManager::_HandleControlDevices(BMessage* message, BMessage* reply)
{
CALLED();
const char *name = NULL;
int32 type = 0;
if (!((message->FindInt32("type", &type) != B_OK)
^ (message->FindString("device", &name) != B_OK)))
return B_BAD_VALUE;
uint32 code = 0;
BMessage controlMessage;
bool hasMessage = true;
if (message->FindInt32("code", (int32*)&code) != B_OK)
return B_BAD_VALUE;
if (message->FindMessage("message", &controlMessage) != B_OK)
hasMessage = false;
return gInputServer->ControlDevices(name, (input_device_type)type,
code, hasMessage ? &controlMessage : NULL);
}
status_t
AddOnManager::_HandleSystemShuttingDown(BMessage* message, BMessage* reply)
{
CALLED();
for (int32 i = 0; i < fDeviceList.CountItems(); i++) {
device_info* info = fDeviceList.ItemAt(i);
info->add_on->SystemShuttingDown();
}
return B_OK;
}
status_t
AddOnManager::_HandleMethodReplicant(BMessage* message, BMessage* reply)
{
CALLED();
if (InputServer::gInputMethodList.CountItems() == 0) {
_UnloadReplicant();
return B_OK;
}
_LoadReplicant();
BAutolock lock(InputServer::gInputMethodListLocker);
if (gInputServer->MethodReplicant()) {
_BMethodAddOn_* addon = InputServer::gKeymapMethod.fOwner;
addon->AddMethod();
for (int32 i = 0; i < InputServer::gInputMethodList.CountItems(); i++) {
BInputServerMethod* method
= (BInputServerMethod*)InputServer::gInputMethodList.ItemAt(i);
_BMethodAddOn_* addon = method->fOwner;
addon->AddMethod();
}
}
return B_OK;
}
void
AddOnManager::_HandleDeviceMonitor(BMessage* message)
{
int32 opcode;
if (message->FindInt32("opcode", &opcode) != B_OK)
return;
switch (opcode) {
case B_ENTRY_CREATED:
case B_ENTRY_REMOVED:
{
const char* path;
const char* watchedPath;
if (message->FindString("watched_path", &watchedPath) != B_OK
|| message->FindString("path", &path) != B_OK) {
#if DEBUG
char string[1024];
sprintf(string, "message does not contain all fields - "
"watched_path: %d, path: %d\n",
message->HasString("watched_path"),
message->HasString("path"));
debugger(string);
#endif
return;
}
// Notify all watching devices
for (int32 i = 0; i < fDeviceAddOns.CountItems(); i++) {
DeviceAddOn* addOn = fDeviceAddOns.ItemAt(i);
if (!addOn->HasPath(watchedPath))
continue;
addOn->Device()->Control(NULL, NULL, B_NODE_MONITOR, message);
}
break;
}
}
}
status_t
AddOnManager::_AddDevicePath(DeviceAddOn* addOn, const char* path,
bool& newPath)
{
newPath = !fDevicePaths.HasPath(path);
status_t status = fDevicePaths.AddPath(path);
if (status == B_OK) {
status = addOn->AddPath(path);
if (status == B_OK) {
if (!fDeviceAddOns.HasItem(addOn)
&& !fDeviceAddOns.AddItem(addOn)) {
addOn->RemovePath(path);
status = B_NO_MEMORY;
}
} else
fDevicePaths.RemovePath(path);
}
return status;
}
status_t
AddOnManager::_RemoveDevicePath(DeviceAddOn* addOn, const char* path,
bool& lastPath)
{
if (!fDevicePaths.HasPath(path) || !addOn->HasPath(path))
return B_ENTRY_NOT_FOUND;
fDevicePaths.RemovePath(path);
lastPath = !fDevicePaths.HasPath(path);
addOn->RemovePath(path);
if (addOn->CountPaths() == 0)
fDeviceAddOns.RemoveItem(addOn);
return B_OK;
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fSafeMode.