コード例 #1
0
ファイル: tkisamp.c プロジェクト: Feneric/xconq
static void
imfsample_event_proc(ClientData cldata, XEvent *eventPtr)
{
    Imfsample *imfsample = (Imfsample *) cldata;

    if (eventPtr->type == Expose) {
	if (!imfsample->update_pending) {
		Tcl_DoWhenIdle(imfsample_display, cldata);
		imfsample->update_pending = 1;
	}
    } else if (eventPtr->type == ConfigureNotify) {
	if (!imfsample->update_pending) {
		Tcl_DoWhenIdle(imfsample_display, cldata);
		imfsample->update_pending = 1;
	}
    } else if (eventPtr->type == DestroyNotify) {
	if (imfsample->tkwin != NULL) {
	    imfsample->tkwin = NULL;
	    Tcl_DeleteCommand(imfsample->interp,
			      Tcl_GetCommandName(imfsample->interp,
						 imfsample->widgetCmd));
	}
	if (imfsample->update_pending) {
	    Tcl_CancelIdleCall(imfsample_display, cldata);
	}
	Tcl_EventuallyFree(cldata, imfsample_destroy);
    }
}
コード例 #2
0
void
TkSelDeadWindow(
    register TkWindow *winPtr)	/* Window that's being deleted. */
{
    register TkSelHandler *selPtr;
    register TkSelInProgress *ipPtr;
    TkSelectionInfo *infoPtr, *prevPtr, *nextPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    /*
     * While deleting all the handlers, be careful to check whether
     * ConvertSelection or TkSelPropProc are about to process one of the
     * deleted handlers.
     */

    while (winPtr->selHandlerList != NULL) {
	selPtr = winPtr->selHandlerList;
	winPtr->selHandlerList = selPtr->nextPtr;
	for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
		ipPtr = ipPtr->nextPtr) {
	    if (ipPtr->selPtr == selPtr) {
		ipPtr->selPtr = NULL;
	    }
	}
	if (selPtr->proc == HandleTclCommand) {
	    /*
	     * Mark the CommandInfo as deleted and free it when we can.
	     */

	    ((CommandInfo *) selPtr->clientData)->interp = NULL;
	    Tcl_EventuallyFree(selPtr->clientData, TCL_DYNAMIC);
	}
	ckfree((char *) selPtr);
    }

    /*
     * Remove selections owned by window being deleted.
     */

    for (infoPtr = winPtr->dispPtr->selectionInfoPtr, prevPtr = NULL;
	    infoPtr != NULL; infoPtr = nextPtr) {
	nextPtr = infoPtr->nextPtr;
	if (infoPtr->owner == (Tk_Window) winPtr) {
	    if (infoPtr->clearProc == LostSelection) {
		ckfree((char *) infoPtr->clearData);
	    }
	    ckfree((char *) infoPtr);
	    infoPtr = prevPtr;
	    if (prevPtr == NULL) {
		winPtr->dispPtr->selectionInfoPtr = nextPtr;
	    } else {
		prevPtr->nextPtr = nextPtr;
	    }
	}
	prevPtr = infoPtr;
    }
}
コード例 #3
0
ファイル: tkBusy.c プロジェクト: das/tk
static void
BusyEventProc(
    ClientData clientData,	/* Busy window record */
    XEvent *eventPtr)		/* Event which triggered call to routine */
{
    Busy *busyPtr = clientData;

    if (eventPtr->type == DestroyNotify) {
        busyPtr->tkBusy = NULL;
        Tcl_EventuallyFree(busyPtr, DestroyBusy);
    }
}
コード例 #4
0
ファイル: tkBusy.c プロジェクト: das/tk
/* ARGSUSED */
static void
BusyCustodyProc(
    ClientData clientData,	/* Information about the busy window. */
    Tk_Window tkwin)		/* Not used. */
{
    Busy *busyPtr = clientData;

    Tk_DeleteEventHandler(busyPtr->tkBusy, StructureNotifyMask, BusyEventProc,
                          busyPtr);
    TkpHideBusyWindow(busyPtr);
    busyPtr->tkBusy = NULL;
    Tcl_EventuallyFree(busyPtr, DestroyBusy);
}
コード例 #5
0
ファイル: tkImage.c プロジェクト: tcltk/tk
static void
EventuallyDeleteImage(
    ImageMaster *masterPtr,	/* Pointer to main data structure for image. */
    int forgetImageHashNow)	/* Flag to say whether the hash table is about
				 * to vanish. */
{
    if (forgetImageHashNow) {
	masterPtr->hPtr = NULL;
    }
    if (!masterPtr->deleted) {
	masterPtr->deleted = 1;
	Tcl_EventuallyFree(masterPtr, (Tcl_FreeProc *) DeleteImage);
    }
}
コード例 #6
0
ファイル: tnmSnmpUtil.c プロジェクト: qyqx/scotty
void
TnmSnmpDeleteSession(TnmSnmp *session)
{
    TnmSnmpRequest **rPtrPtr;

    if (! session) return;

    rPtrPtr = &queueHead;
    while (*rPtrPtr) {
	if ((*rPtrPtr)->session == session) {
	    TnmSnmpRequest *request = *rPtrPtr;
	    *rPtrPtr = (*rPtrPtr)->nextPtr;
	    if (request->timer) {
	        Tcl_DeleteTimerHandler(request->timer);
	    }
	    Tcl_EventuallyFree((ClientData) request, RequestDestroyProc);
	} else {
	    rPtrPtr = &(*rPtrPtr)->nextPtr;
	}
    }

    Tcl_EventuallyFree((ClientData) session, SessionDestroyProc);
}
コード例 #7
0
static void
TcpServerCloseProc(
    ClientData callbackData)	/* The data passed in the call to
				 * Tcl_CreateCloseHandler. */
{
    AcceptCallback *acceptCallbackPtr = (AcceptCallback *) callbackData;
				/* The actual data. */

    if (acceptCallbackPtr->interp != NULL) {
	UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
		acceptCallbackPtr);
    }
    Tcl_EventuallyFree(acceptCallbackPtr->script, TCL_DYNAMIC);
    ckfree((char *) acceptCallbackPtr);
}
コード例 #8
0
ファイル: tkMenubutton.c プロジェクト: lmiadowicz/tk
static void
DestroyMenuButton(
    char *memPtr)		/* Info about button widget. */
{
    register TkMenuButton *mbPtr = (TkMenuButton *) memPtr;
    TkpDestroyMenuButton(mbPtr);

    if (mbPtr->flags & REDRAW_PENDING) {
        Tcl_CancelIdleCall(TkpDisplayMenuButton, mbPtr);
    }

    /*
     * Free up all the stuff that requires special handling, then let
     * Tk_FreeOptions handle all the standard option-related stuff.
     */

    Tcl_DeleteCommandFromToken(mbPtr->interp, mbPtr->widgetCmd);
    if (mbPtr->textVarName != NULL) {
        Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
                       TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
                       MenuButtonTextVarProc, mbPtr);
    }
    if (mbPtr->image != NULL) {
        Tk_FreeImage(mbPtr->image);
    }
    if (mbPtr->normalTextGC != None) {
        Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC);
    }
    if (mbPtr->activeTextGC != None) {
        Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC);
    }
    if (mbPtr->disabledGC != None) {
        Tk_FreeGC(mbPtr->display, mbPtr->disabledGC);
    }
    if (mbPtr->stippleGC != None) {
        Tk_FreeGC(mbPtr->display, mbPtr->stippleGC);
    }
    if (mbPtr->gray != None) {
        Tk_FreeBitmap(mbPtr->display, mbPtr->gray);
    }
    if (mbPtr->textLayout != NULL) {
        Tk_FreeTextLayout(mbPtr->textLayout);
    }
    Tk_FreeConfigOptions((char *) mbPtr, mbPtr->optionTable, mbPtr->tkwin);
    mbPtr->tkwin = NULL;
    Tcl_EventuallyFree(mbPtr, TCL_DYNAMIC);
}
コード例 #9
0
ファイル: tlsIO.c プロジェクト: fahkri/tls
/*
 *-------------------------------------------------------------------
 *
 * TlsCloseProc --
 *
 *	This procedure is invoked by the generic IO level to perform
 *	channel-type-specific cleanup when a SSL socket based channel
 *	is closed.
 *
 *	Note: we leave the underlying socket alone, is this right?
 *
 * Results:
 *	0 if successful, the value of Tcl_GetErrno() if failed.
 *
 * Side effects:
 *	Closes the socket of the channel.
 *
 *-------------------------------------------------------------------
 */
static int
TlsCloseProc(ClientData instanceData,	/* The socket to close. */
             Tcl_Interp *interp)	/* For error reporting - unused. */
{
    State *statePtr = (State *) instanceData;

    dprintf(stderr,"\nTlsCloseProc(0x%x)", (unsigned int) statePtr);

    if (channelTypeVersion == TLS_CHANNEL_VERSION_1) {
	/*
	 * Remove event handler to underlying channel, this could
	 * be because we are closing for real, or being "unstacked".
	 */

	Tcl_DeleteChannelHandler(Tls_GetParent(statePtr),
		TlsChannelHandler, (ClientData) statePtr);
    }

    Tls_Clean(statePtr);
    Tcl_EventuallyFree((ClientData)statePtr, Tls_Free);
    return TCL_OK;
}
コード例 #10
0
ファイル: tkUnixScale.c プロジェクト: dgsb/tk
void
TkpDestroyScale(
    TkScale *scalePtr)
{
    Tcl_EventuallyFree(scalePtr, TCL_DYNAMIC);
}
コード例 #11
0
ファイル: tkPlace.c プロジェクト: AlexShiLucky/bitkeeper
static void
MasterStructureProc(
    ClientData clientData,	/* Pointer to Master structure for window
				 * referred to by eventPtr. */
    XEvent *eventPtr)		/* Describes what just happened. */
{
    register Master *masterPtr = clientData;
    register Slave *slavePtr, *nextPtr;
    TkDisplay *dispPtr = ((TkWindow *) masterPtr->tkwin)->dispPtr;

    switch (eventPtr->type) {
    case ConfigureNotify:
	if ((masterPtr->slavePtr != NULL)
		&& !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
	    masterPtr->flags |= PARENT_RECONFIG_PENDING;
	    Tcl_DoWhenIdle(RecomputePlacement, masterPtr);
	}
	return;
    case DestroyNotify:
	for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
		slavePtr = nextPtr) {
	    slavePtr->masterPtr = NULL;
	    nextPtr = slavePtr->nextPtr;
	    slavePtr->nextPtr = NULL;
	}
	Tcl_DeleteHashEntry(Tcl_FindHashEntry(&dispPtr->masterTable,
		(char *) masterPtr->tkwin));
	if (masterPtr->flags & PARENT_RECONFIG_PENDING) {
	    Tcl_CancelIdleCall(RecomputePlacement, masterPtr);
	}
	masterPtr->tkwin = NULL;
	if (masterPtr->abortPtr != NULL) {
	    *masterPtr->abortPtr = 1;
	}
	Tcl_EventuallyFree(masterPtr, TCL_DYNAMIC);
	return;
    case MapNotify:
	/*
	 * When a master gets mapped, must redo the geometry computation so
	 * that all of its slaves get remapped.
	 */

	if ((masterPtr->slavePtr != NULL)
		&& !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
	    masterPtr->flags |= PARENT_RECONFIG_PENDING;
	    Tcl_DoWhenIdle(RecomputePlacement, masterPtr);
	}
	return;
    case UnmapNotify:
	/*
	 * Unmap all of the slaves when the master gets unmapped, so that they
	 * don't keep redisplaying themselves.
	 */

	for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
		slavePtr = slavePtr->nextPtr) {
	    Tk_UnmapWindow(slavePtr->tkwin);
	}
	return;
    }
}
コード例 #12
0
void
Tk_DeleteSelHandler(
    Tk_Window tkwin,		/* Token for window. */
    Atom selection,		/* The selection whose handler is to be
				 * removed. */
    Atom target)		/* The target whose selection handler is to be
				 * removed. */
{
    TkWindow *winPtr = (TkWindow *) tkwin;
    register TkSelHandler *selPtr, *prevPtr;
    register TkSelInProgress *ipPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    /*
     * Find the selection handler to be deleted, or return if it doesn't
     * exist.
     */

    for (selPtr = winPtr->selHandlerList, prevPtr = NULL; ;
	    prevPtr = selPtr, selPtr = selPtr->nextPtr) {
	if (selPtr == NULL) {
	    return;
	}
	if ((selPtr->selection == selection) && (selPtr->target == target)) {
	    break;
	}
    }

    /*
     * If ConvertSelection is processing this handler, tell it that the
     * handler is dead.
     */

    for (ipPtr = tsdPtr->pendingPtr; ipPtr != NULL;
	    ipPtr = ipPtr->nextPtr) {
	if (ipPtr->selPtr == selPtr) {
	    ipPtr->selPtr = NULL;
	}
    }

    /*
     * Free resources associated with the handler.
     */

    if (prevPtr == NULL) {
	winPtr->selHandlerList = selPtr->nextPtr;
    } else {
	prevPtr->nextPtr = selPtr->nextPtr;
    }

    if ((target == XA_STRING) && (winPtr->dispPtr->utf8Atom != (Atom) NULL)) {
	/*
	 * If the user asked for a STRING handler and we understand
	 * UTF8_STRING, we may have implicitly created a UTF8_STRING handler
	 * for them. Look for it and delete it as necessary.
	 */

	TkSelHandler *utf8selPtr;

	target = winPtr->dispPtr->utf8Atom;
	for (utf8selPtr = winPtr->selHandlerList; utf8selPtr != NULL;
		utf8selPtr = utf8selPtr->nextPtr) {
	    if ((utf8selPtr->selection == selection)
		    && (utf8selPtr->target == target)) {
		break;
	    }
	}
	if (utf8selPtr != NULL) {
	    if ((utf8selPtr->format == target)
		    && (utf8selPtr->proc == selPtr->proc)
		    && (utf8selPtr->size == selPtr->size)) {
		/*
		 * This recursive call is OK, because we've changed the value
		 * of 'target'.
		 */

		Tk_DeleteSelHandler(tkwin, selection, target);
	    }
	}
    }

    if (selPtr->proc == HandleTclCommand) {
	/*
	 * Mark the CommandInfo as deleted and free it if we can.
	 */

	((CommandInfo*)selPtr->clientData)->interp = NULL;
	Tcl_EventuallyFree(selPtr->clientData, TCL_DYNAMIC);
    }
    ckfree((char *) selPtr);
}
コード例 #13
0
ファイル: tkBusy.c プロジェクト: das/tk
int
Tk_BusyObjCmd(
    ClientData clientData,	/* Main window associated with interpreter. */
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    Tk_Window tkwin = clientData;
    Tcl_HashTable *busyTablePtr = &((TkWindow *) tkwin)->mainPtr->busyTable;
    Busy *busyPtr;
    Tcl_Obj *objPtr;
    int index, result = TCL_OK;
    static const char *const optionStrings[] = {
        "cget", "configure", "current", "forget", "hold", "status", NULL
    };
    enum options {
        BUSY_CGET, BUSY_CONFIGURE, BUSY_CURRENT, BUSY_FORGET, BUSY_HOLD,
        BUSY_STATUS
    };

    if (objc < 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "options ?arg arg ...?");
        return TCL_ERROR;
    }

    /*
     * [tk busy <window>] command shortcut.
     */

    if (Tcl_GetString(objv[1])[0] == '.') {
        if (objc%2 == 1) {
            Tcl_WrongNumArgs(interp, 1, objv, "window ?option value ...?");
            return TCL_ERROR;
        }
        return HoldBusy(busyTablePtr, interp, objv[1], objc-2, objv+2);
    }

    if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
                            &index) != TCL_OK) {
        return TCL_ERROR;
    }
    switch ((enum options) index) {
    case BUSY_CGET:
        if (objc != 4) {
            Tcl_WrongNumArgs(interp, 2, objv, "window option");
            return TCL_ERROR;
        }
        busyPtr = GetBusy(interp, busyTablePtr, objv[2]);
        if (busyPtr == NULL) {
            return TCL_ERROR;
        }
        Tcl_Preserve(busyPtr);
        objPtr = Tk_GetOptionValue(interp, (char *) busyPtr,
                                   busyPtr->optionTable, objv[3], busyPtr->tkBusy);
        if (objPtr == NULL) {
            result = TCL_ERROR;
        } else {
            Tcl_SetObjResult(interp, objPtr);
        }
        Tcl_Release(busyPtr);
        return result;

    case BUSY_CONFIGURE:
        if (objc < 3) {
            Tcl_WrongNumArgs(interp, 2, objv, "window ?option? ?value ...?");
            return TCL_ERROR;
        }
        busyPtr = GetBusy(interp, busyTablePtr, objv[2]);
        if (busyPtr == NULL) {
            return TCL_ERROR;
        }
        Tcl_Preserve(busyPtr);
        if (objc <= 4) {
            objPtr = Tk_GetOptionInfo(interp, (char *) busyPtr,
                                      busyPtr->optionTable, (objc == 4) ? objv[3] : NULL,
                                      busyPtr->tkBusy);
            if (objPtr == NULL) {
                result = TCL_ERROR;
            } else {
                Tcl_SetObjResult(interp, objPtr);
            }
        } else {
            result = ConfigureBusy(interp, busyPtr, objc-3, objv+3);
        }
        Tcl_Release(busyPtr);
        return result;

    case BUSY_CURRENT: {
        Tcl_HashEntry *hPtr;
        Tcl_HashSearch cursor;
        const char *pattern = (objc == 3 ? Tcl_GetString(objv[2]) : NULL);

        objPtr = Tcl_NewObj();
        for (hPtr = Tcl_FirstHashEntry(busyTablePtr, &cursor); hPtr != NULL;
                hPtr = Tcl_NextHashEntry(&cursor)) {
            busyPtr = Tcl_GetHashValue(hPtr);
            if (pattern == NULL ||
                    Tcl_StringMatch(Tk_PathName(busyPtr->tkRef), pattern)) {
                Tcl_ListObjAppendElement(interp, objPtr,
                                         TkNewWindowObj(busyPtr->tkRef));
            }
        }
        Tcl_SetObjResult(interp, objPtr);
        return TCL_OK;
    }

    case BUSY_FORGET:
        if (objc != 3) {
            Tcl_WrongNumArgs(interp, 2, objv, "window");
            return TCL_ERROR;
        }
        busyPtr = GetBusy(interp, busyTablePtr, objv[2]);
        if (busyPtr == NULL) {
            return TCL_ERROR;
        }
        TkpHideBusyWindow(busyPtr);
        Tcl_EventuallyFree(busyPtr, DestroyBusy);
        return TCL_OK;

    case BUSY_HOLD:
        if (objc < 3 || objc%2 != 1) {
            Tcl_WrongNumArgs(interp, 2, objv, "window ?option value ...?");
            return TCL_ERROR;
        }
        return HoldBusy(busyTablePtr, interp, objv[2], objc-3, objv+3);

    case BUSY_STATUS:
        if (objc != 3) {
            Tcl_WrongNumArgs(interp, 2, objv, "window");
            return TCL_ERROR;
        }
        Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
                             GetBusy(interp, busyTablePtr, objv[2]) != NULL));
        return TCL_OK;
    }

    Tcl_Panic("unhandled option: %d", index);
    return TCL_ERROR;		/* Unreachable */
}
コード例 #14
0
ファイル: tkBusy.c プロジェクト: das/tk
static void
RefWinEventProc(
    ClientData clientData,	/* Busy window record */
    register XEvent *eventPtr)	/* Event which triggered call to routine */
{
    register Busy *busyPtr = clientData;

    switch (eventPtr->type) {
    case ReparentNotify:
    case DestroyNotify:
        /*
         * Arrange for the busy structure to be removed at a proper time.
         */

        Tcl_EventuallyFree(busyPtr, DestroyBusy);
        break;

    case ConfigureNotify:
        if ((busyPtr->width != Tk_Width(busyPtr->tkRef)) ||
                (busyPtr->height != Tk_Height(busyPtr->tkRef)) ||
                (busyPtr->x != Tk_X(busyPtr->tkRef)) ||
                (busyPtr->y != Tk_Y(busyPtr->tkRef))) {
            int x, y;

            busyPtr->width = Tk_Width(busyPtr->tkRef);
            busyPtr->height = Tk_Height(busyPtr->tkRef);
            busyPtr->x = Tk_X(busyPtr->tkRef);
            busyPtr->y = Tk_Y(busyPtr->tkRef);

            x = y = 0;

            if (busyPtr->tkParent != busyPtr->tkRef) {
                Tk_Window tkwin;

                for (tkwin = busyPtr->tkRef; (tkwin != NULL) &&
                        (!Tk_IsTopLevel(tkwin)); tkwin = Tk_Parent(tkwin)) {
                    if (tkwin == busyPtr->tkParent) {
                        break;
                    }
                    x += Tk_X(tkwin) + Tk_Changes(tkwin)->border_width;
                    y += Tk_Y(tkwin) + Tk_Changes(tkwin)->border_width;
                }
            }
            if (busyPtr->tkBusy != NULL) {
                Tk_MoveResizeWindow(busyPtr->tkBusy, x, y, busyPtr->width,
                                    busyPtr->height);
                TkpShowBusyWindow(busyPtr);
            }
        }
        break;

    case MapNotify:
        if (busyPtr->tkParent != busyPtr->tkRef) {
            TkpShowBusyWindow(busyPtr);
        }
        break;

    case UnmapNotify:
        if (busyPtr->tkParent != busyPtr->tkRef) {
            TkpHideBusyWindow(busyPtr);
        }
        break;
    }
}
コード例 #15
0
ファイル: tnmSnmpUtil.c プロジェクト: qyqx/scotty
void
TnmSnmpDeleteRequest(TnmSnmpRequest *request)
{
    TnmSnmpRequest *rPtr, **rPtrPtr;
    TnmSnmp *session;

    /*
     * Check whether the request still exists. It may have been
     * removed because the session for this request has been 
     * destroyed during callback processing.
     */

    for (rPtr = queueHead; rPtr; rPtr = rPtr->nextPtr) {
	if (rPtr == request) break;
    }
    if (! rPtr) return;
    
    /* 
     * Check whether the session is still in the session list.
     * We sometimes get called when the session has already been
     * destroyed as a side effect of evaluating callbacks.
     */
    
    for (session = tnmSnmpList; session; session = session->nextPtr) {
	if (session == request->session) break;
    }

    if (session) {
	if (request->sends) {
	    session->active--;
	} else {
	    session->waiting--;
	}
    }
    
    /*
     * Remove the request from the list of outstanding requests.
     * and free the resources allocated for this request.
     */

    rPtrPtr = &queueHead;
    while (*rPtrPtr && *rPtrPtr != request) {
	rPtrPtr = &(*rPtrPtr)->nextPtr;
    }
    if (*rPtrPtr) {
	*rPtrPtr = request->nextPtr;
	if (request->timer) {
	    Tcl_DeleteTimerHandler(request->timer);
	    request->timer = NULL;
	}
	Tcl_EventuallyFree((ClientData) request, RequestDestroyProc);
    }

    /*
     * Update the request queue. This will activate async requests
     * that have been queued because of the window size.
     */
     
    if (session) {
	TnmSnmpQueueRequest(session, NULL);
    }
}
コード例 #16
0
ファイル: pgtclId.c プロジェクト: sunyangkobe/cscd43
/*
 * Remove a connection Id from the hash table and
 * close all portals the user forgot.
 */
int
PgDelConnectionId(DRIVER_DEL_PROTO)
{
	Tcl_HashEntry *entry;
	Tcl_HashSearch hsearch;
	Pg_ConnectionId *connid;
	Pg_TclNotifies *notifies;
	int			i;

	connid = (Pg_ConnectionId *) cData;

	for (i = 0; i < connid->res_max; i++)
	{
		if (connid->results[i])
			PQclear(connid->results[i]);
	}
	ckfree((void *) connid->results);

	/* Release associated notify info */
	while ((notifies = connid->notify_list) != NULL)
	{
		connid->notify_list = notifies->next;
		for (entry = Tcl_FirstHashEntry(&notifies->notify_hash, &hsearch);
			 entry != NULL;
			 entry = Tcl_NextHashEntry(&hsearch))
			ckfree((char *) Tcl_GetHashValue(entry));
		Tcl_DeleteHashTable(&notifies->notify_hash);
		if (notifies->conn_loss_cmd)
			ckfree((void *) notifies->conn_loss_cmd);
		if (notifies->interp)
			Tcl_DontCallWhenDeleted(notifies->interp, PgNotifyInterpDelete,
									(ClientData) notifies);
		ckfree((void *) notifies);
	}

	/*
	 * Turn off the Tcl event source for this connection, and delete any
	 * pending notify and connection-loss events.
	 */
	PgStopNotifyEventSource(connid, true);

	/* Close the libpq connection too */
	PQfinish(connid->conn);
	connid->conn = NULL;

	/*
	 * Kill the notifier channel, too.	We must not do this until after
	 * we've closed the libpq connection, because Tcl will try to close
	 * the socket itself!
	 *
	 * XXX Unfortunately, while this works fine if we are closing due to
	 * explicit pg_disconnect, all Tcl versions through 8.4.1 dump core if
	 * we try to do it during interpreter shutdown.  Not clear why. For
	 * now, we kill the channel during pg_disconnect, but during interp
	 * shutdown we just accept leakage of the (fairly small) amount of
	 * memory taken for the channel state representation. (Note we are not
	 * leaking a socket, since libpq closed that already.) We tell the
	 * difference between pg_disconnect and interpreter shutdown by
	 * testing for interp != NULL, which is an undocumented but apparently
	 * safe way to tell.
	 */
#if TCL_MAJOR_VERSION >= 8
	if (connid->notifier_channel != NULL && interp != NULL)
		Tcl_UnregisterChannel(NULL, connid->notifier_channel);
#endif

	/*
	 * We must use Tcl_EventuallyFree because we don't want the connid
	 * struct to vanish instantly if Pg_Notify_EventProc is active for it.
	 * (Otherwise, closing the connection from inside a pg_listen callback
	 * could lead to coredump.)  Pg_Notify_EventProc can detect that the
	 * connection has been deleted from under it by checking connid->conn.
	 */
	Tcl_EventuallyFree((ClientData) connid, TCL_DYNAMIC);

	return 0;
}