#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <string.h>
#include <X11/X.h>
#include <X11/Xproto.h>
#include "misc.h"
#include "os.h"
#include "scrnintstr.h"
#include "windowstr.h"
#include "pixmapstr.h"
#include "gc.h"
#include "extnsionst.h"
#include "dixstruct.h"
#include "resource.h"
#include "opaque.h"
#include "input.h"
#define GLOBAL
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvproto.h>
#include "xvdix.h"
#ifdef PANORAMIX
#include "panoramiX.h"
#include "panoramiXsrv.h"
#endif
#include "xvdisp.h"
static DevPrivateKeyRec XvScreenKeyRec;
#define XvScreenKey (&XvScreenKeyRec)
unsigned long XvExtensionGeneration = 0;
unsigned long XvScreenGeneration = 0;
unsigned long XvResourceGeneration = 0;
int XvReqCode;
int XvEventBase;
int XvErrorBase;
unsigned long XvRTPort;
unsigned long XvRTEncoding;
unsigned long XvRTGrab;
unsigned long XvRTVideoNotify;
unsigned long XvRTVideoNotifyList;
unsigned long XvRTPortNotify;
static void WriteSwappedVideoNotifyEvent(xvEvent *, xvEvent *);
static void WriteSwappedPortNotifyEvent(xvEvent *, xvEvent *);
static Bool CreateResourceTypes(void);
static Bool XvCloseScreen(int, ScreenPtr);
static Bool XvDestroyPixmap(PixmapPtr);
static Bool XvDestroyWindow(WindowPtr);
static void XvResetProc(ExtensionEntry*);
static int XvdiDestroyGrab(pointer, XID);
static int XvdiDestroyEncoding(pointer, XID);
static int XvdiDestroyVideoNotify(pointer, XID);
static int XvdiDestroyPortNotify(pointer, XID);
static int XvdiDestroyVideoNotifyList(pointer, XID);
static int XvdiDestroyPort(pointer, XID);
static int XvdiSendVideoNotify(XvPortPtr, DrawablePtr, int);
void
XvExtensionInit(void)
{
ExtensionEntry *extEntry;
if (!dixRegisterPrivateKey(&XvScreenKeyRec, PRIVATE_SCREEN, 0))
return;
if (XvScreenGeneration != serverGeneration)
{
if (!CreateResourceTypes())
{
ErrorF("XvExtensionInit: Unable to allocate resource types\n");
return;
}
#ifdef PANORAMIX
XineramaRegisterConnectionBlockCallback(XineramifyXv);
#endif
XvScreenGeneration = serverGeneration;
}
if (XvExtensionGeneration != serverGeneration)
{
XvExtensionGeneration = serverGeneration;
extEntry = AddExtension(XvName, XvNumEvents, XvNumErrors,
ProcXvDispatch, SProcXvDispatch,
XvResetProc, StandardMinorOpcode);
if (!extEntry)
{
FatalError("XvExtensionInit: AddExtensions failed\n");
}
XvReqCode = extEntry->base;
XvEventBase = extEntry->eventBase;
XvErrorBase = extEntry->errorBase;
EventSwapVector[XvEventBase+XvVideoNotify] =
(EventSwapPtr)WriteSwappedVideoNotifyEvent;
EventSwapVector[XvEventBase+XvPortNotify] =
(EventSwapPtr)WriteSwappedPortNotifyEvent;
SetResourceTypeErrorValue(XvRTPort, _XvBadPort);
(void)MakeAtom(XvName, strlen(XvName), xTrue);
}
}
static Bool
CreateResourceTypes(void)
{
if (XvResourceGeneration == serverGeneration) return TRUE;
XvResourceGeneration = serverGeneration;
if (!(XvRTPort = CreateNewResourceType(XvdiDestroyPort, "XvRTPort")))
{
ErrorF("CreateResourceTypes: failed to allocate port resource.\n");
return FALSE;
}
if (!(XvRTGrab = CreateNewResourceType(XvdiDestroyGrab, "XvRTGrab")))
{
ErrorF("CreateResourceTypes: failed to allocate grab resource.\n");
return FALSE;
}
if (!(XvRTEncoding = CreateNewResourceType(XvdiDestroyEncoding,
"XvRTEncoding")))
{
ErrorF("CreateResourceTypes: failed to allocate encoding resource.\n");
return FALSE;
}
if (!(XvRTVideoNotify = CreateNewResourceType(XvdiDestroyVideoNotify,
"XvRTVideoNotify")))
{
ErrorF("CreateResourceTypes: failed to allocate video notify resource.\n");
return FALSE;
}
if (!(XvRTVideoNotifyList = CreateNewResourceType(XvdiDestroyVideoNotifyList,
"XvRTVideoNotifyList")))
{
ErrorF("CreateResourceTypes: failed to allocate video notify list resource.\n");
return FALSE;
}
if (!(XvRTPortNotify = CreateNewResourceType(XvdiDestroyPortNotify,
"XvRTPortNotify")))
{
ErrorF("CreateResourceTypes: failed to allocate port notify resource.\n");
return FALSE;
}
return TRUE;
}
int
XvScreenInit(ScreenPtr pScreen)
{
XvScreenPtr pxvs;
if (XvScreenGeneration != serverGeneration)
{
if (!CreateResourceTypes())
{
ErrorF("XvScreenInit: Unable to allocate resource types\n");
return BadAlloc;
}
#ifdef PANORAMIX
XineramaRegisterConnectionBlockCallback(XineramifyXv);
#endif
XvScreenGeneration = serverGeneration;
}
if (!dixRegisterPrivateKey(&XvScreenKeyRec, PRIVATE_SCREEN, 0))
return BadAlloc;
if (dixLookupPrivate(&pScreen->devPrivates, XvScreenKey))
{
ErrorF("XvScreenInit: screen devPrivates ptr non-NULL before init\n");
}
pxvs = malloc(sizeof (XvScreenRec));
if (!pxvs)
{
ErrorF("XvScreenInit: Unable to allocate screen private structure\n");
return BadAlloc;
}
dixSetPrivate(&pScreen->devPrivates, XvScreenKey, pxvs);
pxvs->DestroyPixmap = pScreen->DestroyPixmap;
pxvs->DestroyWindow = pScreen->DestroyWindow;
pxvs->CloseScreen = pScreen->CloseScreen;
pScreen->DestroyPixmap = XvDestroyPixmap;
pScreen->DestroyWindow = XvDestroyWindow;
pScreen->CloseScreen = XvCloseScreen;
return Success;
}
static Bool
XvCloseScreen(
int ii,
ScreenPtr pScreen
){
XvScreenPtr pxvs;
pxvs = (XvScreenPtr)dixLookupPrivate(&pScreen->devPrivates, XvScreenKey);
pScreen->DestroyPixmap = pxvs->DestroyPixmap;
pScreen->DestroyWindow = pxvs->DestroyWindow;
pScreen->CloseScreen = pxvs->CloseScreen;
(* pxvs->ddCloseScreen)(ii, pScreen);
free(pxvs);
dixSetPrivate(&pScreen->devPrivates, XvScreenKey, NULL);
return (*pScreen->CloseScreen)(ii, pScreen);
}
static void
XvResetProc(ExtensionEntry* extEntry)
{
XvResetProcVector();
}
DevPrivateKey
XvGetScreenKey(void)
{
return XvScreenKey;
}
unsigned long
XvGetRTPort(void)
{
return XvRTPort;
}
static Bool
XvDestroyPixmap(PixmapPtr pPix)
{
Bool status;
ScreenPtr pScreen;
XvScreenPtr pxvs;
XvAdaptorPtr pa;
int na;
XvPortPtr pp;
int np;
pScreen = pPix->drawable.pScreen;
SCREEN_PROLOGUE(pScreen, DestroyPixmap);
pxvs = (XvScreenPtr)dixLookupPrivate(&pScreen->devPrivates, XvScreenKey);
pa = pxvs->pAdaptors;
na = pxvs->nAdaptors;
while (na--)
{
np = pa->nPorts;
pp = pa->pPorts;
while (np--)
{
if (pp->pDraw == (DrawablePtr)pPix)
{
XvdiSendVideoNotify(pp, pp->pDraw, XvPreempted);
(void)(* pp->pAdaptor->ddStopVideo)(NULL, pp, pp->pDraw);
pp->pDraw = NULL;
pp->client = NULL;
pp->time = currentTime;
}
pp++;
}
pa++;
}
status = (* pScreen->DestroyPixmap)(pPix);
SCREEN_EPILOGUE(pScreen, DestroyPixmap, XvDestroyPixmap);
return status;
}
static Bool
XvDestroyWindow(WindowPtr pWin)
{
Bool status;
ScreenPtr pScreen;
XvScreenPtr pxvs;
XvAdaptorPtr pa;
int na;
XvPortPtr pp;
int np;
pScreen = pWin->drawable.pScreen;
SCREEN_PROLOGUE(pScreen, DestroyWindow);
pxvs = (XvScreenPtr)dixLookupPrivate(&pScreen->devPrivates, XvScreenKey);
pa = pxvs->pAdaptors;
na = pxvs->nAdaptors;
while (na--)
{
np = pa->nPorts;
pp = pa->pPorts;
while (np--)
{
if (pp->pDraw == (DrawablePtr)pWin)
{
XvdiSendVideoNotify(pp, pp->pDraw, XvPreempted);
(void)(* pp->pAdaptor->ddStopVideo)(NULL, pp, pp->pDraw);
pp->pDraw = NULL;
pp->client = NULL;
pp->time = currentTime;
}
pp++;
}
pa++;
}
status = (* pScreen->DestroyWindow)(pWin);
SCREEN_EPILOGUE(pScreen, DestroyWindow, XvDestroyWindow);
return status;
}
int
XvdiVideoStopped(XvPortPtr pPort, int reason)
{
if (!pPort->pDraw) return Success;
XvdiSendVideoNotify(pPort, pPort->pDraw, reason);
pPort->pDraw = NULL;
pPort->client = NULL;
pPort->time = currentTime;
return Success;
}
static int
XvdiDestroyPort(pointer pPort, XID id)
{
return (* ((XvPortPtr)pPort)->pAdaptor->ddFreePort)(pPort);
}
static int
XvdiDestroyGrab(pointer pGrab, XID id)
{
((XvGrabPtr)pGrab)->client = NULL;
return Success;
}
static int
XvdiDestroyVideoNotify(pointer pn, XID id)
{
((XvVideoNotifyPtr)pn)->client = NULL;
return Success;
}
static int
XvdiDestroyPortNotify(pointer pn, XID id)
{
((XvPortNotifyPtr)pn)->client = NULL;
return Success;
}
static int
XvdiDestroyVideoNotifyList(pointer pn, XID id)
{
XvVideoNotifyPtr npn,cpn;
cpn = (XvVideoNotifyPtr)pn;
while (cpn)
{
npn = cpn->next;
if (cpn->client) FreeResource(cpn->id, XvRTVideoNotify);
free(cpn);
cpn = npn;
}
return Success;
}
static int
XvdiDestroyEncoding(pointer value, XID id)
{
return Success;
}
static int
XvdiSendVideoNotify(XvPortPtr pPort, DrawablePtr pDraw, int reason)
{
xvEvent event;
XvVideoNotifyPtr pn;
dixLookupResourceByType((pointer *)&pn, pDraw->id, XvRTVideoNotifyList,
serverClient, DixReadAccess);
while (pn)
{
event.u.u.type = XvEventBase + XvVideoNotify;
event.u.videoNotify.time = currentTime.milliseconds;
event.u.videoNotify.drawable = pDraw->id;
event.u.videoNotify.port = pPort->id;
event.u.videoNotify.reason = reason;
WriteEventsToClient(pn->client, 1, (xEventPtr)&event);
pn = pn->next;
}
return Success;
}
int
XvdiSendPortNotify(
XvPortPtr pPort,
Atom attribute,
INT32 value
){
xvEvent event;
XvPortNotifyPtr pn;
pn = pPort->pNotify;
while (pn)
{
event.u.u.type = XvEventBase + XvPortNotify;
event.u.portNotify.time = currentTime.milliseconds;
event.u.portNotify.port = pPort->id;
event.u.portNotify.attribute = attribute;
event.u.portNotify.value = value;
WriteEventsToClient(pn->client, 1, (xEventPtr)&event);
pn = pn->next;
}
return Success;
}
#define CHECK_SIZE(dw, dh, sw, sh) { \
if(!dw || !dh || !sw || !sh) return Success; \
\
if((dw > 32767) || (dh > 32767) || (sw > 32767) || (sh > 32767)) \
return BadValue; \
}
int
XvdiPutVideo(
ClientPtr client,
DrawablePtr pDraw,
XvPortPtr pPort,
GCPtr pGC,
INT16 vid_x, INT16 vid_y,
CARD16 vid_w, CARD16 vid_h,
INT16 drw_x, INT16 drw_y,
CARD16 drw_w, CARD16 drw_h
){
DrawablePtr pOldDraw;
CHECK_SIZE(drw_w, drw_h, vid_w, vid_h);
UpdateCurrentTime();
if (pPort->grab.client && (pPort->grab.client != client))
{
XvdiSendVideoNotify(pPort, pDraw, XvBusy);
return Success;
}
pOldDraw = pPort->pDraw;
if ((pOldDraw) && (pOldDraw != pDraw))
{
XvdiSendVideoNotify(pPort, pPort->pDraw, XvPreempted);
}
(void) (* pPort->pAdaptor->ddPutVideo)(client, pDraw, pPort, pGC,
vid_x, vid_y, vid_w, vid_h,
drw_x, drw_y, drw_w, drw_h);
if ((pPort->pDraw) && (pOldDraw != pDraw))
{
pPort->client = client;
XvdiSendVideoNotify(pPort, pPort->pDraw, XvStarted);
}
pPort->time = currentTime;
return Success;
}
int
XvdiPutStill(
ClientPtr client,
DrawablePtr pDraw,
XvPortPtr pPort,
GCPtr pGC,
INT16 vid_x, INT16 vid_y,
CARD16 vid_w, CARD16 vid_h,
INT16 drw_x, INT16 drw_y,
CARD16 drw_w, CARD16 drw_h
){
int status;
CHECK_SIZE(drw_w, drw_h, vid_w, vid_h);
UpdateCurrentTime();
if (pPort->grab.client && (pPort->grab.client != client))
{
XvdiSendVideoNotify(pPort, pDraw, XvBusy);
return Success;
}
pPort->time = currentTime;
status = (* pPort->pAdaptor->ddPutStill)(client, pDraw, pPort, pGC,
vid_x, vid_y, vid_w, vid_h,
drw_x, drw_y, drw_w, drw_h);
return status;
}
int
XvdiPutImage(
ClientPtr client,
DrawablePtr pDraw,
XvPortPtr pPort,
GCPtr pGC,
INT16 src_x, INT16 src_y,
CARD16 src_w, CARD16 src_h,
INT16 drw_x, INT16 drw_y,
CARD16 drw_w, CARD16 drw_h,
XvImagePtr image,
unsigned char* data,
Bool sync,
CARD16 width, CARD16 height
){
CHECK_SIZE(drw_w, drw_h, src_w, src_h);
UpdateCurrentTime();
if (pPort->grab.client && (pPort->grab.client != client))
{
XvdiSendVideoNotify(pPort, pDraw, XvBusy);
return Success;
}
pPort->time = currentTime;
return (* pPort->pAdaptor->ddPutImage)(client, pDraw, pPort, pGC,
src_x, src_y, src_w, src_h,
drw_x, drw_y, drw_w, drw_h,
image, data, sync, width, height);
}
int
XvdiGetVideo(
ClientPtr client,
DrawablePtr pDraw,
XvPortPtr pPort,
GCPtr pGC,
INT16 vid_x, INT16 vid_y,
CARD16 vid_w, CARD16 vid_h,
INT16 drw_x, INT16 drw_y,
CARD16 drw_w, CARD16 drw_h
){
DrawablePtr pOldDraw;
CHECK_SIZE(drw_w, drw_h, vid_w, vid_h);
UpdateCurrentTime();
if (pPort->grab.client && (pPort->grab.client != client))
{
XvdiSendVideoNotify(pPort, pDraw, XvBusy);
return Success;
}
pOldDraw = pPort->pDraw;
if ((pOldDraw) && (pOldDraw != pDraw))
{
XvdiSendVideoNotify(pPort, pPort->pDraw, XvPreempted);
}
(void) (* pPort->pAdaptor->ddGetVideo)(client, pDraw, pPort, pGC,
vid_x, vid_y, vid_w, vid_h,
drw_x, drw_y, drw_w, drw_h);
if ((pPort->pDraw) && (pOldDraw != pDraw))
{
pPort->client = client;
XvdiSendVideoNotify(pPort, pPort->pDraw, XvStarted);
}
pPort->time = currentTime;
return Success;
}
int
XvdiGetStill(
ClientPtr client,
DrawablePtr pDraw,
XvPortPtr pPort,
GCPtr pGC,
INT16 vid_x, INT16 vid_y,
CARD16 vid_w, CARD16 vid_h,
INT16 drw_x, INT16 drw_y,
CARD16 drw_w, CARD16 drw_h
){
int status;
CHECK_SIZE(drw_w, drw_h, vid_w, vid_h);
UpdateCurrentTime();
if (pPort->grab.client && (pPort->grab.client != client))
{
XvdiSendVideoNotify(pPort, pDraw, XvBusy);
return Success;
}
status = (* pPort->pAdaptor->ddGetStill)(client, pDraw, pPort, pGC,
vid_x, vid_y, vid_w, vid_h,
drw_x, drw_y, drw_w, drw_h);
pPort->time = currentTime;
return status;
}
int
XvdiGrabPort(
ClientPtr client,
XvPortPtr pPort,
Time ctime,
int *p_result
){
unsigned long id;
TimeStamp time;
UpdateCurrentTime();
time = ClientTimeToServerTime(ctime);
if (pPort->grab.client && (client != pPort->grab.client))
{
*p_result = XvAlreadyGrabbed;
return Success;
}
if ((CompareTimeStamps(time, currentTime) == LATER) ||
(CompareTimeStamps(time, pPort->time) == EARLIER))
{
*p_result = XvInvalidTime;
return Success;
}
if (client == pPort->grab.client)
{
*p_result = Success;
return Success;
}
id = FakeClientID(client->index);
if (!AddResource(id, XvRTGrab, &pPort->grab))
{
return BadAlloc;
}
if ((pPort->pDraw) && (client != pPort->client))
{
XvdiStopVideo(NULL, pPort, pPort->pDraw);
}
pPort->grab.client = client;
pPort->grab.id = id;
pPort->time = currentTime;
*p_result = Success;
return Success;
}
int
XvdiUngrabPort(
ClientPtr client,
XvPortPtr pPort,
Time ctime
){
TimeStamp time;
UpdateCurrentTime();
time = ClientTimeToServerTime(ctime);
if ((!pPort->grab.client) || (client != pPort->grab.client))
{
return Success;
}
if ((CompareTimeStamps(time, currentTime) == LATER) ||
(CompareTimeStamps(time, pPort->time) == EARLIER))
{
return Success;
}
FreeResource(pPort->grab.id, XvRTGrab);
pPort->grab.client = NULL;
pPort->time = currentTime;
return Success;
}
int
XvdiSelectVideoNotify(
ClientPtr client,
DrawablePtr pDraw,
BOOL onoff
){
XvVideoNotifyPtr pn,tpn,fpn;
int rc;
rc = dixLookupResourceByType((pointer *)&pn, pDraw->id, XvRTVideoNotifyList,
client, DixWriteAccess);
if (rc != Success && rc != BadValue)
return rc;
if (!onoff && !pn) return Success;
if (!pn)
{
if (!(tpn = malloc(sizeof(XvVideoNotifyRec))))
return BadAlloc;
tpn->next = NULL;
if (!AddResource(pDraw->id, XvRTVideoNotifyList, tpn))
{
free(tpn);
return BadAlloc;
}
}
else
{
fpn = NULL;
tpn = pn;
while (tpn)
{
if (tpn->client == client)
{
if (!onoff) tpn->client = NULL;
return Success;
}
if (!tpn->client) fpn = tpn;
tpn = tpn->next;
}
if (!onoff) return Success;
if (fpn)
{
tpn = fpn;
}
else
{
if (!(tpn = malloc(sizeof(XvVideoNotifyRec))))
return BadAlloc;
tpn->next = pn->next;
pn->next = tpn;
}
}
tpn->client = NULL;
tpn->id = FakeClientID(client->index);
AddResource(tpn->id, XvRTVideoNotify, tpn);
tpn->client = client;
return Success;
}
int
XvdiSelectPortNotify(
ClientPtr client,
XvPortPtr pPort,
BOOL onoff
){
XvPortNotifyPtr pn,tpn;
tpn = NULL;
pn = pPort->pNotify;
while (pn)
{
if (!pn->client) tpn = pn;
if (pn->client == client) break;
pn = pn->next;
}
if (pn)
{
if (!onoff)
{
pn->client = NULL;
FreeResource(pn->id, XvRTPortNotify);
}
return Success;
}
if (!tpn)
{
if (!(tpn = malloc(sizeof(XvPortNotifyRec))))
return BadAlloc;
tpn->next = pPort->pNotify;
pPort->pNotify = tpn;
}
tpn->client = client;
tpn->id = FakeClientID(client->index);
AddResource(tpn->id, XvRTPortNotify, tpn);
return Success;
}
int
XvdiStopVideo(
ClientPtr client,
XvPortPtr pPort,
DrawablePtr pDraw
){
int status;
if (!pPort->pDraw || (pPort->pDraw != pDraw))
{
XvdiSendVideoNotify(pPort, pDraw, XvStopped);
return Success;
}
if ((client) && (pPort->grab.client) && (pPort->grab.client != client))
{
XvdiSendVideoNotify(pPort, pDraw, XvBusy);
return Success;
}
XvdiSendVideoNotify(pPort, pDraw, XvStopped);
status = (* pPort->pAdaptor->ddStopVideo)(client, pPort, pDraw);
pPort->pDraw = NULL;
pPort->client = (ClientPtr)client;
pPort->time = currentTime;
return status;
}
int
XvdiPreemptVideo(
ClientPtr client,
XvPortPtr pPort,
DrawablePtr pDraw
){
int status;
if (!pPort->pDraw || (pPort->pDraw != pDraw)) return Success;
XvdiSendVideoNotify(pPort, pPort->pDraw, XvPreempted);
status = (* pPort->pAdaptor->ddStopVideo)(client, pPort, pPort->pDraw);
pPort->pDraw = NULL;
pPort->client = (ClientPtr)client;
pPort->time = currentTime;
return status;
}
int
XvdiMatchPort(
XvPortPtr pPort,
DrawablePtr pDraw
){
XvAdaptorPtr pa;
XvFormatPtr pf;
int nf;
pa = pPort->pAdaptor;
if (pa->pScreen != pDraw->pScreen) return BadMatch;
nf = pa->nFormats;
pf = pa->pFormats;
while (nf--)
{
if ((pf->depth == pDraw->depth)
#if 0
&& ((pDraw->type == DRAWABLE_PIXMAP) ||
(wVisual(((WindowPtr)pDraw)) == pf->visual))
#endif
)
return Success;
pf++;
}
return BadMatch;
}
int
XvdiSetPortAttribute(
ClientPtr client,
XvPortPtr pPort,
Atom attribute,
INT32 value
){
int status;
status = (* pPort->pAdaptor->ddSetPortAttribute)(client, pPort, attribute, value);
if (status == Success)
XvdiSendPortNotify(pPort, attribute, value);
return status;
}
int
XvdiGetPortAttribute(
ClientPtr client,
XvPortPtr pPort,
Atom attribute,
INT32 *p_value
){
return
(* pPort->pAdaptor->ddGetPortAttribute)(client, pPort, attribute, p_value);
}
static void
WriteSwappedVideoNotifyEvent(xvEvent *from, xvEvent *to)
{
to->u.u.type = from->u.u.type;
to->u.u.detail = from->u.u.detail;
cpswaps(from->u.videoNotify.sequenceNumber,
to->u.videoNotify.sequenceNumber);
cpswapl(from->u.videoNotify.time, to->u.videoNotify.time);
cpswapl(from->u.videoNotify.drawable, to->u.videoNotify.drawable);
cpswapl(from->u.videoNotify.port, to->u.videoNotify.port);
}
static void
WriteSwappedPortNotifyEvent(xvEvent *from, xvEvent *to)
{
to->u.u.type = from->u.u.type;
to->u.u.detail = from->u.u.detail;
cpswaps(from->u.portNotify.sequenceNumber, to->u.portNotify.sequenceNumber);
cpswapl(from->u.portNotify.time, to->u.portNotify.time);
cpswapl(from->u.portNotify.port, to->u.portNotify.port);
cpswapl(from->u.portNotify.value, to->u.portNotify.value);
}