#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <new>
#include <Gradient.h>
#include <GradientLinear.h>
#include <GradientRadial.h>
#include <GradientRadialFocus.h>
#include <GradientDiamond.h>
#include <GradientConic.h>
#include <Region.h>
#include <Shape.h>
#include <ShapePrivate.h>
#include <ServerProtocol.h>
#include <LinkSender.h>
#include "link_message.h"
#include "syscalls.h"
#ifdef DEBUG_BPORTLINK
# include <stdio.h>
# define STRACE(x) printf x
#else
# define STRACE(x) ;
#endif
#ifdef TRACE_SERVER_LINK_GRADIENTS
# include <OS.h>
# define GTRACE(x) debug_printf x
#else
# define GTRACE(x) ;
#endif
static const size_t kMaxStringSize = 4096;
static const size_t kWatermark = kInitialBufferSize - 24;
namespace BPrivate {
LinkSender::LinkSender(port_id port)
:
fPort(port),
fTargetTeam(-1),
fBuffer(NULL),
fBufferSize(0),
fCurrentEnd(0),
fCurrentStart(0),
fCurrentStatus(B_OK)
{
}
LinkSender::~LinkSender()
{
free(fBuffer);
}
void
LinkSender::SetPort(port_id port)
{
fPort = port;
}
status_t
LinkSender::StartMessage(int32 code, size_t minSize)
{
if (EndMessage() < B_OK)
CancelMessage();
if (minSize > kMaxBufferSize - sizeof(message_header)) {
minSize = sizeof(area_id);
}
minSize += sizeof(message_header);
if (fBufferSize > 0 && (minSize > SpaceLeft() || fCurrentStart >= kWatermark)) {
status_t status = Flush();
if (status < B_OK)
return status;
}
if (minSize > fBufferSize) {
if (AdjustBuffer(minSize) != B_OK)
return fCurrentStatus = B_NO_MEMORY;
}
message_header *header = (message_header *)(fBuffer + fCurrentStart);
header->size = 0;
header->code = code;
header->flags = 0;
STRACE(("info: LinkSender buffered header %ld (%lx) [%lu %lu %lu].\n",
code, code, header->size, header->code, header->flags));
fCurrentEnd += sizeof(message_header);
return B_OK;
}
status_t
LinkSender::EndMessage(bool needsReply)
{
if (fCurrentEnd == fCurrentStart || fCurrentStatus < B_OK)
return fCurrentStatus;
message_header *header = (message_header *)(fBuffer + fCurrentStart);
header->size = CurrentMessageSize();
if (needsReply)
header->flags |= needsReply;
STRACE(("info: LinkSender EndMessage() of size %ld.\n", header->size));
fCurrentStart = fCurrentEnd;
return B_OK;
}
void
LinkSender::CancelMessage()
{
fCurrentEnd = fCurrentStart;
fCurrentStatus = B_OK;
}
status_t
LinkSender::Attach(const void *passedData, size_t passedSize)
{
size_t size = passedSize;
const void* data = passedData;
if (fCurrentStatus < B_OK)
return fCurrentStatus;
if (size == 0)
return fCurrentStatus = B_BAD_VALUE;
if (fCurrentEnd == fCurrentStart)
return B_NO_INIT;
bool useArea = false;
if (size >= kMaxBufferSize) {
useArea = true;
size = sizeof(area_id);
}
if (SpaceLeft() < size) {
status_t status = FlushCompleted(size + CurrentMessageSize());
if (status < B_OK)
return fCurrentStatus = status;
}
area_id senderArea = -1;
if (useArea) {
if (fTargetTeam < 0) {
port_info info;
status_t result = get_port_info(fPort, &info);
if (result != B_OK)
return result;
fTargetTeam = info.team;
}
void* address = NULL;
off_t alignedSize = (passedSize + B_PAGE_SIZE) & ~(B_PAGE_SIZE - 1);
senderArea = create_area("LinkSenderArea", &address, B_ANY_ADDRESS,
alignedSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
if (senderArea < B_OK)
return senderArea;
data = &senderArea;
memcpy(address, passedData, passedSize);
area_id areaID = senderArea;
senderArea = _kern_transfer_area(senderArea, &address,
B_ANY_ADDRESS, fTargetTeam);
if (senderArea < B_OK) {
delete_area(areaID);
return senderArea;
}
}
memcpy(fBuffer + fCurrentEnd, data, size);
fCurrentEnd += size;
return B_OK;
}
status_t
LinkSender::AttachString(const char *string, int32 length)
{
if (string == NULL)
string = "";
size_t maxLength = strlen(string);
if (length == -1) {
length = (int32)maxLength;
if (maxLength > kMaxStringSize)
length = 0;
} else if (length > (int32)maxLength)
length = maxLength;
status_t status = Attach<int32>(length);
if (status < B_OK)
return status;
if (length > 0) {
status = Attach(string, length);
if (status < B_OK)
fCurrentEnd -= sizeof(int32);
}
return status;
}
status_t
LinkSender::AttachRegion(const BRegion& region)
{
Attach(®ion.fCount, sizeof(int32));
if (region.fCount > 0) {
Attach(®ion.fBounds, sizeof(clipping_rect));
return Attach(region.fData,
region.fCount * sizeof(clipping_rect));
}
return Attach(®ion.fBounds, sizeof(clipping_rect));
}
status_t
LinkSender::AttachShape(BShape& shape)
{
int32 opCount, ptCount;
uint32* opList;
BPoint* ptList;
BShape::Private(shape).GetData(&opCount, &ptCount, &opList, &ptList);
Attach(&opCount, sizeof(int32));
Attach(&ptCount, sizeof(int32));
if (opCount > 0)
Attach(opList, opCount * sizeof(uint32));
if (ptCount > 0)
Attach(ptList, ptCount * sizeof(BPoint));
return B_OK;
}
status_t
LinkSender::AttachGradient(const BGradient& gradient)
{
GTRACE(("ServerLink::AttachGradient\n"));
BGradient::Type gradientType = gradient.GetType();
int32 stopCount = gradient.CountColorStops();
GTRACE(("ServerLink::AttachGradient> color stop count == %d\n",
(int)stopCount));
Attach(&gradientType, sizeof(BGradient::Type));
Attach(&stopCount, sizeof(int32));
if (stopCount > 0) {
for (int i = 0; i < stopCount; i++) {
Attach(gradient.ColorStopAtFast(i),
sizeof(BGradient::ColorStop));
}
}
switch (gradientType) {
case BGradient::TYPE_LINEAR:
{
GTRACE(("ServerLink::AttachGradient> type == TYPE_LINEAR\n"));
const BGradientLinear* linear = (BGradientLinear*)&gradient;
Attach(linear->Start());
Attach(linear->End());
break;
}
case BGradient::TYPE_RADIAL:
{
GTRACE(("ServerLink::AttachGradient> type == TYPE_RADIAL\n"));
const BGradientRadial* radial = (BGradientRadial*)&gradient;
BPoint center = radial->Center();
float radius = radial->Radius();
Attach(¢er, sizeof(BPoint));
Attach(&radius, sizeof(float));
break;
}
case BGradient::TYPE_RADIAL_FOCUS:
{
GTRACE(("ServerLink::AttachGradient> type == TYPE_RADIAL_FOCUS\n"));
const BGradientRadialFocus* radialFocus
= (BGradientRadialFocus*)&gradient;
BPoint center = radialFocus->Center();
BPoint focal = radialFocus->Focal();
float radius = radialFocus->Radius();
Attach(¢er, sizeof(BPoint));
Attach(&focal, sizeof(BPoint));
Attach(&radius, sizeof(float));
break;
}
case BGradient::TYPE_DIAMOND:
{
GTRACE(("ServerLink::AttachGradient> type == TYPE_DIAMOND\n"));
const BGradientDiamond* diamond = (BGradientDiamond*)&gradient;
BPoint center = diamond->Center();
Attach(¢er, sizeof(BPoint));
break;
}
case BGradient::TYPE_CONIC:
{
GTRACE(("ServerLink::AttachGradient> type == TYPE_CONIC\n"));
const BGradientConic* conic = (BGradientConic*)&gradient;
BPoint center = conic->Center();
float angle = conic->Angle();
Attach(¢er, sizeof(BPoint));
Attach(&angle, sizeof(float));
break;
}
case BGradient::TYPE_NONE:
{
GTRACE(("ServerLink::AttachGradient> type == TYPE_NONE\n"));
break;
}
}
return B_OK;
}
status_t
LinkSender::AdjustBuffer(size_t newSize, char **_oldBuffer)
{
if (newSize <= kInitialBufferSize)
newSize = kInitialBufferSize;
else if (newSize > kMaxBufferSize)
return B_BUFFER_OVERFLOW;
else if (newSize > kInitialBufferSize)
newSize = (newSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
if (newSize == fBufferSize) {
if (_oldBuffer)
*_oldBuffer = fBuffer;
return B_OK;
}
char *buffer = (char *)malloc(newSize);
if (buffer == NULL)
return B_NO_MEMORY;
if (_oldBuffer)
*_oldBuffer = fBuffer;
else
free(fBuffer);
fBuffer = buffer;
fBufferSize = newSize;
return B_OK;
}
status_t
LinkSender::FlushCompleted(size_t newBufferSize)
{
int32 end = fCurrentEnd;
int32 start = fCurrentStart;
fCurrentEnd = fCurrentStart;
status_t status = Flush();
if (status < B_OK) {
fCurrentEnd = end;
return status;
}
char *oldBuffer = NULL;
status = AdjustBuffer(newBufferSize, &oldBuffer);
if (status != B_OK)
return status;
fCurrentEnd = end - start;
if (oldBuffer != fBuffer) {
memcpy(fBuffer, oldBuffer + start, fCurrentEnd);
free(oldBuffer);
} else
memmove(fBuffer, fBuffer + start, fCurrentEnd);
return B_OK;
}
status_t
LinkSender::Flush(bigtime_t timeout, bool needsReply)
{
if (fCurrentStatus < B_OK)
return fCurrentStatus;
EndMessage(needsReply);
if (fCurrentStart == 0)
return B_OK;
STRACE(("info: LinkSender Flush() waiting to send messages of %ld bytes on port %ld.\n",
fCurrentEnd, fPort));
status_t err;
if (timeout != B_INFINITE_TIMEOUT) {
do {
err = write_port_etc(fPort, kLinkCode, fBuffer,
fCurrentEnd, B_RELATIVE_TIMEOUT, timeout);
} while (err == B_INTERRUPTED);
} else {
do {
err = write_port(fPort, kLinkCode, fBuffer, fCurrentEnd);
} while (err == B_INTERRUPTED);
}
if (err < B_OK) {
STRACE(("error info: LinkSender Flush() failed for %ld bytes (%s) on port %ld.\n",
fCurrentEnd, strerror(err), fPort));
return err;
}
STRACE(("info: LinkSender Flush() messages total of %ld bytes on port %ld.\n",
fCurrentEnd, fPort));
fCurrentEnd = 0;
fCurrentStart = 0;
return B_OK;
}
}