/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinnotify.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclwinnotify.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 67 - (hide annotations) (download)
Mon Oct 31 00:57:34 2016 UTC (7 years, 11 months ago) by dashley
File MIME type: text/plain
File size: 13458 byte(s)
Header and footer cleanup.
1 dashley 64 /* $Header$ */
2 dashley 25 /*
3     * tclWinNotify.c --
4     *
5     * This file contains Windows-specific procedures for the notifier,
6     * which is the lowest-level part of the Tcl event loop. This file
7     * works together with ../generic/tclNotify.c.
8     *
9     * Copyright (c) 1995-1997 Sun Microsystems, Inc.
10     *
11     * See the file "license.terms" for information on usage and redistribution
12     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13     *
14     * RCS: @(#) $Id: tclwinnotify.c,v 1.1.1.1 2001/06/13 04:49:27 dtashley Exp $
15     */
16    
17     #include "tclWinInt.h"
18     #include <winsock.h>
19    
20     /*
21     * The follwing static indicates whether this module has been initialized.
22     */
23    
24     static int initialized = 0;
25    
26     #define INTERVAL_TIMER 1 /* Handle of interval timer. */
27    
28     #define WM_WAKEUP WM_USER /* Message that is send by
29     * Tcl_AlertNotifier. */
30     /*
31     * The following static structure contains the state information for the
32     * Windows implementation of the Tcl notifier. One of these structures
33     * is created for each thread that is using the notifier.
34     */
35    
36     typedef struct ThreadSpecificData {
37     CRITICAL_SECTION crit; /* Monitor for this notifier. */
38     DWORD thread; /* Identifier for thread associated with this
39     * notifier. */
40     HANDLE event; /* Event object used to wake up the notifier
41     * thread. */
42     int pending; /* Alert message pending, this field is
43     * locked by the notifierMutex. */
44     HWND hwnd; /* Messaging window. */
45     int timeout; /* Current timeout value. */
46     int timerActive; /* 1 if interval timer is running. */
47     } ThreadSpecificData;
48    
49     static Tcl_ThreadDataKey dataKey;
50    
51     extern TclStubs tclStubs;
52     /*
53     * The following static indicates the number of threads that have
54     * initialized notifiers. It controls the lifetime of the TclNotifier
55     * window class.
56     *
57     * You must hold the notifierMutex lock before accessing this variable.
58     */
59    
60     static int notifierCount = 0;
61     TCL_DECLARE_MUTEX(notifierMutex)
62    
63     /*
64     * Static routines defined in this file.
65     */
66    
67     static LRESULT CALLBACK NotifierProc(HWND hwnd, UINT message,
68     WPARAM wParam, LPARAM lParam);
69    
70    
71     /*
72     *----------------------------------------------------------------------
73     *
74     * Tcl_InitNotifier --
75     *
76     * Initializes the platform specific notifier state.
77     *
78     * Results:
79     * Returns a handle to the notifier state for this thread..
80     *
81     * Side effects:
82     * None.
83     *
84     *----------------------------------------------------------------------
85     */
86    
87     ClientData
88     Tcl_InitNotifier()
89     {
90     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
91     WNDCLASS class;
92    
93     /*
94     * Register Notifier window class if this is the first thread to
95     * use this module.
96     */
97    
98     Tcl_MutexLock(&notifierMutex);
99     if (notifierCount == 0) {
100     class.style = 0;
101     class.cbClsExtra = 0;
102     class.cbWndExtra = 0;
103     class.hInstance = TclWinGetTclInstance();
104     class.hbrBackground = NULL;
105     class.lpszMenuName = NULL;
106     class.lpszClassName = "TclNotifier";
107     class.lpfnWndProc = NotifierProc;
108     class.hIcon = NULL;
109     class.hCursor = NULL;
110    
111     if (!RegisterClassA(&class)) {
112     panic("Unable to register TclNotifier window class");
113     }
114     }
115     notifierCount++;
116     Tcl_MutexUnlock(&notifierMutex);
117    
118     tsdPtr->pending = 0;
119     tsdPtr->timerActive = 0;
120    
121     InitializeCriticalSection(&tsdPtr->crit);
122    
123     tsdPtr->hwnd = NULL;
124     tsdPtr->thread = GetCurrentThreadId();
125     tsdPtr->event = CreateEvent(NULL, TRUE /* manual */,
126     FALSE /* !signaled */, NULL);
127    
128     return (ClientData) tsdPtr;
129     }
130    
131     /*
132     *----------------------------------------------------------------------
133     *
134     * Tcl_FinalizeNotifier --
135     *
136     * This function is called to cleanup the notifier state before
137     * a thread is terminated.
138     *
139     * Results:
140     * None.
141     *
142     * Side effects:
143     * May dispose of the notifier window and class.
144     *
145     *----------------------------------------------------------------------
146     */
147    
148     void
149     Tcl_FinalizeNotifier(clientData)
150     ClientData clientData; /* Pointer to notifier data. */
151     {
152     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
153    
154     DeleteCriticalSection(&tsdPtr->crit);
155     CloseHandle(tsdPtr->event);
156    
157     /*
158     * Clean up the timer and messaging window for this thread.
159     */
160    
161     if (tsdPtr->hwnd) {
162     KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
163     DestroyWindow(tsdPtr->hwnd);
164     }
165    
166     /*
167     * If this is the last thread to use the notifier, unregister
168     * the notifier window class.
169     */
170    
171     Tcl_MutexLock(&notifierMutex);
172     notifierCount--;
173     if (notifierCount == 0) {
174     UnregisterClassA("TclNotifier", TclWinGetTclInstance());
175     }
176     Tcl_MutexUnlock(&notifierMutex);
177     }
178    
179     /*
180     *----------------------------------------------------------------------
181     *
182     * Tcl_AlertNotifier --
183     *
184     * Wake up the specified notifier from any thread. This routine
185     * is called by the platform independent notifier code whenever
186     * the Tcl_ThreadAlert routine is called. This routine is
187     * guaranteed not to be called on a given notifier after
188     * Tcl_FinalizeNotifier is called for that notifier. This routine
189     * is typically called from a thread other than the notifier's
190     * thread.
191     *
192     * Results:
193     * None.
194     *
195     * Side effects:
196     * Sends a message to the messaging window for the notifier
197     * if there isn't already one pending.
198     *
199     *----------------------------------------------------------------------
200     */
201    
202     void
203     Tcl_AlertNotifier(clientData)
204     ClientData clientData; /* Pointer to thread data. */
205     {
206     ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData;
207    
208     /*
209     * Note that we do not need to lock around access to the hwnd
210     * because the race condition has no effect since any race condition
211     * implies that the notifier thread is already awake.
212     */
213    
214     if (tsdPtr->hwnd) {
215     /*
216     * We do need to lock around access to the pending flag.
217     */
218    
219     EnterCriticalSection(&tsdPtr->crit);
220     if (!tsdPtr->pending) {
221     PostMessage(tsdPtr->hwnd, WM_WAKEUP, 0, 0);
222     }
223     tsdPtr->pending = 1;
224     LeaveCriticalSection(&tsdPtr->crit);
225     } else {
226     SetEvent(tsdPtr->event);
227     }
228     }
229    
230     /*
231     *----------------------------------------------------------------------
232     *
233     * Tcl_SetTimer --
234     *
235     * This procedure sets the current notifier timer value. The
236     * notifier will ensure that Tcl_ServiceAll() is called after
237     * the specified interval, even if no events have occurred.
238     *
239     * Results:
240     * None.
241     *
242     * Side effects:
243     * Replaces any previous timer.
244     *
245     *----------------------------------------------------------------------
246     */
247    
248     void
249     Tcl_SetTimer(
250     Tcl_Time *timePtr) /* Maximum block time, or NULL. */
251     {
252     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
253     UINT timeout;
254    
255     /*
256     * Allow the notifier to be hooked. This may not make sense
257     * on Windows, but mirrors the UNIX hook.
258     */
259    
260     if (tclStubs.tcl_SetTimer != Tcl_SetTimer) {
261     tclStubs.tcl_SetTimer(timePtr);
262     return;
263     }
264    
265     /*
266     * We only need to set up an interval timer if we're being called
267     * from an external event loop. If we don't have a window handle
268     * then we just return immediately and let Tcl_WaitForEvent handle
269     * timeouts.
270     */
271    
272     if (!tsdPtr->hwnd) {
273     return;
274     }
275    
276     if (!timePtr) {
277     timeout = 0;
278     } else {
279     /*
280     * Make sure we pass a non-zero value into the timeout argument.
281     * Windows seems to get confused by zero length timers.
282     */
283    
284     timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
285     if (timeout == 0) {
286     timeout = 1;
287     }
288     }
289     tsdPtr->timeout = timeout;
290     if (timeout != 0) {
291     tsdPtr->timerActive = 1;
292     SetTimer(tsdPtr->hwnd, INTERVAL_TIMER,
293     (unsigned long) tsdPtr->timeout, NULL);
294     } else {
295     tsdPtr->timerActive = 0;
296     KillTimer(tsdPtr->hwnd, INTERVAL_TIMER);
297     }
298     }
299    
300     /*
301     *----------------------------------------------------------------------
302     *
303     * Tcl_ServiceModeHook --
304     *
305     * This function is invoked whenever the service mode changes.
306     *
307     * Results:
308     * None.
309     *
310     * Side effects:
311     * If this is the first time the notifier is set into
312     * TCL_SERVICE_ALL, then the communication window is created.
313     *
314     *----------------------------------------------------------------------
315     */
316    
317     void
318     Tcl_ServiceModeHook(mode)
319     int mode; /* Either TCL_SERVICE_ALL, or
320     * TCL_SERVICE_NONE. */
321     {
322     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
323    
324     /*
325     * If this is the first time that the notifier has been used from a
326     * modal loop, then create a communication window. Note that after
327     * this point, the application needs to service events in a timely
328     * fashion or Windows will hang waiting for the window to respond
329     * to synchronous system messages. At some point, we may want to
330     * consider destroying the window if we leave the modal loop, but
331     * for now we'll leave it around.
332     */
333    
334     if (mode == TCL_SERVICE_ALL && !tsdPtr->hwnd) {
335     tsdPtr->hwnd = CreateWindowA("TclNotifier", "TclNotifier", WS_TILED,
336     0, 0, 0, 0, NULL, NULL, TclWinGetTclInstance(), NULL);
337     /*
338     * Send an initial message to the window to ensure that we wake up the
339     * notifier once we get into the modal loop. This will force the
340     * notifier to recompute the timeout value and schedule a timer
341     * if one is needed.
342     */
343    
344     Tcl_AlertNotifier((ClientData)tsdPtr);
345     }
346     }
347    
348     /*
349     *----------------------------------------------------------------------
350     *
351     * NotifierProc --
352     *
353     * This procedure is invoked by Windows to process events on
354     * the notifier window. Messages will be sent to this window
355     * in response to external timer events or calls to
356     * TclpAlertTsdPtr->
357     *
358     * Results:
359     * A standard windows result.
360     *
361     * Side effects:
362     * Services any pending events.
363     *
364     *----------------------------------------------------------------------
365     */
366    
367     static LRESULT CALLBACK
368     NotifierProc(
369     HWND hwnd,
370     UINT message,
371     WPARAM wParam,
372     LPARAM lParam)
373     {
374     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
375    
376     if (message == WM_WAKEUP) {
377     EnterCriticalSection(&tsdPtr->crit);
378     tsdPtr->pending = 0;
379     LeaveCriticalSection(&tsdPtr->crit);
380     } else if (message != WM_TIMER) {
381     return DefWindowProc(hwnd, message, wParam, lParam);
382     }
383    
384     /*
385     * Process all of the runnable events.
386     */
387    
388     Tcl_ServiceAll();
389     return 0;
390     }
391    
392     /*
393     *----------------------------------------------------------------------
394     *
395     * Tcl_WaitForEvent --
396     *
397     * This function is called by Tcl_DoOneEvent to wait for new
398     * events on the message queue. If the block time is 0, then
399     * Tcl_WaitForEvent just polls the event queue without blocking.
400     *
401     * Results:
402     * Returns -1 if a WM_QUIT message is detected, returns 1 if
403     * a message was dispatched, otherwise returns 0.
404     *
405     * Side effects:
406     * Dispatches a message to a window procedure, which could do
407     * anything.
408     *
409     *----------------------------------------------------------------------
410     */
411    
412     int
413     Tcl_WaitForEvent(
414     Tcl_Time *timePtr) /* Maximum block time, or NULL. */
415     {
416     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
417     MSG msg;
418     DWORD timeout, result;
419     int status;
420    
421     /*
422     * Allow the notifier to be hooked. This may not make
423     * sense on windows, but mirrors the UNIX hook.
424     */
425    
426     if (tclStubs.tcl_WaitForEvent != Tcl_WaitForEvent) {
427     return tclStubs.tcl_WaitForEvent(timePtr);
428     }
429    
430     /*
431     * Compute the timeout in milliseconds.
432     */
433    
434     if (timePtr) {
435     timeout = timePtr->sec * 1000 + timePtr->usec / 1000;
436     } else {
437     timeout = INFINITE;
438     }
439    
440     /*
441     * Check to see if there are any messages in the queue before waiting
442     * because MsgWaitForMultipleObjects will not wake up if there are events
443     * currently sitting in the queue.
444     */
445    
446     if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
447     /*
448     * Wait for something to happen (a signal from another thread, a
449     * message, or timeout).
450     */
451    
452     result = MsgWaitForMultipleObjects(1, &tsdPtr->event, FALSE, timeout,
453     QS_ALLINPUT);
454     }
455    
456     /*
457     * Check to see if there are any messages to process.
458     */
459    
460     if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
461     /*
462     * Retrieve and dispatch the first message.
463     */
464    
465     result = GetMessage(&msg, NULL, 0, 0);
466     if (result == 0) {
467     /*
468     * We received a request to exit this thread (WM_QUIT), so
469     * propagate the quit message and start unwinding.
470     */
471    
472     PostQuitMessage(msg.wParam);
473     status = -1;
474     } else if (result == -1) {
475     /*
476     * We got an error from the system. I have no idea why this would
477     * happen, so we'll just unwind.
478     */
479    
480     status = -1;
481     } else {
482     TranslateMessage(&msg);
483     DispatchMessage(&msg);
484     status = 1;
485     }
486     } else {
487     status = 0;
488     }
489    
490     ResetEvent(tsdPtr->event);
491     return status;
492     }
493    
494     /*
495     *----------------------------------------------------------------------
496     *
497     * Tcl_Sleep --
498     *
499     * Delay execution for the specified number of milliseconds.
500     *
501     * Results:
502     * None.
503     *
504     * Side effects:
505     * Time passes.
506     *
507     *----------------------------------------------------------------------
508     */
509    
510     void
511     Tcl_Sleep(ms)
512     int ms; /* Number of milliseconds to sleep. */
513     {
514     Sleep(ms);
515     }
516    
517 dashley 67 /* End of tclwinnotify.c */

Properties

Name Value
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25