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);
|
||
}
|