326 lines
7.0 KiB
C
326 lines
7.0 KiB
C
|
/*
|
|||
|
* tclWinNotify.c --
|
|||
|
*
|
|||
|
* This file contains Windows-specific procedures for the notifier,
|
|||
|
* which is the lowest-level part of the Tcl event loop. This file
|
|||
|
* works together with ../generic/tclNotify.c.
|
|||
|
*
|
|||
|
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
|
|||
|
*
|
|||
|
* See the file "license.terms" for information on usage and redistribution
|
|||
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|||
|
*
|
|||
|
* SCCS: @(#) tclWinNotify.c 1.17 97/05/23 10:48:44
|
|||
|
*/
|
|||
|
|
|||
|
#include "tclInt.h"
|
|||
|
#include "tclPort.h"
|
|||
|
#include <winsock.h>
|
|||
|
|
|||
|
/*
|
|||
|
* The follwing static indicates whether this module has been initialized.
|
|||
|
*/
|
|||
|
|
|||
|
static int initialized = 0;
|
|||
|
|
|||
|
#define INTERVAL_TIMER 1 /* Handle of interval timer. */
|
|||
|
|
|||
|
/*
|
|||
|
* The following static structure contains the state information for the
|
|||
|
* Windows implementation of the Tcl notifier.
|
|||
|
*/
|
|||
|
|
|||
|
static struct {
|
|||
|
HWND hwnd; /* Messaging window. */
|
|||
|
int timeout; /* Current timeout value. */
|
|||
|
int timerActive; /* 1 if interval timer is running. */
|
|||
|
} notifier;
|
|||
|
|
|||
|
/*
|
|||
|
* Static routines defined in this file.
|
|||
|
*/
|
|||
|
|
|||
|
static void InitNotifier(void);
|
|||
|
static void NotifierExitHandler(ClientData clientData);
|
|||
|
static LRESULT CALLBACK NotifierProc(HWND hwnd, UINT message,
|
|||
|
WPARAM wParam, LPARAM lParam);
|
|||
|
static void UpdateTimer(int timeout);
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* InitNotifier --
|
|||
|
*
|
|||
|
* Initializes the notifier window.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Creates a new notifier window and window class.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
InitNotifier(void)
|
|||
|
{
|
|||
|
WNDCLASS class;
|
|||
|
|
|||
|
initialized = 1;
|
|||
|
notifier.timerActive = 0;
|
|||
|
class.style = 0;
|
|||
|
class.cbClsExtra = 0;
|
|||
|
class.cbWndExtra = 0;
|
|||
|
class.hInstance = TclWinGetTclInstance();
|
|||
|
class.hbrBackground = NULL;
|
|||
|
class.lpszMenuName = NULL;
|
|||
|
class.lpszClassName = "TclNotifier";
|
|||
|
class.lpfnWndProc = NotifierProc;
|
|||
|
class.hIcon = NULL;
|
|||
|
class.hCursor = NULL;
|
|||
|
|
|||
|
if (!RegisterClass(&class)) {
|
|||
|
panic("Unable to register TclNotifier window class");
|
|||
|
}
|
|||
|
notifier.hwnd = CreateWindow("TclNotifier", "TclNotifier", WS_TILED,
|
|||
|
0, 0, 0, 0, NULL, NULL, TclWinGetTclInstance(), NULL);
|
|||
|
Tcl_CreateExitHandler(NotifierExitHandler, NULL);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* NotifierExitHandler --
|
|||
|
*
|
|||
|
* This function is called to cleanup the notifier state before
|
|||
|
* Tcl is unloaded.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Destroys the notifier window.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
NotifierExitHandler(
|
|||
|
ClientData clientData) /* Old window proc */
|
|||
|
{
|
|||
|
initialized = 0;
|
|||
|
if (notifier.hwnd) {
|
|||
|
KillTimer(notifier.hwnd, INTERVAL_TIMER);
|
|||
|
DestroyWindow(notifier.hwnd);
|
|||
|
UnregisterClass("TclNotifier", TclWinGetTclInstance());
|
|||
|
notifier.hwnd = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* UpdateTimer --
|
|||
|
*
|
|||
|
* This function starts or stops the notifier interval timer.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
UpdateTimer(
|
|||
|
int timeout) /* ms timeout, 0 means cancel timer */
|
|||
|
{
|
|||
|
notifier.timeout = timeout;
|
|||
|
if (timeout != 0) {
|
|||
|
notifier.timerActive = 1;
|
|||
|
SetTimer(notifier.hwnd, INTERVAL_TIMER,
|
|||
|
(unsigned long) notifier.timeout, NULL);
|
|||
|
} else {
|
|||
|
notifier.timerActive = 0;
|
|||
|
KillTimer(notifier.hwnd, INTERVAL_TIMER);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tcl_SetTimer --
|
|||
|
*
|
|||
|
* This procedure sets the current notifier timer value. The
|
|||
|
* notifier will ensure that Tcl_ServiceAll() is called after
|
|||
|
* the specified interval, even if no events have occurred.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Replaces any previous timer.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tcl_SetTimer(
|
|||
|
Tcl_Time *timePtr) /* Maximum block time, or NULL. */
|
|||
|
{
|
|||
|
UINT timeout;
|
|||
|
|
|||
|
if (!initialized) {
|
|||
|
InitNotifier();
|
|||
|
}
|
|||
|
|
|||
|
if (!timePtr) {
|
|||
|
timeout = 0;
|
|||
|
} else {
|
|||
|
/*
|
|||
|
* Make sure we pass a non-zero value into the timeout argument.
|
|||
|
* Windows seems to get confused by zero length timers.
|
|||
|
*/
|
|||
|
timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
|
|||
|
if (timeout == 0) {
|
|||
|
timeout = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
UpdateTimer(timeout);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* NotifierProc --
|
|||
|
*
|
|||
|
* This procedure is invoked by Windows to process the timer
|
|||
|
* message whenever we are using an external dispatch loop.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* A standard windows result.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Services any pending events.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static LRESULT CALLBACK
|
|||
|
NotifierProc(
|
|||
|
HWND hwnd,
|
|||
|
UINT message,
|
|||
|
WPARAM wParam,
|
|||
|
LPARAM lParam)
|
|||
|
{
|
|||
|
|
|||
|
if (message != WM_TIMER) {
|
|||
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Process all of the runnable events.
|
|||
|
*/
|
|||
|
|
|||
|
Tcl_ServiceAll();
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tcl_WaitForEvent --
|
|||
|
*
|
|||
|
* This function is called by Tcl_DoOneEvent to wait for new
|
|||
|
* events on the message queue. If the block time is 0, then
|
|||
|
* Tcl_WaitForEvent just polls the event queue without blocking.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns -1 if a WM_QUIT message is detected, returns 1 if
|
|||
|
* a message was dispatched, otherwise returns 0.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Dispatches a message to a window procedure, which could do
|
|||
|
* anything.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
Tcl_WaitForEvent(
|
|||
|
Tcl_Time *timePtr) /* Maximum block time, or NULL. */
|
|||
|
{
|
|||
|
MSG msg;
|
|||
|
int timeout;
|
|||
|
|
|||
|
if (!initialized) {
|
|||
|
InitNotifier();
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Only use the interval timer for non-zero timeouts. This avoids
|
|||
|
* generating useless messages when we really just want to poll.
|
|||
|
*/
|
|||
|
|
|||
|
if (timePtr) {
|
|||
|
timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
|
|||
|
} else {
|
|||
|
timeout = 0;
|
|||
|
}
|
|||
|
UpdateTimer(timeout);
|
|||
|
|
|||
|
if (!timePtr || (timeout != 0)
|
|||
|
|| PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
|
|||
|
if (!GetMessage(&msg, NULL, 0, 0)) {
|
|||
|
|
|||
|
/*
|
|||
|
* The application is exiting, so repost the quit message
|
|||
|
* and start unwinding.
|
|||
|
*/
|
|||
|
|
|||
|
PostQuitMessage(msg.wParam);
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Handle timer expiration as a special case so we don't
|
|||
|
* claim to be doing work when we aren't.
|
|||
|
*/
|
|||
|
|
|||
|
if (msg.message == WM_TIMER && msg.hwnd == notifier.hwnd) {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
TranslateMessage(&msg);
|
|||
|
DispatchMessage(&msg);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tcl_Sleep --
|
|||
|
*
|
|||
|
* Delay execution for the specified number of milliseconds.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Time passes.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tcl_Sleep(ms)
|
|||
|
int ms; /* Number of milliseconds to sleep. */
|
|||
|
{
|
|||
|
Sleep(ms);
|
|||
|
}
|