⛏️ index : haiku.git

/*
 * Copyright 2008, FranΓ§ois Revol, revol@free.fr. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include "SystemInfoHandler.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <Catalog.h>
#include <Clipboard.h>
#include <Drivers.h>
#include <Handler.h>
#include <Input.h>
#include <List.h>
#include <MediaRoster.h>
#include <Messenger.h>
#include <Roster.h>

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "SystemInfoHandler"


SystemInfoHandler::SystemInfoHandler()
	: BHandler("SystemInfoHandler")
{
	fRunningApps = 0;
	fClipboardSize = 0;
	fClipboardTextSize = 0;
	fMediaNodes = 0;
	fMediaConnections = 0;
	fMediaBuffers = 0;

	fThermalFD = open("/dev/power/acpi_thermal/0", O_RDONLY);
	char buffer[256];
	if ((fThermalFD > 0) && (ioctl(fThermalFD, B_GET_DEVICE_NAME, buffer, sizeof(buffer)) == B_OK))
		fThermalZoneName = buffer;
	else
		fThermalZoneName = B_TRANSLATE("No thermal sensor");
}


SystemInfoHandler::~SystemInfoHandler()
{
	close(fThermalFD);
}


status_t
SystemInfoHandler::Archive(BMessage* data, bool deep) const
{
	// we don't want ourselves to be archived at all...
	// return BHandler::Archive(data, deep);
	return B_OK;
}


void
SystemInfoHandler::StartWatching()
{
	status_t status;
	fRunningApps = 0;
	fClipboardSize = 0;
	fClipboardTextSize = 0;
	fMediaNodes = 0;
	fMediaConnections = 0;
	fMediaBuffers = 0;

	// running applications count
	BList teamList;
	if (be_roster) {
		be_roster->StartWatching(BMessenger(this),
			B_REQUEST_LAUNCHED | B_REQUEST_QUIT);
		be_roster->GetAppList(&teamList);
		fRunningApps = teamList.CountItems();
		teamList.MakeEmpty();
	}

	// useless clipboard size
	if (be_clipboard) {
		be_clipboard->StartWatching(BMessenger(this));
		_UpdateClipboardData();
	}

	if (BMediaRoster::Roster(&status) && (status >= B_OK)) {
		BMediaRoster::Roster()->StartWatching(BMessenger(this), B_MEDIA_NODE_CREATED);
		BMediaRoster::Roster()->StartWatching(BMessenger(this), B_MEDIA_NODE_DELETED);
		BMediaRoster::Roster()->StartWatching(BMessenger(this), B_MEDIA_CONNECTION_MADE);
		BMediaRoster::Roster()->StartWatching(BMessenger(this), B_MEDIA_CONNECTION_BROKEN);
		BMediaRoster::Roster()->StartWatching(BMessenger(this), B_MEDIA_BUFFER_CREATED);
		BMediaRoster::Roster()->StartWatching(BMessenger(this), B_MEDIA_BUFFER_DELETED);
		// XXX: this won't survive a media_server restart...

		live_node_info nodeInfo; // I just need one
		int32 nodeCount = 1;
		if (BMediaRoster::Roster()->GetLiveNodes(&nodeInfo, &nodeCount)) {
			if (nodeCount > 0)
				fMediaNodes = (uint32)nodeCount;
			// TODO: retry with an array, and use GetNodeInput/Output
			// to find initial connection count
		}
		// TODO: get initial buffer count
	}

	// doesn't work on R5
	watch_input_devices(BMessenger(this), true);
}


void
SystemInfoHandler::StopWatching()
{
	status_t status;
	watch_input_devices(BMessenger(this), false);
	if (BMediaRoster::Roster(&status) && (status >= B_OK)) {
		BMediaRoster::Roster()->StopWatching(BMessenger(this), B_MEDIA_NODE_CREATED);
		BMediaRoster::Roster()->StopWatching(BMessenger(this), B_MEDIA_NODE_DELETED);
		BMediaRoster::Roster()->StopWatching(BMessenger(this), B_MEDIA_CONNECTION_MADE);
		BMediaRoster::Roster()->StopWatching(BMessenger(this), B_MEDIA_CONNECTION_BROKEN);
		BMediaRoster::Roster()->StopWatching(BMessenger(this), B_MEDIA_BUFFER_CREATED);
		BMediaRoster::Roster()->StopWatching(BMessenger(this), B_MEDIA_BUFFER_DELETED);
	}
	if (be_clipboard)
		be_clipboard->StopWatching(BMessenger(this));
	if (be_roster)
		be_roster->StopWatching(BMessenger(this));
}


void
SystemInfoHandler::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case B_SOME_APP_LAUNCHED:
			fRunningApps++;
			// TODO: maybe resync periodically in case we miss one
			break;
		case B_SOME_APP_QUIT:
			fRunningApps--;
			// TODO: maybe resync periodically in case we miss one
			break;
		case B_CLIPBOARD_CHANGED:
			_UpdateClipboardData();
			break;
		case B_MEDIA_NODE_CREATED:
			fMediaNodes++;
			break;
		case B_MEDIA_NODE_DELETED:
			fMediaNodes--;
			break;
		case B_MEDIA_CONNECTION_MADE:
			fMediaConnections++;
			break;
		case B_MEDIA_CONNECTION_BROKEN:
			fMediaConnections--;
			break;
		case B_MEDIA_BUFFER_CREATED:
			fMediaBuffers++;
			break;
		case B_MEDIA_BUFFER_DELETED:
			fMediaBuffers--;
			break;
		default:
			message->PrintToStream();
			BHandler::MessageReceived(message);
	}
}


uint32
SystemInfoHandler::RunningApps() const
{
	return fRunningApps;
}


uint32
SystemInfoHandler::ClipboardSize() const
{
	return fClipboardSize;
}


uint32
SystemInfoHandler::ClipboardTextSize() const
{
	return fClipboardTextSize;
}


uint32
SystemInfoHandler::MediaNodes() const
{
	return fMediaNodes;
}


uint32
SystemInfoHandler::MediaConnections() const
{
	return fMediaConnections;
}


uint32
SystemInfoHandler::MediaBuffers() const
{
	return fMediaBuffers;
}


float
SystemInfoHandler::Temperature() const
{
	float temperature = std::nanf("");
	if (fThermalFD >= 0) {
		char buffer[256];
		// TODO it would be nice to use ioctl to get direct access to the temperature instead,
		// but that provides raw values from the sensors without the "kelvin offset" fixup, making
		// it unusable.
		// The ioctl reply also uses a different format for each of the *_thermal drivers, so that
		// would be a problem if ActivityMonitor is extended to collect info from these as well.
		pread(fThermalFD, buffer, sizeof(buffer), 0);

		char* currTemp = strstr(buffer, "Current Temperature: ");
		if (currTemp != NULL)
			sscanf(currTemp + strlen("Current Temperature: "), "%f", &temperature);

	}

	return temperature;
}


const BString&
SystemInfoHandler::ThermalZone() const
{
	return fThermalZoneName;
}


void
SystemInfoHandler::_UpdateClipboardData()
{
	fClipboardSize = 0;
	fClipboardTextSize = 0;

	if (be_clipboard == NULL || !be_clipboard->Lock())
		return;

	BMessage* data = be_clipboard->Data();
	if (data) {
		ssize_t size = data->FlattenedSize();
		fClipboardSize = size < 0 ? 0 : (uint32)size;

		const void* text;
		ssize_t textSize;
		if (data->FindData("text/plain", B_MIME_TYPE, &text, &textSize) >= B_OK)
			fClipboardTextSize = textSize;
	}

	be_clipboard->Unlock();
}