591 lines
12 KiB
C++
591 lines
12 KiB
C++
/*
|
|
* Copyright 2001-2011, Haiku.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Pahtz <pahtz@yahoo.com.au>
|
|
* Axel Dörfler
|
|
* Stephan Aßmus <superstippi@gmx.de>
|
|
* Artur Wyszynski <harakash@gmail.com>
|
|
*/
|
|
|
|
|
|
/*! Class for low-overhead port-based messaging */
|
|
|
|
|
|
#include <LinkReceiver.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <new>
|
|
|
|
#include <ServerProtocol.h>
|
|
#include <String.h>
|
|
#include <Region.h>
|
|
#include <GradientLinear.h>
|
|
#include <GradientRadial.h>
|
|
#include <GradientRadialFocus.h>
|
|
#include <GradientDiamond.h>
|
|
#include <GradientConic.h>
|
|
|
|
#include "link_message.h"
|
|
|
|
//#define DEBUG_BPORTLINK
|
|
#ifdef DEBUG_BPORTLINK
|
|
# include <stdio.h>
|
|
# define STRACE(x) printf x
|
|
#else
|
|
# define STRACE(x) ;
|
|
#endif
|
|
|
|
//#define TRACE_LINK_RECEIVER_GRADIENTS
|
|
#ifdef TRACE_LINK_RECEIVER_GRADIENTS
|
|
# include <OS.h>
|
|
# define GTRACE(x) debug_printf x
|
|
#else
|
|
# define GTRACE(x) ;
|
|
#endif
|
|
|
|
|
|
namespace BPrivate {
|
|
|
|
|
|
LinkReceiver::LinkReceiver(port_id port)
|
|
:
|
|
fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0),
|
|
fRecvBufferSize(0), fDataSize(0),
|
|
fReplySize(0), fReadError(B_OK)
|
|
{
|
|
}
|
|
|
|
|
|
LinkReceiver::~LinkReceiver()
|
|
{
|
|
free(fRecvBuffer);
|
|
}
|
|
|
|
|
|
void
|
|
LinkReceiver::SetPort(port_id port)
|
|
{
|
|
fReceivePort = port;
|
|
}
|
|
|
|
|
|
status_t
|
|
LinkReceiver::GetNextMessage(int32 &code, bigtime_t timeout)
|
|
{
|
|
fReadError = B_OK;
|
|
|
|
int32 remaining = fDataSize - (fRecvStart + fReplySize);
|
|
STRACE(("info: LinkReceiver GetNextReply() reports %ld bytes remaining in buffer.\n", remaining));
|
|
|
|
// find the position of the next message header in the buffer
|
|
message_header *header;
|
|
if (remaining <= 0) {
|
|
status_t err = ReadFromPort(timeout);
|
|
if (err < B_OK)
|
|
return err;
|
|
remaining = fDataSize;
|
|
header = (message_header *)fRecvBuffer;
|
|
} else {
|
|
fRecvStart += fReplySize; // start of the next message
|
|
fRecvPosition = fRecvStart;
|
|
header = (message_header *)(fRecvBuffer + fRecvStart);
|
|
}
|
|
|
|
// check we have a well-formed message
|
|
if (remaining < (int32)sizeof(message_header)) {
|
|
// we don't have enough data for a complete header
|
|
STRACE(("error info: LinkReceiver remaining %ld bytes is less than header size.\n", remaining));
|
|
ResetBuffer();
|
|
return B_ERROR;
|
|
}
|
|
|
|
fReplySize = header->size;
|
|
if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) {
|
|
STRACE(("error info: LinkReceiver message size of %ld bytes smaller than header size.\n", fReplySize));
|
|
ResetBuffer();
|
|
return B_ERROR;
|
|
}
|
|
|
|
code = header->code;
|
|
fRecvPosition += sizeof(message_header);
|
|
|
|
STRACE(("info: LinkReceiver got header %ld [%ld %ld %ld] from port %ld.\n",
|
|
header->code, fReplySize, header->code, header->flags, fReceivePort));
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
bool
|
|
LinkReceiver::HasMessages() const
|
|
{
|
|
return fDataSize - (fRecvStart + fReplySize) > 0
|
|
|| port_count(fReceivePort) > 0;
|
|
}
|
|
|
|
|
|
bool
|
|
LinkReceiver::NeedsReply() const
|
|
{
|
|
if (fReplySize == 0)
|
|
return false;
|
|
|
|
message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
|
|
return (header->flags & kNeedsReply) != 0;
|
|
}
|
|
|
|
|
|
int32
|
|
LinkReceiver::Code() const
|
|
{
|
|
if (fReplySize == 0)
|
|
return B_ERROR;
|
|
|
|
message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
|
|
return header->code;
|
|
}
|
|
|
|
|
|
void
|
|
LinkReceiver::ResetBuffer()
|
|
{
|
|
fRecvPosition = 0;
|
|
fRecvStart = 0;
|
|
fDataSize = 0;
|
|
fReplySize = 0;
|
|
}
|
|
|
|
|
|
status_t
|
|
LinkReceiver::AdjustReplyBuffer(bigtime_t timeout)
|
|
{
|
|
// Here we take advantage of the compiler's dead-code elimination
|
|
if (kInitialBufferSize == kMaxBufferSize) {
|
|
// fixed buffer size
|
|
|
|
if (fRecvBuffer != NULL)
|
|
return B_OK;
|
|
|
|
fRecvBuffer = (char *)malloc(kInitialBufferSize);
|
|
if (fRecvBuffer == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
fRecvBufferSize = kInitialBufferSize;
|
|
} else {
|
|
STRACE(("info: LinkReceiver getting port_buffer_size().\n"));
|
|
|
|
ssize_t bufferSize;
|
|
do {
|
|
bufferSize = port_buffer_size_etc(fReceivePort,
|
|
timeout == B_INFINITE_TIMEOUT ? 0 : B_RELATIVE_TIMEOUT,
|
|
timeout);
|
|
} while (bufferSize == B_INTERRUPTED);
|
|
|
|
STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize));
|
|
|
|
if (bufferSize < 0)
|
|
return (status_t)bufferSize;
|
|
|
|
// make sure our receive buffer is large enough
|
|
if (bufferSize > fRecvBufferSize) {
|
|
if (bufferSize <= (ssize_t)kInitialBufferSize)
|
|
bufferSize = (ssize_t)kInitialBufferSize;
|
|
else
|
|
bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
|
|
|
|
if (bufferSize > (ssize_t)kMaxBufferSize)
|
|
return B_ERROR; // we can't continue
|
|
|
|
STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize));
|
|
char *buffer = (char *)malloc(bufferSize);
|
|
if (buffer == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
free(fRecvBuffer);
|
|
fRecvBuffer = buffer;
|
|
fRecvBufferSize = bufferSize;
|
|
}
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
LinkReceiver::ReadFromPort(bigtime_t timeout)
|
|
{
|
|
// we are here so it means we finished reading the buffer contents
|
|
ResetBuffer();
|
|
|
|
status_t err = AdjustReplyBuffer(timeout);
|
|
if (err < B_OK)
|
|
return err;
|
|
|
|
int32 code;
|
|
ssize_t bytesRead;
|
|
|
|
STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort));
|
|
while (true) {
|
|
if (timeout != B_INFINITE_TIMEOUT) {
|
|
do {
|
|
bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer,
|
|
fRecvBufferSize, B_TIMEOUT, timeout);
|
|
} while (bytesRead == B_INTERRUPTED);
|
|
} else {
|
|
do {
|
|
bytesRead = read_port(fReceivePort, &code, fRecvBuffer,
|
|
fRecvBufferSize);
|
|
} while (bytesRead == B_INTERRUPTED);
|
|
}
|
|
|
|
STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead));
|
|
if (bytesRead < B_OK)
|
|
return bytesRead;
|
|
|
|
// we just ignore incorrect messages, and don't bother our caller
|
|
|
|
if (code != kLinkCode) {
|
|
STRACE(("wrong port message %lx received.\n", code));
|
|
continue;
|
|
}
|
|
|
|
// port read seems to be valid
|
|
break;
|
|
}
|
|
|
|
fDataSize = bytesRead;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
LinkReceiver::Read(void *data, ssize_t passedSize)
|
|
{
|
|
// STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size));
|
|
ssize_t size = passedSize;
|
|
|
|
if (fReadError < B_OK)
|
|
return fReadError;
|
|
|
|
if (data == NULL || size < 1) {
|
|
fReadError = B_BAD_VALUE;
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
if (fDataSize == 0 || fReplySize == 0)
|
|
return B_NO_INIT; // need to call GetNextReply() first
|
|
|
|
bool useArea = false;
|
|
if ((size_t)size >= kMaxBufferSize) {
|
|
useArea = true;
|
|
size = sizeof(area_id);
|
|
}
|
|
|
|
if (fRecvPosition + size > fRecvStart + fReplySize) {
|
|
// reading past the end of current message
|
|
fReadError = B_BAD_VALUE;
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
if (useArea) {
|
|
area_id sourceArea;
|
|
memcpy((void*)&sourceArea, fRecvBuffer + fRecvPosition, size);
|
|
|
|
area_info areaInfo;
|
|
if (get_area_info(sourceArea, &areaInfo) < B_OK)
|
|
fReadError = B_BAD_VALUE;
|
|
|
|
if (fReadError >= B_OK) {
|
|
void* areaAddress = areaInfo.address;
|
|
|
|
if (areaAddress && sourceArea >= B_OK) {
|
|
memcpy(data, areaAddress, passedSize);
|
|
delete_area(sourceArea);
|
|
}
|
|
}
|
|
} else {
|
|
memcpy(data, fRecvBuffer + fRecvPosition, size);
|
|
}
|
|
fRecvPosition += size;
|
|
return fReadError;
|
|
}
|
|
|
|
|
|
status_t
|
|
LinkReceiver::ReadString(char** _string, size_t* _length)
|
|
{
|
|
int32 length = 0;
|
|
status_t status = Read<int32>(&length);
|
|
|
|
if (status < B_OK)
|
|
return status;
|
|
|
|
char *string;
|
|
if (length < 0) {
|
|
status = B_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
string = (char *)malloc(length + 1);
|
|
if (string == NULL) {
|
|
status = B_NO_MEMORY;
|
|
goto err;
|
|
}
|
|
|
|
if (length > 0) {
|
|
status = Read(string, length);
|
|
if (status < B_OK) {
|
|
free(string);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
// make sure the string is null terminated
|
|
string[length] = '\0';
|
|
|
|
if (_length)
|
|
*_length = length;
|
|
|
|
*_string = string;
|
|
|
|
return B_OK;
|
|
|
|
err:
|
|
fRecvPosition -= sizeof(int32);
|
|
// rewind the transaction
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
LinkReceiver::ReadString(BString &string, size_t* _length)
|
|
{
|
|
int32 length = 0;
|
|
status_t status = Read<int32>(&length);
|
|
|
|
if (status < B_OK)
|
|
return status;
|
|
|
|
if (length < 0) {
|
|
status = B_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
if (length > 0) {
|
|
char* buffer = string.LockBuffer(length + 1);
|
|
if (buffer == NULL) {
|
|
status = B_NO_MEMORY;
|
|
goto err;
|
|
}
|
|
|
|
status = Read(buffer, length);
|
|
if (status < B_OK) {
|
|
string.UnlockBuffer();
|
|
goto err;
|
|
}
|
|
|
|
// make sure the string is null terminated
|
|
buffer[length] = '\0';
|
|
string.UnlockBuffer(length);
|
|
} else
|
|
string = "";
|
|
|
|
if (_length)
|
|
*_length = length;
|
|
|
|
return B_OK;
|
|
|
|
err:
|
|
fRecvPosition -= sizeof(int32);
|
|
// rewind the transaction
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
LinkReceiver::ReadString(char *buffer, size_t bufferLength)
|
|
{
|
|
int32 length = 0;
|
|
status_t status = Read<int32>(&length);
|
|
|
|
if (status < B_OK)
|
|
return status;
|
|
|
|
if (length >= (int32)bufferLength) {
|
|
status = B_BUFFER_OVERFLOW;
|
|
goto err;
|
|
}
|
|
|
|
if (length < 0) {
|
|
status = B_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
if (length > 0) {
|
|
status = Read(buffer, length);
|
|
if (status < B_OK)
|
|
goto err;
|
|
}
|
|
|
|
// make sure the string is null terminated
|
|
buffer[length] = '\0';
|
|
return B_OK;
|
|
|
|
err:
|
|
fRecvPosition -= sizeof(int32);
|
|
// rewind the transaction
|
|
return status;
|
|
}
|
|
|
|
status_t
|
|
LinkReceiver::ReadRegion(BRegion* region)
|
|
{
|
|
status_t status = Read(®ion->fCount, sizeof(int32));
|
|
if (status >= B_OK)
|
|
status = Read(®ion->fBounds, sizeof(clipping_rect));
|
|
if (status >= B_OK) {
|
|
if (!region->_SetSize(region->fCount))
|
|
status = B_NO_MEMORY;
|
|
else {
|
|
status = Read(region->fData,
|
|
region->fCount * sizeof(clipping_rect));
|
|
}
|
|
if (status < B_OK)
|
|
region->MakeEmpty();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
static BGradient*
|
|
gradient_for_type(BGradient::Type type)
|
|
{
|
|
switch (type) {
|
|
case BGradient::TYPE_LINEAR:
|
|
return new (std::nothrow) BGradientLinear();
|
|
case BGradient::TYPE_RADIAL:
|
|
return new (std::nothrow) BGradientRadial();
|
|
case BGradient::TYPE_RADIAL_FOCUS:
|
|
return new (std::nothrow) BGradientRadialFocus();
|
|
case BGradient::TYPE_DIAMOND:
|
|
return new (std::nothrow) BGradientDiamond();
|
|
case BGradient::TYPE_CONIC:
|
|
return new (std::nothrow) BGradientConic();
|
|
case BGradient::TYPE_NONE:
|
|
return new (std::nothrow) BGradient();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
status_t
|
|
LinkReceiver::ReadGradient(BGradient** _gradient)
|
|
{
|
|
GTRACE(("LinkReceiver::ReadGradient\n"));
|
|
|
|
BGradient::Type gradientType;
|
|
int32 colorsCount;
|
|
Read(&gradientType, sizeof(BGradient::Type));
|
|
status_t status = Read(&colorsCount, sizeof(int32));
|
|
if (status != B_OK)
|
|
return status;
|
|
|
|
BGradient* gradient = gradient_for_type(gradientType);
|
|
if (!gradient)
|
|
return B_NO_MEMORY;
|
|
|
|
*_gradient = gradient;
|
|
|
|
if (colorsCount > 0) {
|
|
BGradient::ColorStop stop;
|
|
for (int i = 0; i < colorsCount; i++) {
|
|
if ((status = Read(&stop, sizeof(BGradient::ColorStop))) != B_OK)
|
|
return status;
|
|
if (!gradient->AddColorStop(stop, i))
|
|
return B_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
switch (gradientType) {
|
|
case BGradient::TYPE_LINEAR:
|
|
{
|
|
GTRACE(("LinkReceiver::ReadGradient> type == TYPE_LINEAR\n"));
|
|
BGradientLinear* linear = (BGradientLinear*)gradient;
|
|
BPoint start;
|
|
BPoint end;
|
|
Read(&start, sizeof(BPoint));
|
|
if ((status = Read(&end, sizeof(BPoint))) != B_OK)
|
|
return status;
|
|
linear->SetStart(start);
|
|
linear->SetEnd(end);
|
|
return B_OK;
|
|
}
|
|
case BGradient::TYPE_RADIAL:
|
|
{
|
|
GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL\n"));
|
|
BGradientRadial* radial = (BGradientRadial*)gradient;
|
|
BPoint center;
|
|
float radius;
|
|
Read(¢er, sizeof(BPoint));
|
|
if ((status = Read(&radius, sizeof(float))) != B_OK)
|
|
return status;
|
|
radial->SetCenter(center);
|
|
radial->SetRadius(radius);
|
|
return B_OK;
|
|
}
|
|
case BGradient::TYPE_RADIAL_FOCUS:
|
|
{
|
|
GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL_FOCUS\n"));
|
|
BGradientRadialFocus* radialFocus =
|
|
(BGradientRadialFocus*)gradient;
|
|
BPoint center;
|
|
BPoint focal;
|
|
float radius;
|
|
Read(¢er, sizeof(BPoint));
|
|
Read(&focal, sizeof(BPoint));
|
|
if ((status = Read(&radius, sizeof(float))) != B_OK)
|
|
return status;
|
|
radialFocus->SetCenter(center);
|
|
radialFocus->SetFocal(focal);
|
|
radialFocus->SetRadius(radius);
|
|
return B_OK;
|
|
}
|
|
case BGradient::TYPE_DIAMOND:
|
|
{
|
|
GTRACE(("LinkReceiver::ReadGradient> type == TYPE_DIAMOND\n"));
|
|
BGradientDiamond* diamond = (BGradientDiamond*)gradient;
|
|
BPoint center;
|
|
if ((status = Read(¢er, sizeof(BPoint))) != B_OK)
|
|
return status;
|
|
diamond->SetCenter(center);
|
|
return B_OK;
|
|
}
|
|
case BGradient::TYPE_CONIC:
|
|
{
|
|
GTRACE(("LinkReceiver::ReadGradient> type == TYPE_CONIC\n"));
|
|
BGradientConic* conic = (BGradientConic*)gradient;
|
|
BPoint center;
|
|
float angle;
|
|
Read(¢er, sizeof(BPoint));
|
|
if ((status = Read(&angle, sizeof(float))) != B_OK)
|
|
return status;
|
|
conic->SetCenter(center);
|
|
conic->SetAngle(angle);
|
|
return B_OK;
|
|
}
|
|
case BGradient::TYPE_NONE:
|
|
{
|
|
GTRACE(("LinkReceiver::ReadGradient> type == TYPE_NONE\n"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return B_ERROR;
|
|
}
|
|
|
|
|
|
} // namespace BPrivate
|