* Copyright 2024-2025, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "PackageScreenshotRepository.h"
#include <unistd.h>
#include <AutoDeleter.h>
#include <TranslationUtils.h>
#include "FileIO.h"
#include "Logger.h"
#include "Model.h"
#include "StorageUtils.h"
#include "WebAppInterface.h"
static const uint32 kMaxRetainedCachedScreenshots = 25;
PackageScreenshotRepository::PackageScreenshotRepository(
PackageScreenshotRepositoryListenerRef listener, Model* model)
:
fListener(listener),
fModel(model)
{
_Init();
}
PackageScreenshotRepository::~PackageScreenshotRepository()
{
fListener.Unset();
_CleanCache();
}
the cache. It will load the data into a file before loading it in order to
avoid the data needing to be resident in memory twice; thus saving memory
use.
*/
status_t
PackageScreenshotRepository::LoadScreenshot(const ScreenshotCoordinate& coord,
BitmapHolderRef& bitmapHolderRef)
{
if (!coord.IsValid())
return B_BAD_VALUE;
BPath temporaryFilePath(tmpnam(NULL), NULL, true);
status_t result = _DownloadToLocalFile(coord, temporaryFilePath);
const char* temporaryFilePathStr = temporaryFilePath.Path();
if (result == B_OK) {
FILE* file = fopen(temporaryFilePathStr, "rb");
if (file == NULL) {
HDERROR("unable to open the screenshot file for read at [%s]", temporaryFilePathStr);
result = B_IO_ERROR;
}
if (result == B_OK) {
BFileIO fileIo(file, true);
BBitmap* bitmap = BTranslationUtils::GetBitmap(&fileIo);
if (bitmap == NULL)
return B_IO_ERROR;
bitmapHolderRef.SetTo(new(std::nothrow) BitmapHolder(bitmap), true);
}
}
if (remove(temporaryFilePathStr) != 0)
HDERROR("unable to delete the temporary file [%s]", temporaryFilePathStr);
return result;
}
status_t
PackageScreenshotRepository::CacheAndLoadScreenshot(const ScreenshotCoordinate& coord,
BitmapHolderRef& bitmapHolderRef)
{
if (!coord.IsValid())
return B_BAD_VALUE;
CacheScreenshot(coord);
BPositionIO* data = NULL;
status_t result = _CreateCachedData(coord, &data);
if (result == B_OK) {
ObjectDeleter<BPositionIO> dataDeleter(data);
BBitmap* bitmap = BTranslationUtils::GetBitmap(data);
if (bitmap == NULL)
return B_IO_ERROR;
bitmapHolderRef.SetTo(new(std::nothrow) BitmapHolder(bitmap), true);
}
return result;
}
status_t
PackageScreenshotRepository::HasCachedScreenshot(const ScreenshotCoordinate& coord, bool* value)
{
if (value == NULL)
debugger("expected the value to be supplied");
*value = false;
if (!coord.IsValid())
return B_BAD_VALUE;
BPath path = _DeriveCachePath(coord);
const char* pathStr = path.Path();
BEntry entry(pathStr);
struct stat s = {};
status_t result = entry.GetStat(&s);
switch (result) {
case B_ENTRY_NOT_FOUND:
*value = false;
return B_OK;
case B_OK:
*value = (s.st_size > 0);
return B_OK;
default:
return result;
}
}
status_t
PackageScreenshotRepository::CacheScreenshot(const ScreenshotCoordinate& coord)
{
if (!coord.IsValid())
return B_BAD_VALUE;
bool present = false;
status_t result = HasCachedScreenshot(coord, &present);
if (result == B_OK && present)
return result;
if (result == B_OK)
result = _DownloadToLocalFile(coord, _DeriveCachePath(coord));
return result;
}
status_t
PackageScreenshotRepository::_Init()
{
fBaseDirectory.Unset();
status_t result = StorageUtils::LocalWorkingDirectoryPath("screenshot_cache", fBaseDirectory);
if (B_OK != result)
HDERROR("unable to setup the cache directory");
_CleanCache();
return result;
}
most recent files are retained and the rest are deleted from the disk.
*/
status_t
PackageScreenshotRepository::_CleanCache()
{
HDINFO("will clean the screenshot cache");
return StorageUtils::RemoveDirectoryContentsRetainingLatestFiles(fBaseDirectory,
kMaxRetainedCachedScreenshots);
}
status_t
PackageScreenshotRepository::_DownloadToLocalFile(const ScreenshotCoordinate& coord,
const BPath& path)
{
const char* pathStr = path.Path();
FILE* file = fopen(pathStr, "wb");
if (file == NULL) {
HDERROR("unable to open the screenshot file for writing at [%s]", pathStr);
return B_IO_ERROR;
}
BFileIO outputDataStream(file, true);
status_t result = _WebApp()->RetrieveScreenshot(coord.Code(), coord.Width(), coord.Height(),
&outputDataStream);
if (result == B_OK)
result = outputDataStream.Flush();
if (result == B_OK)
fListener->ScreenshotCached(coord);
return result;
}
BPath
PackageScreenshotRepository::_DeriveCachePath(const ScreenshotCoordinate& coord) const
{
BPath path(fBaseDirectory);
path.Append(coord.CacheFilename());
return path;
}
status_t
PackageScreenshotRepository::_CreateCachedData(const ScreenshotCoordinate& coord,
BPositionIO** data)
{
status_t result = B_OK;
BPath path = _DeriveCachePath(coord);
const char* pathStr = path.Path();
FILE* file = fopen(pathStr, "rb");
if (file == NULL) {
HDERROR("unable to open the screenshot file for read at [%s]", pathStr);
result = B_IO_ERROR;
}
if (result == B_OK)
*data = new BFileIO(file, true);
return result;
}
WebAppInterfaceRef
PackageScreenshotRepository::_WebApp()
{
return fModel->WebApp();
}