XEmacs 21.2.41 "Polyhymnia".
[chise/xemacs-chise.git.1] / src / event-msw.c
1 /* The mswindows event_stream interface.
2    Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
3    Copyright (C) 1995 Sun Microsystems, Inc.
4    Copyright (C) 1996, 2000 Ben Wing.
5    Copyright (C) 1997 Jonathan Harris.
6
7 This file is part of XEmacs.
8
9 XEmacs is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by the
11 Free Software Foundation; either version 2, or (at your option) any
12 later version.
13
14 XEmacs is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with XEmacs; see the file COPYING.  If not, write to
21 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.  */
23
24 /* Synched up with: Not in FSF. */
25
26 /* Authorship:
27
28    Ultimately based on FSF.
29    Rewritten by Ben Wing.
30    Rewritten for mswindows by Jonathan Harris, November 1997 for 21.0.
31    Subprocess and modal loop support by Kirill M. Katsnelson.
32  */
33
34 #include <config.h>
35 #include "lisp.h"
36
37 #include "console-msw.h"
38
39 #ifdef HAVE_SCROLLBARS
40 # include "scrollbar-msw.h"
41 #endif
42
43 #ifdef HAVE_MENUBARS
44 # include "menubar.h"
45 # include "menubar-msw.h"
46 #endif
47
48 #ifdef HAVE_DRAGNDROP
49 # include "dragdrop.h"
50 #endif
51
52 #include "device.h"
53 #include "events.h"
54 #include "frame.h"
55 #include "buffer.h"
56 #include "faces.h"
57 #include "lstream.h"
58 #include "process.h"
59 #include "redisplay.h"
60 #include "select.h"
61 #include "window.h"
62 #include "sysproc.h"
63 #include "syswait.h"
64 #include "systime.h"
65 #include "sysdep.h"
66 #include "objects-msw.h"
67
68 #include "events-mod.h"
69 #ifdef HAVE_MSG_SELECT
70 #include "sysfile.h"
71 #include "console-tty.h"
72 #elif defined(CYGWIN)
73 typedef unsigned int SOCKET;
74 #endif
75 #include <io.h>
76 #include <errno.h>
77
78 #if !(defined(CYGWIN) || defined(MINGW))
79 # include <shlobj.h>    /* For IShellLink */
80 #endif
81
82 #ifdef HAVE_MENUBARS
83 #define ADJR_MENUFLAG TRUE
84 #else
85 #define ADJR_MENUFLAG FALSE
86 #endif
87
88 /* Fake key modifier which is attached to a quit char event.
89    Removed upon dequeueing an event */
90 #define FAKE_MOD_QUIT   0x80
91
92 /* Timer ID used for button2 emulation */
93 #define BUTTON_2_TIMER_ID 1
94
95 static Lisp_Object mswindows_find_frame (HWND hwnd);
96 static Lisp_Object mswindows_find_console (HWND hwnd);
97 static Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
98                                                   int extendedp);
99 static int mswindows_modifier_state (BYTE* keymap, DWORD fwKeys,
100                                      int has_AltGr);
101 static void mswindows_set_chord_timer (HWND hwnd);
102 static int mswindows_button2_near_enough (POINTS p1, POINTS p2);
103 static int mswindows_current_layout_has_AltGr (void);
104 static int mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam,
105                                               int downp, int keyp);
106
107 static struct event_stream *mswindows_event_stream;
108
109 #ifdef HAVE_MSG_SELECT
110 extern SELECT_TYPE input_wait_mask, non_fake_input_wait_mask;
111 extern SELECT_TYPE process_only_mask, tty_only_mask;
112 SELECT_TYPE zero_mask;
113 extern int signal_event_pipe_initialized;
114 int windows_fd;
115 #endif
116
117 /*
118  * Two separate queues, for efficiency, one (_u_) for user events, and
119  * another (_s_) for non-user ones. We always return events out of the
120  * first one until it is empty and only then proceed with the second
121  * one.
122  */
123 static Lisp_Object mswindows_u_dispatch_event_queue, mswindows_u_dispatch_event_queue_tail;
124 static Lisp_Object mswindows_s_dispatch_event_queue, mswindows_s_dispatch_event_queue_tail;
125
126 /* The number of things we can wait on */
127 #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1)
128
129 #ifndef HAVE_MSG_SELECT
130 /* List of mswindows waitable handles. */
131 static HANDLE mswindows_waitable_handles[MAX_WAITABLE];
132
133 /* Number of wait handles */
134 static int mswindows_waitable_count=0;
135 #endif /* HAVE_MSG_SELECT */
136
137 /* Brush for painting widgets */
138 static HBRUSH widget_brush = 0;
139 static LONG     last_widget_brushed = 0;
140
141 /* Count of quit chars currently in the queue */
142 /* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc()
143    Decremented in mswindows_dequeue_dispatch_event() */
144 int mswindows_quit_chars_count = 0;
145
146 /* These are Lisp integers; see DEFVARS in this file for description. */
147 int mswindows_dynamic_frame_resize;
148 int mswindows_alt_by_itself_activates_menu;
149 int mswindows_num_mouse_buttons;
150 int mswindows_mouse_button_max_skew_x;
151 int mswindows_mouse_button_max_skew_y;
152 int mswindows_mouse_button_tolerance;
153
154 #ifdef DEBUG_XEMACS
155 int debug_mswindows_events;
156 #endif
157
158 /* This is the event signaled by the event pump.
159    See mswindows_pump_outstanding_events for comments */
160 static Lisp_Object mswindows_error_caught_in_modal_loop;
161 static int mswindows_in_modal_loop;
162
163 /* Count of wound timers */
164 static int mswindows_pending_timers_count;
165
166 static DWORD mswindows_last_mouse_button_state;
167 \f
168 /************************************************************************/
169 /*                Pipe instream - reads process output                  */
170 /************************************************************************/
171
172 #define PIPE_READ_DELAY 20
173
174 #define HANDLE_TO_USID(h) ((USID)(h))
175
176 #define NTPIPE_SLURP_STREAM_DATA(stream) \
177   LSTREAM_TYPE_DATA (stream, ntpipe_slurp)
178
179 /* This structure is allocated by the main thread, and is deallocated
180    in the thread upon exit.  There are situations when a thread
181    remains blocked for a long time, much longer than the lstream
182    exists. For example, "start notepad" command is issued from the
183    shell, then the shell is closed by C-c C-d. Although the shell
184    process exits, its output pipe will not get closed until the
185    notepad process exits also, because it inherits the pipe form the
186    shell. In this case, we abandon the thread, and let it live until
187    all such processes exit. While struct ntpipe_slurp_stream is
188    deallocated in this case, ntpipe_slurp_stream_shared_data are not. */
189
190 struct ntpipe_slurp_stream_shared_data
191 {
192   HANDLE hev_thread;    /* Our thread blocks on this, signaled by caller */
193   /* This is a manual-reset object.              */
194   HANDLE hev_caller;    /* Caller blocks on this, and we signal it       */
195   /* This is a manual-reset object.              */
196   HANDLE hev_unsleep;   /* Pipe read delay is canceled if this is set    */
197   /* This is a manual-reset object.              */
198   HANDLE hpipe;         /* Pipe read end handle.                         */
199   LONG   die_p;         /* Thread must exit ASAP if non-zero             */
200   BOOL   eof_p   : 1;   /* Set when thread saw EOF                       */
201   BOOL   error_p : 1;   /* Read error other than EOF/broken pipe         */
202   BOOL   inuse_p : 1;   /* this structure is in use                      */
203   LONG   lock_count;    /* Client count of this struct, 0=safe to free   */
204   BYTE   onebyte;       /* One byte buffer read by thread                */
205 };
206
207 #define MAX_SLURP_STREAMS 32
208 struct ntpipe_slurp_stream_shared_data
209 shared_data_block[MAX_SLURP_STREAMS]={{0}};
210
211 struct ntpipe_slurp_stream
212 {
213   LPARAM user_data;     /* Any user data stored in the stream object     */
214   struct ntpipe_slurp_stream_shared_data* thread_data;
215 };
216
217 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-input", lstream_ntpipe_slurp,
218                                sizeof (struct ntpipe_slurp_stream));
219
220 /* This function is thread-safe, and is called from either thread
221    context. It serializes freeing shared data structure */
222 static void
223 slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s)
224 {
225   if (InterlockedDecrement (&s->lock_count) == 0)
226     {
227       /* Destroy events */
228       CloseHandle (s->hev_thread);
229       CloseHandle (s->hev_caller);
230       CloseHandle (s->hev_unsleep);
231       s->inuse_p = 0;
232     }
233 }
234
235 static struct ntpipe_slurp_stream_shared_data*
236 slurper_allocate_shared_data (void)
237 {
238   int i=0;
239   for (i=0; i<MAX_SLURP_STREAMS; i++)
240     {
241       if (!shared_data_block[i].inuse_p)
242         {
243           shared_data_block[i].inuse_p=1;
244           return &shared_data_block[i];
245         }
246     }
247   return (struct ntpipe_slurp_stream_shared_data*)0;
248 }
249
250 static DWORD WINAPI
251 slurp_thread (LPVOID vparam)
252 {
253   struct ntpipe_slurp_stream_shared_data *s =
254     (struct ntpipe_slurp_stream_shared_data*)vparam;
255
256   for (;;)
257     {
258       /* Read one byte from the pipe */
259       DWORD actually_read;
260       if (!ReadFile (s->hpipe, &s->onebyte, 1, &actually_read, NULL))
261         {
262           DWORD err = GetLastError ();
263           if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA)
264             s->eof_p = TRUE;
265           else
266             s->error_p = TRUE;
267         }
268       else if (actually_read == 0)
269         s->eof_p = TRUE;
270
271       /* We must terminate on an error or eof */
272       if (s->eof_p || s->error_p)
273         InterlockedIncrement (&s->die_p);
274
275       /* Before we notify caller, we unsignal our event. */
276       ResetEvent (s->hev_thread);
277
278       /* Now we got something to notify caller, either a byte or an
279          error/eof indication. Before we do, allow internal pipe
280          buffer to accumulate little bit more data.
281          Reader function pulses this event before waiting for
282          a character, to avoid pipe delay, and to get the byte
283          immediately. */
284       if (!s->die_p)
285         WaitForSingleObject (s->hev_unsleep, PIPE_READ_DELAY);
286
287       /* Either make event loop generate a process event, or
288          inblock reader */
289       SetEvent (s->hev_caller);
290
291       /* Cleanup and exit if we're shot off */
292       if (s->die_p)
293         break;
294
295       /* Block until the client finishes with retrieving the rest of
296          pipe data */
297       WaitForSingleObject (s->hev_thread, INFINITE);
298     }
299
300   slurper_free_shared_data_maybe (s);
301
302   return 0;
303 }
304
305 static Lisp_Object
306 make_ntpipe_input_stream (HANDLE hpipe, LPARAM param)
307 {
308   Lisp_Object obj;
309   Lstream *lstr = Lstream_new (lstream_ntpipe_slurp, "r");
310   struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA (lstr);
311   DWORD thread_id_unused;
312   HANDLE hthread;
313
314   /* We deal only with pipes, for we're using PeekNamedPipe api */
315   assert (GetFileType (hpipe) == FILE_TYPE_PIPE);
316
317   s->thread_data = slurper_allocate_shared_data();
318
319   /* Create reader thread. This could fail, so do not create events
320      until thread is created */
321   hthread = CreateThread (NULL, 0, slurp_thread, (LPVOID)s->thread_data,
322                           CREATE_SUSPENDED, &thread_id_unused);
323   if (hthread == NULL)
324     {
325       Lstream_delete (lstr);
326       s->thread_data->inuse_p=0;
327       return Qnil;
328     }
329
330   /* Shared data are initially owned by both main and slurper
331      threads. */
332   s->thread_data->lock_count = 2;
333   s->thread_data->die_p = 0;
334   s->thread_data->eof_p = FALSE;
335   s->thread_data->error_p = FALSE;
336   s->thread_data->hpipe = hpipe;
337   s->user_data = param;
338
339   /* hev_thread is a manual-reset event, initially signaled */
340   s->thread_data->hev_thread = CreateEvent (NULL, TRUE, TRUE, NULL);
341   /* hev_caller is a manual-reset event, initially nonsignaled */
342   s->thread_data->hev_caller = CreateEvent (NULL, TRUE, FALSE, NULL);
343   /* hev_unsleep is a manual-reset event, initially nonsignaled */
344   s->thread_data->hev_unsleep = CreateEvent (NULL, TRUE, FALSE, NULL);
345
346   /* Now let it go */
347   ResumeThread (hthread);
348   CloseHandle (hthread);
349
350   lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
351   XSETLSTREAM (obj, lstr);
352   return obj;
353 }
354
355 static LPARAM
356 get_ntpipe_input_stream_param (Lstream *stream)
357 {
358   struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream);
359   return s->user_data;
360 }
361
362 static HANDLE
363 get_ntpipe_input_stream_waitable (Lstream *stream)
364 {
365   struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream);
366   return s->thread_data->hev_caller;
367 }
368
369 static ssize_t
370 ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size)
371 {
372   /* This function must be called from the main thread only */
373   struct ntpipe_slurp_stream_shared_data* s =
374     NTPIPE_SLURP_STREAM_DATA(stream)->thread_data;
375
376   if (!s->die_p)
377     {
378       DWORD wait_result;
379       /* Disallow pipe read delay for the thread: we need a character
380          ASAP */
381       SetEvent (s->hev_unsleep);
382
383       /* Check if we have a character ready. Give it a short delay,
384          for the thread to awake from pipe delay, just ion case*/
385       wait_result = WaitForSingleObject (s->hev_caller, 2);
386
387       /* Revert to the normal sleep behavior. */
388       ResetEvent (s->hev_unsleep);
389
390       /* If there's no byte buffered yet, give up */
391       if (wait_result == WAIT_TIMEOUT)
392         {
393           errno = EAGAIN;
394           return -1;
395         }
396     }
397
398   /* Reset caller unlock event now, as we've handled the pending
399      process output event */
400   ResetEvent (s->hev_caller);
401
402   /* It is now safe to do anything with contents of S, except for
403      changing s->die_p, which still should be interlocked */
404
405   if (s->eof_p)
406     return 0;
407   if (s->error_p || s->die_p)
408     return -1;
409
410   /* Ok, there were no error neither eof - we've got a byte from the
411      pipe */
412   *(data++) = s->onebyte;
413   --size;
414
415   {
416     DWORD bytes_read = 0;
417     if (size > 0)
418       {
419         DWORD bytes_available;
420
421         /* If the api call fails, return at least one byte already
422            read.  ReadFile in thread will return error */
423         if (PeekNamedPipe (s->hpipe, NULL, 0, NULL, &bytes_available, NULL))
424           {
425
426             /* Fetch available bytes. The same consideration applies,
427                so do not check for errors. ReadFile in the thread will
428                fail if the next call fails. */
429             if (bytes_available)
430               ReadFile (s->hpipe, data, min (bytes_available, size),
431                         &bytes_read, NULL);
432           }
433
434         /* Now we can unblock thread, so it attempts to read more */
435         SetEvent (s->hev_thread);
436         return bytes_read + 1;
437       }
438   }
439   return 0;
440 }
441
442 static int
443 ntpipe_slurp_closer (Lstream *stream)
444 {
445   /* This function must be called from the main thread only */
446   struct ntpipe_slurp_stream_shared_data* s =
447     NTPIPE_SLURP_STREAM_DATA(stream)->thread_data;
448
449   /* Force thread to stop */
450   InterlockedIncrement (&s->die_p);
451
452   /* Set events which could possibly block slurper. Let it finish soon
453      or later. */
454   SetEvent (s->hev_unsleep);
455   SetEvent (s->hev_thread);
456
457   /* Unlock and maybe free shared data */
458   slurper_free_shared_data_maybe (s);
459
460   return 0;
461 }
462
463 static void
464 init_slurp_stream (void)
465 {
466   LSTREAM_HAS_METHOD (ntpipe_slurp, reader);
467   LSTREAM_HAS_METHOD (ntpipe_slurp, closer);
468 }
469 \f
470 /************************************************************************/
471 /*                Pipe outstream - writes process input                 */
472 /************************************************************************/
473
474 #define NTPIPE_SHOVE_STREAM_DATA(stream) \
475   LSTREAM_TYPE_DATA (stream, ntpipe_shove)
476
477 #define MAX_SHOVE_BUFFER_SIZE 512
478
479 struct ntpipe_shove_stream
480 {
481   LPARAM user_data;     /* Any user data stored in the stream object     */
482   HANDLE hev_thread;    /* Our thread blocks on this, signaled by caller */
483   /* This is an auto-reset object.               */
484   HANDLE hpipe;         /* Pipe write end handle.                        */
485   HANDLE hthread;       /* Reader thread handle.                         */
486   char   buffer[MAX_SHOVE_BUFFER_SIZE]; /* Buffer being written          */
487   DWORD  size;          /* Number of bytes to write                      */
488   LONG   die_p;         /* Thread must exit ASAP if non-zero             */
489   LONG   idle_p;        /* Non-zero if thread is waiting for job         */
490   BOOL   error_p : 1;   /* Read error other than EOF/broken pipe         */
491   BOOL   blocking_p : 1;/* Last write attempt would cause blocking       */
492 };
493
494 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-output", lstream_ntpipe_shove,
495                                sizeof (struct ntpipe_shove_stream));
496
497 #ifndef HAVE_MSG_SELECT
498 static DWORD WINAPI
499 shove_thread (LPVOID vparam)
500 {
501   struct ntpipe_shove_stream *s = (struct ntpipe_shove_stream*) vparam;
502
503   for (;;)
504     {
505       DWORD bytes_written;
506
507       /* Block on event and wait for a job */
508       InterlockedIncrement (&s->idle_p);
509       WaitForSingleObject (s->hev_thread, INFINITE);
510
511       /* Write passed buffer if any */
512       if (s->size > 0)
513         {
514          if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL)
515              || bytes_written != s->size)
516            {
517              s->error_p = TRUE;
518              InterlockedIncrement (&s->die_p);
519            }
520          /* Set size to zero so we won't write it again if the closer sets
521             die_p and kicks us */
522          s->size = 0;
523         }
524
525       if (s->die_p)
526         break;
527     }
528
529   return 0;
530 }
531
532 static Lisp_Object
533 make_ntpipe_output_stream (HANDLE hpipe, LPARAM param)
534 {
535   Lisp_Object obj;
536   Lstream *lstr = Lstream_new (lstream_ntpipe_shove, "w");
537   struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA (lstr);
538   DWORD thread_id_unused;
539
540   s->die_p = 0;
541   s->error_p = FALSE;
542   s->hpipe = hpipe;
543   s->user_data = param;
544
545   /* Create reader thread. This could fail, so do not
546      create the event until thread is created */
547   s->hthread = CreateThread (NULL, 0, shove_thread, (LPVOID)s,
548                              CREATE_SUSPENDED, &thread_id_unused);
549   if (s->hthread == NULL)
550     {
551       Lstream_delete (lstr);
552       return Qnil;
553     }
554
555   /* Set the priority of the thread higher so we don't end up waiting
556      on it to send things. */
557   if (!SetThreadPriority (s->hthread, THREAD_PRIORITY_HIGHEST))
558     {
559       CloseHandle (s->hthread);
560       Lstream_delete (lstr);
561       return Qnil;
562     }
563
564   /* hev_thread is an auto-reset event, initially nonsignaled */
565   s->hev_thread = CreateEvent (NULL, FALSE, FALSE, NULL);
566
567   /* Now let it go */
568   ResumeThread (s->hthread);
569
570   lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
571   XSETLSTREAM (obj, lstr);
572   return obj;
573 }
574
575 static LPARAM
576 get_ntpipe_output_stream_param (Lstream *stream)
577 {
578   struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
579   return s->user_data;
580 }
581 #endif
582
583 static ssize_t
584 ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size)
585 {
586   struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
587
588   if (s->error_p)
589     return -1;
590
591   s->blocking_p = !s->idle_p;
592   if (s->blocking_p)
593     return 0;
594
595   if (size>MAX_SHOVE_BUFFER_SIZE)
596     return 0;
597
598   memcpy (s->buffer, data, size);
599   s->size = size;
600
601   /* Start output */
602   InterlockedDecrement (&s->idle_p);
603   SetEvent (s->hev_thread);
604   /* Give it a chance to run -- this dramatically improves performance
605      of things like crypt. */
606   if (xSwitchToThread) /* not in Win9x or NT 3.51 */
607     (void) xSwitchToThread ();
608   return size;
609 }
610
611 static int
612 ntpipe_shove_was_blocked_p (Lstream *stream)
613 {
614   struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
615   return s->blocking_p;
616 }
617
618 static int
619 ntpipe_shove_closer (Lstream *stream)
620 {
621   struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
622
623   /* Force thread stop */
624   InterlockedIncrement (&s->die_p);
625
626   /* Thread will end upon unblocking.  If it's already unblocked this will
627      do nothing, but the thread won't look at die_p until it's written any
628      pending output. */
629   SetEvent (s->hev_thread);
630
631   /* Wait while thread terminates */
632   WaitForSingleObject (s->hthread, INFINITE);
633
634   /* Close pipe handle, possibly breaking it */
635   CloseHandle (s->hpipe);
636
637   /* Close the thread handle */
638   CloseHandle (s->hthread);
639
640   /* Destroy the event */
641   CloseHandle (s->hev_thread);
642
643   return 0;
644 }
645
646 static void
647 init_shove_stream (void)
648 {
649   LSTREAM_HAS_METHOD (ntpipe_shove, writer);
650   LSTREAM_HAS_METHOD (ntpipe_shove, was_blocked_p);
651   LSTREAM_HAS_METHOD (ntpipe_shove, closer);
652 }
653 \f
654 /************************************************************************/
655 /*                         Winsock I/O stream                           */
656 /************************************************************************/
657 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
658
659 #define WINSOCK_READ_BUFFER_SIZE 1024
660
661 struct winsock_stream
662 {
663   LPARAM user_data;             /* Any user data stored in the stream object */
664   SOCKET s;                     /* Socket handle (which is a Win32 handle)   */
665   OVERLAPPED ov;                /* Overlapped I/O structure                  */
666   void* buffer;                 /* Buffer. Allocated for input stream only   */
667   unsigned long bufsize;        /* Number of bytes last read                 */
668   unsigned long bufpos;         /* Position in buffer for next fetch         */
669   unsigned int error_p :1;      /* I/O Error seen                            */
670   unsigned int eof_p :1;        /* EOF Error seen                            */
671   unsigned int pending_p :1;    /* There is a pending I/O operation          */
672   unsigned int blocking_p :1;   /* Last write attempt would block            */
673 };
674
675 #define WINSOCK_STREAM_DATA(stream) LSTREAM_TYPE_DATA (stream, winsock)
676
677 DEFINE_LSTREAM_IMPLEMENTATION ("winsock", lstream_winsock,
678                                sizeof (struct winsock_stream));
679
680 static void
681 winsock_initiate_read (struct winsock_stream *str)
682 {
683   ResetEvent (str->ov.hEvent);
684   str->bufpos = 0;
685
686   if (!ReadFile ((HANDLE)str->s, str->buffer, WINSOCK_READ_BUFFER_SIZE,
687                  &str->bufsize, &str->ov))
688     {
689       if (GetLastError () == ERROR_IO_PENDING)
690         str->pending_p = 1;
691       else if (GetLastError () == ERROR_HANDLE_EOF)
692         str->eof_p = 1;
693       else
694         str->error_p = 1;
695     }
696   else if (str->bufsize == 0)
697     str->eof_p = 1;
698 }
699
700 static ssize_t
701 winsock_reader (Lstream *stream, unsigned char *data, size_t size)
702 {
703   struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
704
705   /* If the current operation is not yet complete, there's nothing to
706      give back */
707   if (str->pending_p)
708     {
709       if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT)
710         {
711           errno = EAGAIN;
712           return -1;
713         }
714       else
715         {
716           if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &str->bufsize, TRUE))
717             {
718               if (GetLastError() == ERROR_HANDLE_EOF)
719                 str->bufsize = 0;
720               else
721                 str->error_p = 1;
722             }
723           if (str->bufsize == 0)
724             str->eof_p = 1;
725           str->pending_p = 0;
726         }
727     }
728
729   if (str->eof_p)
730     return 0;
731   if (str->error_p)
732     return -1;
733
734   /* Return as much of buffer as we have */
735   size = min (size, (size_t) (str->bufsize - str->bufpos));
736   memcpy (data, (void*)((BYTE*)str->buffer + str->bufpos), size);
737   str->bufpos += size;
738
739   /* Read more if buffer is exhausted */
740   if (str->bufsize == str->bufpos)
741     winsock_initiate_read (str);
742
743   return size;
744 }
745
746 static ssize_t
747 winsock_writer (Lstream *stream, const unsigned char *data, size_t size)
748 {
749   struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
750
751   if (str->pending_p)
752     {
753       if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT)
754         {
755           str->blocking_p = 1;
756           return -1;
757         }
758       else
759         {
760           DWORD dw_unused;
761           if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &dw_unused, TRUE))
762             str->error_p = 1;
763           str->pending_p = 0;
764         }
765     }
766
767   str->blocking_p = 0;
768
769   if (str->error_p)
770     return -1;
771
772   if (size == 0)
773     return 0;
774
775   {
776     ResetEvent (str->ov.hEvent);
777
778     /* Docs indicate that 4th parameter to WriteFile can be NULL since this is
779      * an overlapped operation. This fails on Win95 with winsock 1.x so we
780      * supply a spare address which is ignored by Win95 anyway. Sheesh. */
781     if (WriteFile ((HANDLE)str->s, data, size, (LPDWORD)&str->buffer, &str->ov)
782         || GetLastError() == ERROR_IO_PENDING)
783       str->pending_p = 1;
784     else
785       str->error_p = 1;
786   }
787
788   return str->error_p ? -1 : size;
789 }
790
791 static int
792 winsock_closer (Lstream *lstr)
793 {
794   struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
795
796   if (lstr->flags & LSTREAM_FL_READ)
797     shutdown (str->s, 0);
798   else
799     shutdown (str->s, 1);
800
801   CloseHandle ((HANDLE)str->s);
802   if (str->pending_p)
803     WaitForSingleObject (str->ov.hEvent, INFINITE);
804
805   if (lstr->flags & LSTREAM_FL_READ)
806     xfree (str->buffer);
807
808   CloseHandle (str->ov.hEvent);
809   return 0;
810 }
811
812 static int
813 winsock_was_blocked_p (Lstream *stream)
814 {
815   struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
816   return str->blocking_p;
817 }
818
819 static Lisp_Object
820 make_winsock_stream_1 (SOCKET s, LPARAM param, const char *mode)
821 {
822   Lisp_Object obj;
823   Lstream *lstr = Lstream_new (lstream_winsock, mode);
824   struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
825
826   str->s = s;
827   str->blocking_p = 0;
828   str->error_p = 0;
829   str->eof_p = 0;
830   str->pending_p = 0;
831   str->user_data = param;
832
833   xzero (str->ov);
834   str->ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
835
836   if (lstr->flags & LSTREAM_FL_READ)
837     {
838       str->buffer = xmalloc (WINSOCK_READ_BUFFER_SIZE);
839       winsock_initiate_read (str);
840     }
841
842   lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
843   XSETLSTREAM (obj, lstr);
844   return obj;
845 }
846
847 static Lisp_Object
848 make_winsock_input_stream (SOCKET s, LPARAM param)
849 {
850   return make_winsock_stream_1 (s, param, "r");
851 }
852
853 static Lisp_Object
854 make_winsock_output_stream (SOCKET s, LPARAM param)
855 {
856   return make_winsock_stream_1 (s, param, "w");
857 }
858
859 static HANDLE
860 get_winsock_stream_waitable (Lstream *lstr)
861 {
862   struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
863   return str->ov.hEvent;
864 }
865
866 static LPARAM
867 get_winsock_stream_param (Lstream *lstr)
868 {
869   struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
870   return str->user_data;
871 }
872
873 static void
874 init_winsock_stream (void)
875 {
876   LSTREAM_HAS_METHOD (winsock, reader);
877   LSTREAM_HAS_METHOD (winsock, writer);
878   LSTREAM_HAS_METHOD (winsock, closer);
879   LSTREAM_HAS_METHOD (winsock, was_blocked_p);
880 }
881 #endif /* defined (HAVE_SOCKETS) */
882 \f
883 /************************************************************************/
884 /*                     Dispatch queue management                        */
885 /************************************************************************/
886
887 static int
888 mswindows_user_event_p (Lisp_Event* sevt)
889 {
890   return (sevt->event_type == key_press_event
891           || sevt->event_type == button_press_event
892           || sevt->event_type == button_release_event
893           || sevt->event_type == misc_user_event);
894 }
895
896 /*
897  * Add an emacs event to the proper dispatch queue
898  */
899 void
900 mswindows_enqueue_dispatch_event (Lisp_Object event)
901 {
902   int user_p = mswindows_user_event_p (XEVENT(event));
903   enqueue_event (event,
904                  user_p ? &mswindows_u_dispatch_event_queue :
905                  &mswindows_s_dispatch_event_queue,
906                  user_p ? &mswindows_u_dispatch_event_queue_tail :
907                  &mswindows_s_dispatch_event_queue_tail);
908
909   /* Avoid blocking on WaitMessage */
910   PostMessage (NULL, XM_BUMPQUEUE, 0, 0);
911 }
912
913 /*
914  * Add a misc-user event to the dispatch queue.
915  *
916  * Stuff it into our own dispatch queue, so we have something
917  * to return from next_event callback.
918  */
919 void
920 mswindows_enqueue_misc_user_event (Lisp_Object channel, Lisp_Object function,
921                                    Lisp_Object object)
922 {
923   Lisp_Object event = Fmake_event (Qnil, Qnil);
924   Lisp_Event* e = XEVENT (event);
925
926   e->event_type = misc_user_event;
927   e->channel = channel;
928   e->timestamp = GetTickCount ();
929   e->event.misc.function = function;
930   e->event.misc.object = object;
931
932   mswindows_enqueue_dispatch_event (event);
933 }
934
935 void
936 mswindows_enqueue_magic_event (HWND hwnd, UINT msg)
937 {
938   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
939   Lisp_Event* event = XEVENT (emacs_event);
940
941   event->channel = hwnd ? mswindows_find_frame (hwnd) : Qnil;
942   event->timestamp = GetMessageTime();
943   event->event_type = magic_event;
944   EVENT_MSWINDOWS_MAGIC_TYPE (event) = msg;
945
946   mswindows_enqueue_dispatch_event (emacs_event);
947 }
948
949 static void
950 mswindows_enqueue_process_event (Lisp_Process* p)
951 {
952   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
953   Lisp_Event* event = XEVENT (emacs_event);
954   Lisp_Object process;
955   XSETPROCESS (process, p);
956
957   event->event_type = process_event;
958   event->timestamp  = GetTickCount ();
959   event->event.process.process = process;
960
961   mswindows_enqueue_dispatch_event (emacs_event);
962 }
963
964 static void
965 mswindows_enqueue_mouse_button_event (HWND hwnd, UINT msg, POINTS where,
966                                       int mods, DWORD when)
967 {
968   int downp = (msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN ||
969                msg == WM_RBUTTONDOWN);
970
971   /* We always use last message time, because mouse button
972      events may get delayed, and XEmacs double click
973      recognition will fail */
974
975   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
976   Lisp_Event* event = XEVENT (emacs_event);
977
978   mswindows_handle_sticky_modifiers (0, 0, downp, 0);
979   event->channel = mswindows_find_frame (hwnd);
980   event->timestamp = when;
981   event->event.button.button =
982     (msg==WM_LBUTTONDOWN || msg==WM_LBUTTONUP) ? 1 :
983     ((msg==WM_RBUTTONDOWN || msg==WM_RBUTTONUP) ? 3 : 2);
984   event->event.button.x = where.x;
985   event->event.button.y = where.y;
986   event->event.button.modifiers = mswindows_modifier_state (NULL, mods, 0);
987
988   if (downp)
989     {
990       event->event_type = button_press_event;
991       SetCapture (hwnd);
992       /* we need this to make sure the main window regains the focus
993          from control subwindows */
994       if (GetFocus() != hwnd)
995         {
996           SetFocus (hwnd);
997           mswindows_enqueue_magic_event (hwnd, WM_SETFOCUS);
998         }
999     }
1000   else
1001     {
1002       event->event_type = button_release_event;
1003       ReleaseCapture ();
1004     }
1005
1006   mswindows_enqueue_dispatch_event (emacs_event);
1007 }
1008
1009 static void
1010 mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods)
1011 {
1012   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1013   Lisp_Event* event = XEVENT(emacs_event);
1014
1015   event->channel = mswindows_find_console(hwnd);
1016   event->timestamp = GetMessageTime();
1017   event->event_type = key_press_event;
1018   event->event.key.keysym = keysym;
1019   event->event.key.modifiers = mods;
1020   mswindows_enqueue_dispatch_event (emacs_event);
1021 }
1022
1023 /*
1024  * Remove and return the first emacs event on the dispatch queue.
1025  * Give a preference to user events over non-user ones.
1026  */
1027 static Lisp_Object
1028 mswindows_dequeue_dispatch_event (void)
1029 {
1030   Lisp_Object event;
1031   Lisp_Event* sevt;
1032
1033   assert (!NILP(mswindows_u_dispatch_event_queue) ||
1034           !NILP(mswindows_s_dispatch_event_queue));
1035
1036   event = dequeue_event (
1037                          NILP(mswindows_u_dispatch_event_queue) ?
1038                          &mswindows_s_dispatch_event_queue :
1039                          &mswindows_u_dispatch_event_queue,
1040                          NILP(mswindows_u_dispatch_event_queue) ?
1041                          &mswindows_s_dispatch_event_queue_tail :
1042                          &mswindows_u_dispatch_event_queue_tail);
1043
1044   sevt = XEVENT(event);
1045   if (sevt->event_type == key_press_event
1046       && (sevt->event.key.modifiers & FAKE_MOD_QUIT))
1047     {
1048       sevt->event.key.modifiers &= ~FAKE_MOD_QUIT;
1049       --mswindows_quit_chars_count;
1050     }
1051
1052   return event;
1053 }
1054
1055 /*
1056  * Remove and return the first emacs event on the dispatch queue that matches
1057  * the supplied event.
1058  * Timeout event matches if interval_id is equal to that of the given event.
1059  * Keypress event matches if logical AND between modifiers bitmask of the
1060  * event in the queue and that of the given event is non-zero.
1061  * For all other event types, this function aborts.
1062  */
1063
1064 Lisp_Object
1065 mswindows_cancel_dispatch_event (Lisp_Event *match)
1066 {
1067   Lisp_Object event;
1068   Lisp_Object previous_event = Qnil;
1069   int user_p = mswindows_user_event_p (match);
1070   Lisp_Object* head = user_p ? &mswindows_u_dispatch_event_queue :
1071     &mswindows_s_dispatch_event_queue;
1072   Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail :
1073     &mswindows_s_dispatch_event_queue_tail;
1074
1075   assert (match->event_type == timeout_event
1076           || match->event_type == key_press_event);
1077
1078   EVENT_CHAIN_LOOP (event, *head)
1079     {
1080       Lisp_Event *e = XEVENT (event);
1081       if ((e->event_type == match->event_type) &&
1082           ((e->event_type == timeout_event) ?
1083            (e->event.timeout.interval_id == match->event.timeout.interval_id) :
1084            /* Must be key_press_event */
1085            ((e->event.key.modifiers & match->event.key.modifiers) != 0)))
1086         {
1087           if (NILP (previous_event))
1088             dequeue_event (head, tail);
1089           else
1090             {
1091               XSET_EVENT_NEXT (previous_event, XEVENT_NEXT (event));
1092               if (EQ (*tail, event))
1093                 *tail = previous_event;
1094             }
1095
1096           return event;
1097         }
1098       previous_event = event;
1099     }
1100   return Qnil;
1101 }
1102 \f
1103 #ifndef HAVE_MSG_SELECT
1104 /************************************************************************/
1105 /*                     Waitable handles manipulation                    */
1106 /************************************************************************/
1107 static int
1108 find_waitable_handle (HANDLE h)
1109 {
1110   int i;
1111   for (i = 0; i < mswindows_waitable_count; ++i)
1112     if (mswindows_waitable_handles[i] == h)
1113       return i;
1114
1115   return -1;
1116 }
1117
1118 static BOOL
1119 add_waitable_handle (HANDLE h)
1120 {
1121   assert (find_waitable_handle (h) < 0);
1122   if (mswindows_waitable_count == MAX_WAITABLE)
1123     return FALSE;
1124
1125   mswindows_waitable_handles [mswindows_waitable_count++] = h;
1126   return TRUE;
1127 }
1128
1129 static void
1130 remove_waitable_handle (HANDLE h)
1131 {
1132   int ix = find_waitable_handle (h);
1133   if (ix < 0)
1134     return;
1135
1136   mswindows_waitable_handles [ix] =
1137     mswindows_waitable_handles [--mswindows_waitable_count];
1138 }
1139 #endif /* HAVE_MSG_SELECT */
1140
1141 \f
1142 /************************************************************************/
1143 /*                             Event pump                               */
1144 /************************************************************************/
1145
1146 static Lisp_Object
1147 mswindows_modal_loop_error_handler (Lisp_Object cons_sig_data,
1148                                     Lisp_Object u_n_u_s_e_d)
1149 {
1150   mswindows_error_caught_in_modal_loop = cons_sig_data;
1151   return Qunbound;
1152 }
1153
1154 Lisp_Object
1155 mswindows_protect_modal_loop (Lisp_Object (*bfun) (Lisp_Object barg),
1156                               Lisp_Object barg)
1157 {
1158   Lisp_Object tmp;
1159
1160   ++mswindows_in_modal_loop;
1161   tmp = condition_case_1 (Qt,
1162                           bfun, barg,
1163                           mswindows_modal_loop_error_handler, Qnil);
1164   --mswindows_in_modal_loop;
1165
1166   return tmp;
1167 }
1168
1169 void
1170 mswindows_unmodalize_signal_maybe (void)
1171 {
1172   if (!NILP (mswindows_error_caught_in_modal_loop))
1173     {
1174       /* Got an error while messages were pumped while
1175          in window procedure - have to resignal */
1176       Lisp_Object sym = XCAR (mswindows_error_caught_in_modal_loop);
1177       Lisp_Object data = XCDR (mswindows_error_caught_in_modal_loop);
1178       mswindows_error_caught_in_modal_loop = Qnil;
1179       Fsignal (sym, data);
1180     }
1181 }
1182
1183 /*
1184  * This is an unsafe part of event pump, guarded by
1185  * condition_case. See mswindows_pump_outstanding_events
1186  */
1187 static Lisp_Object
1188 mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d)
1189 {
1190   /* This function can call lisp */
1191   Lisp_Object event = Fmake_event (Qnil, Qnil);
1192   struct gcpro gcpro1;
1193   int do_redisplay = 0;
1194   GCPRO1 (event);
1195
1196   while (detect_input_pending ())
1197     {
1198       Fnext_event (event, Qnil);
1199       Fdispatch_event (event);
1200       do_redisplay = 1;
1201     }
1202
1203   if (do_redisplay)
1204     redisplay ();
1205
1206   Fdeallocate_event (event);
1207   UNGCPRO;
1208
1209   /* Qt becomes return value of mswindows_pump_outstanding_events
1210      once we get here */
1211   return Qt;
1212 }
1213
1214 /*
1215  * This function pumps emacs events, while available, by using
1216  * next_message/dispatch_message loop. Errors are trapped around
1217  * the loop so the function always returns.
1218  *
1219  * Windows message queue is not looked into during the call,
1220  * neither are waitable handles checked. The function pumps
1221  * thus only dispatch events already queued, as well as those
1222  * resulted in dispatching thereof. This is done by setting
1223  * module local variable mswindows_in_modal_loop to nonzero.
1224  *
1225  * Return value is Qt if no errors was trapped, or Qunbound if
1226  * there was an error.
1227  *
1228  * In case of error, a cons representing the error, in the
1229  * form (SIGNAL . DATA), is stored in the module local variable
1230  * mswindows_error_caught_in_modal_loop. This error is signaled
1231  * again when DispatchMessage returns. Thus, Windows internal
1232  * modal loops are protected against throws, which are proven
1233  * to corrupt internal Windows structures.
1234  *
1235  * In case of success, mswindows_error_caught_in_modal_loop is
1236  * assigned Qnil.
1237  *
1238  * If the value of mswindows_error_caught_in_modal_loop is not
1239  * nil already upon entry, the function just returns non-nil.
1240  * This situation means that a new event has been queued while
1241  * in cancel mode. The event will be dequeued on the next regular
1242  * call of next-event; the pump is off since error is caught.
1243  * The caller must *unconditionally* cancel modal loop if the
1244  * value returned by this function is nil. Otherwise, everything
1245  * will become frozen until the modal loop exits under normal
1246  * condition (scrollbar drag is released, menu closed etc.)
1247  */
1248 Lisp_Object
1249 mswindows_pump_outstanding_events (void)
1250 {
1251   /* This function can call lisp */
1252
1253   Lisp_Object result = Qt;
1254   struct gcpro gcpro1;
1255   GCPRO1 (result);
1256
1257   if (NILP(mswindows_error_caught_in_modal_loop))
1258     result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil);
1259   UNGCPRO;
1260   return result;
1261 }
1262
1263 /*
1264  * KEYBOARD_ONLY_P is set to non-zero when we are called from
1265  * QUITP, and are interesting in keyboard messages only.
1266  */
1267 static void
1268 mswindows_drain_windows_queue (void)
1269 {
1270   MSG msg;
1271
1272   /* should call mswindows_need_event_in_modal_loop() if in modal loop */
1273   assert (!mswindows_in_modal_loop);
1274
1275   while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1276     {
1277       char class_name_buf [sizeof (XEMACS_CLASS) + 2] = "";
1278
1279       /* Don't translate messages destined for a dialog box, this
1280          makes keyboard traversal work. I think?? */
1281       if (mswindows_is_dialog_msg (&msg))
1282         {
1283           mswindows_unmodalize_signal_maybe ();
1284           continue;
1285         }
1286
1287       /* We have to translate messages that are not sent to an XEmacs
1288          frame. This is so that key presses work ok in things like
1289          edit fields. However, we *musn't* translate message for XEmacs
1290          frames as this is handled in the wnd proc.
1291          We also have to avoid generating paint magic events for windows
1292          that aren't XEmacs frames */
1293       /* GetClassName will truncate a longer class name. By adding one
1294          extra character, we are forcing textual comparison to fail
1295          if the name is longer than XEMACS_CLASS */
1296
1297       GetClassName (msg.hwnd, class_name_buf, sizeof (class_name_buf) - 1);
1298       if (stricmp (class_name_buf, XEMACS_CLASS) != 0)
1299         {
1300           /* Not an XEmacs frame */
1301           TranslateMessage (&msg);
1302         }
1303       else if (msg.message == WM_PAINT)
1304         {
1305           struct mswindows_frame* msframe;
1306
1307           /* hdc will be NULL unless this is a subwindow - in which case we
1308              shouldn't have received a paint message for it here. */
1309           assert (msg.wParam == 0);
1310
1311           /* Queue a magic event for handling when safe */
1312           msframe =
1313             FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (msg.hwnd)));
1314           if (!msframe->paint_pending)
1315             {
1316               msframe->paint_pending = 1;
1317               mswindows_enqueue_magic_event (msg.hwnd, WM_PAINT);
1318             }
1319           /* Don't dispatch. WM_PAINT is always the last message in the
1320              queue so it's OK to just return. */
1321           return;
1322         }
1323       DispatchMessage (&msg);
1324       mswindows_unmodalize_signal_maybe ();
1325     }
1326 }
1327
1328 /*
1329  * This is a special flavor of the mswindows_need_event function,
1330  * used while in event pump. Actually, there is only kind of events
1331  * allowed while in event pump: a timer.  An attempt to fetch any
1332  * other event leads to a deadlock, as there's no source of user input
1333  * ('cause event pump mirrors windows modal loop, which is a sole
1334  * owner of thread message queue).
1335  *
1336  * To detect this, we use a counter of active timers, and allow
1337  * fetching WM_TIMER messages. Instead of trying to fetch a WM_TIMER
1338  * which will never come when there are no pending timers, which leads
1339  * to deadlock, we simply signal an error.
1340  */
1341 static void
1342 mswindows_need_event_in_modal_loop (int badly_p)
1343 {
1344   MSG msg;
1345
1346   /* Check if already have one */
1347   if (!NILP (mswindows_u_dispatch_event_queue)
1348       || !NILP (mswindows_s_dispatch_event_queue))
1349     return;
1350
1351   /* No event is ok */
1352   if (!badly_p)
1353     return;
1354
1355   /* We do not check the _u_ queue, because timers go to _s_ */
1356   while (NILP (mswindows_s_dispatch_event_queue))
1357     {
1358       /* We'll deadlock if go waiting */
1359       if (mswindows_pending_timers_count == 0)
1360         error ("Deadlock due to an attempt to call next-event in a wrong context");
1361
1362       /* Fetch and dispatch any pending timers */
1363       GetMessage (&msg, NULL, WM_TIMER, WM_TIMER);
1364       DispatchMessage (&msg);
1365     }
1366 }
1367
1368 /*
1369  * This drains the event queue and fills up two internal queues until
1370  * an event of a type specified by USER_P is retrieved.
1371  *
1372  *
1373  * Used by emacs_mswindows_event_pending_p and emacs_mswindows_next_event
1374  */
1375 static void
1376 mswindows_need_event (int badly_p)
1377 {
1378   int active;
1379
1380   if (mswindows_in_modal_loop)
1381     {
1382       mswindows_need_event_in_modal_loop (badly_p);
1383       return;
1384     }
1385
1386   while (NILP (mswindows_u_dispatch_event_queue)
1387          && NILP (mswindows_s_dispatch_event_queue))
1388     {
1389 #ifdef HAVE_MSG_SELECT
1390       int i;
1391       SELECT_TYPE temp_mask = input_wait_mask;
1392       EMACS_TIME sometime;
1393       EMACS_SELECT_TIME select_time_to_block, *pointer_to_this;
1394
1395       if (badly_p)
1396         pointer_to_this = 0;
1397       else
1398         {
1399           EMACS_SET_SECS_USECS (sometime, 0, 0);
1400           EMACS_TIME_TO_SELECT_TIME (sometime, select_time_to_block);
1401           pointer_to_this = &select_time_to_block;
1402         }
1403
1404       active = select (MAXDESC, &temp_mask, 0, 0, pointer_to_this);
1405
1406       if (active == 0)
1407         {
1408           assert (!badly_p);
1409           return;               /* timeout */
1410         }
1411       else if (active > 0)
1412         {
1413           if (FD_ISSET (windows_fd, &temp_mask))
1414             {
1415               mswindows_drain_windows_queue ();
1416             }
1417           else
1418             {
1419 #ifdef HAVE_TTY
1420               /* Look for a TTY event */
1421               for (i = 0; i < MAXDESC-1; i++)
1422                 {
1423                   /* To avoid race conditions (among other things, an infinite
1424                      loop when called from Fdiscard_input()), we must return
1425                      user events ahead of process events. */
1426                   if (FD_ISSET (i, &temp_mask) && FD_ISSET (i, &tty_only_mask))
1427                     {
1428                       struct console *c = tty_find_console_from_fd (i);
1429                       Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1430                       Lisp_Event* event = XEVENT (emacs_event);
1431
1432                       assert (c);
1433                       if (read_event_from_tty_or_stream_desc (event, c, i))
1434                         {
1435                           mswindows_enqueue_dispatch_event (emacs_event);
1436                           return;
1437                         }
1438                     }
1439                 }
1440 #endif
1441               /* Look for a process event */
1442               for (i = 0; i < MAXDESC-1; i++)
1443                 {
1444                   if (FD_ISSET (i, &temp_mask))
1445                     {
1446                       if (FD_ISSET (i, &process_only_mask))
1447                         {
1448                           Lisp_Process *p =
1449                             get_process_from_usid (FD_TO_USID(i));
1450
1451                           mswindows_enqueue_process_event (p);
1452                         }
1453                       else
1454                         {
1455                           /* We might get here when a fake event came
1456                              through a signal. Return a dummy event, so
1457                              that a cycle of the command loop will
1458                              occur. */
1459                           drain_signal_event_pipe ();
1460                           mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
1461                         }
1462                     }
1463                 }
1464             }
1465         }
1466       else if (active==-1)
1467         {
1468           if (errno != EINTR)
1469             {
1470               /* something bad happened */
1471               assert(0);
1472             }
1473         }
1474       else
1475         {
1476           assert(0);
1477         }
1478 #else
1479       /* Now try getting a message or process event */
1480       active = MsgWaitForMultipleObjects (mswindows_waitable_count,
1481                                           mswindows_waitable_handles,
1482                                           FALSE, badly_p ? INFINITE : 0,
1483                                           QS_ALLINPUT);
1484
1485       /* This will assert if handle being waited for becomes abandoned.
1486          Not the case currently tho */
1487       assert ((!badly_p && active == WAIT_TIMEOUT) ||
1488               (active >= WAIT_OBJECT_0 &&
1489                active <= WAIT_OBJECT_0 + mswindows_waitable_count));
1490
1491       if (active == WAIT_TIMEOUT)
1492         {
1493           /* No luck trying - just return what we've already got */
1494           return;
1495         }
1496       else if (active == WAIT_OBJECT_0 + mswindows_waitable_count)
1497         {
1498           /* Got your message, thanks */
1499           mswindows_drain_windows_queue ();
1500         }
1501       else
1502         {
1503           int ix = active - WAIT_OBJECT_0;
1504           /* First, try to find which process' output has signaled */
1505           Lisp_Process *p =
1506             get_process_from_usid (HANDLE_TO_USID (mswindows_waitable_handles[ix]));
1507           if (p != NULL)
1508             {
1509               /* Found a signaled process input handle */
1510               mswindows_enqueue_process_event (p);
1511             }
1512           else
1513             {
1514               /* None. This means that the process handle itself has signaled.
1515                  Remove the handle from the wait vector, and make status_notify
1516                  note the exited process */
1517               mswindows_waitable_handles [ix] =
1518                 mswindows_waitable_handles [--mswindows_waitable_count];
1519               kick_status_notify ();
1520               /* We need to return a process event here so that
1521                  (1) accept-process-output will return when called on this
1522                  process, and (2) status notifications will happen in
1523                  accept-process-output, sleep-for, and sit-for. */
1524               /* #### horrible kludge till my real process fixes go in.
1525                */
1526               if (!NILP (Vprocess_list))
1527                 {
1528                   Lisp_Object vaffanculo = XCAR (Vprocess_list);
1529                   mswindows_enqueue_process_event (XPROCESS (vaffanculo));
1530                 }
1531               else /* trash me soon. */
1532                 /* Have to return something: there may be no accompanying
1533                    process event */
1534                 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
1535             }
1536         }
1537 #endif
1538     } /* while */
1539 }
1540
1541 /************************************************************************/
1542 /*                           Event generators                           */
1543 /************************************************************************/
1544
1545 /*
1546  * Callback procedure for synchronous timer messages
1547  */
1548 static void CALLBACK
1549 mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime)
1550 {
1551   Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1552   Lisp_Event *event = XEVENT (emacs_event);
1553
1554   if (KillTimer (NULL, id_timer))
1555     --mswindows_pending_timers_count;
1556
1557   event->channel = Qnil;
1558   event->timestamp = dwtime;
1559   event->event_type = timeout_event;
1560   event->event.timeout.interval_id = id_timer;
1561   event->event.timeout.function = Qnil;
1562   event->event.timeout.object = Qnil;
1563
1564   mswindows_enqueue_dispatch_event (emacs_event);
1565 }
1566
1567 /*
1568  * Callback procedure for dde messages
1569  *
1570  * We execute a dde Open("file") by simulating a file drop, so dde support
1571  * depends on dnd support.
1572  */
1573 #ifdef HAVE_DRAGNDROP
1574 HDDEDATA CALLBACK
1575 mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
1576                         HSZ hszTopic, HSZ hszItem, HDDEDATA hdata,
1577                         DWORD dwData1, DWORD dwData2)
1578 {
1579   switch (uType)
1580     {
1581     case XTYP_CONNECT:
1582       if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
1583         return (HDDEDATA)TRUE;
1584       return (HDDEDATA)FALSE;
1585
1586     case XTYP_WILDCONNECT:
1587       {
1588         /* We only support one {service,topic} pair */
1589         HSZPAIR pairs[2] = {
1590           { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } };
1591
1592         if (!(hszItem  || DdeCmpStringHandles (hszItem, mswindows_dde_service)) &&
1593             !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)))
1594           return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs,
1595                                        sizeof (pairs), 0L, 0, uFmt, 0));
1596       }
1597       return (HDDEDATA)NULL;
1598
1599     case XTYP_EXECUTE:
1600       if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
1601         {
1602           DWORD len = DdeGetData (hdata, NULL, 0, 0);
1603           LPBYTE cmd = (LPBYTE) alloca (len+1);
1604           char *end;
1605           char *filename;
1606           struct gcpro gcpro1, gcpro2;
1607           Lisp_Object l_dndlist = Qnil;
1608           Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1609           Lisp_Object frmcons, devcons, concons;
1610           Lisp_Event *event = XEVENT (emacs_event);
1611
1612           DdeGetData (hdata, cmd, len, 0);
1613           cmd[len] = '\0';
1614           DdeFreeDataHandle (hdata);
1615
1616           /* Check syntax & that it's an [Open("foo")] command, which we
1617            * treat like a file drop */
1618           /* #### Ought to be generalised and accept some other commands */
1619           if (*cmd == '[')
1620             cmd++;
1621           if (strnicmp (cmd, MSWINDOWS_DDE_ITEM_OPEN,
1622                         strlen (MSWINDOWS_DDE_ITEM_OPEN)))
1623             return DDE_FNOTPROCESSED;
1624           cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN);
1625           while (*cmd==' ')
1626             cmd++;
1627           if (*cmd!='(' || *(cmd+1)!='\"')
1628             return DDE_FNOTPROCESSED;
1629           end = (cmd+=2);
1630           while (*end && *end!='\"')
1631             end++;
1632           if (!*end)
1633             return DDE_FNOTPROCESSED;
1634           *end = '\0';
1635           if (*(++end)!=')')
1636             return DDE_FNOTPROCESSED;
1637           if (*(++end)==']')
1638             end++;
1639           if (*end)
1640             return DDE_FNOTPROCESSED;
1641
1642 #ifdef CYGWIN
1643           filename = alloca (cygwin32_win32_to_posix_path_list_buf_size (cmd) + 5);
1644           strcpy (filename, "file:");
1645           cygwin32_win32_to_posix_path_list (cmd, filename+5);
1646 #else
1647           dostounix_filename (cmd);
1648           filename = alloca (strlen (cmd)+6);
1649           strcpy (filename, "file:");
1650           strcat (filename, cmd);
1651 #endif
1652           GCPRO2 (emacs_event, l_dndlist);
1653           l_dndlist = make_string (filename, strlen (filename));
1654
1655           /* Find a mswindows frame */
1656           event->channel = Qnil;
1657           FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
1658             {
1659               Lisp_Object frame = XCAR (frmcons);
1660               if (FRAME_TYPE_P (XFRAME (frame), mswindows))
1661                 event->channel = frame;
1662             };
1663           assert (!NILP (event->channel));
1664
1665           event->timestamp = GetTickCount();
1666           event->event_type = misc_user_event;
1667           event->event.misc.button = 1;
1668           event->event.misc.modifiers = 0;
1669           event->event.misc.x = -1;
1670           event->event.misc.y = -1;
1671           event->event.misc.function = Qdragdrop_drop_dispatch;
1672           event->event.misc.object = Fcons (Qdragdrop_URL,
1673                                             Fcons (l_dndlist, Qnil));
1674           mswindows_enqueue_dispatch_event (emacs_event);
1675           UNGCPRO;
1676           return (HDDEDATA) DDE_FACK;
1677         }
1678       DdeFreeDataHandle (hdata);
1679       return (HDDEDATA) DDE_FNOTPROCESSED;
1680
1681     default:
1682       return (HDDEDATA) NULL;
1683     }
1684 }
1685 #endif
1686
1687 /*
1688  * Helper to do repainting - repaints can happen both from the windows
1689  * procedure and from magic events
1690  */
1691 static void
1692 mswindows_handle_paint (struct frame *frame)
1693 {
1694   HWND hwnd = FRAME_MSWINDOWS_HANDLE (frame);
1695
1696   /* According to the docs we need to check GetUpdateRect() before
1697      actually doing a WM_PAINT */
1698   if (GetUpdateRect (hwnd, NULL, FALSE))
1699     {
1700       PAINTSTRUCT paintStruct;
1701       int x, y, width, height;
1702
1703       BeginPaint (hwnd, &paintStruct);
1704       x = paintStruct.rcPaint.left;
1705       y = paintStruct.rcPaint.top;
1706       width = paintStruct.rcPaint.right - paintStruct.rcPaint.left;
1707       height = paintStruct.rcPaint.bottom - paintStruct.rcPaint.top;
1708       /* Normally we want to ignore expose events when child
1709          windows are unmapped, however once we are in the guts of
1710          WM_PAINT we need to make sure that we don't register
1711          unmaps then because they will not actually occur. */
1712       /* #### commenting out the next line seems to fix some problems
1713          but not all.  only andy currently understands this stuff and
1714          he needs to review it more carefully. --ben */
1715       if (!check_for_ignored_expose (frame, x, y, width, height))
1716         {
1717           hold_ignored_expose_registration = 1;
1718           mswindows_redraw_exposed_area (frame, x, y, width, height);
1719           hold_ignored_expose_registration = 0;
1720         }
1721       EndPaint (hwnd, &paintStruct);
1722     }
1723 }
1724
1725 /*
1726  * Returns 1 if a key is a real modifier or special key, which
1727  * is better handled by DefWindowProc
1728  */
1729 static int
1730 key_needs_default_processing_p (UINT vkey)
1731 {
1732   if (mswindows_alt_by_itself_activates_menu && vkey == VK_MENU
1733       /* if we let ALT activate the menu like this, then sticky ALT-modified
1734          keystrokes become impossible. */
1735       && !modifier_keys_are_sticky)
1736     return 1;
1737
1738   return 0;
1739 }
1740
1741 /* key-handling code is always ugly.  It just ends up working out
1742    that way.
1743
1744    #### Most of the sticky-modifier code below is copied from similar
1745    code in event-Xt.c.  They should somehow or other be merged.
1746
1747    Here are some pointers:
1748
1749    -- DOWN_MASK indicates which modifiers should be treated as "down"
1750       when the corresponding upstroke happens.  It gets reset for
1751       a particular modifier when that modifier goes up, and reset
1752       for all modifiers when a non-modifier key is pressed.  Example:
1753
1754       I press Control-A-Shift and then release Control-A-Shift.
1755       I want the Shift key to be sticky but not the Control key.
1756
1757    -- If a modifier key is sticky, I can unstick it by pressing
1758       the modifier key again. */
1759
1760 static WPARAM last_downkey;
1761 static int need_to_add_mask, down_mask;
1762
1763 #define XEMSW_LCONTROL (1<<0)
1764 #define XEMSW_RCONTROL (1<<1)
1765 #define XEMSW_LSHIFT (1<<2)
1766 #define XEMSW_RSHIFT (1<<3)
1767 #define XEMSW_LMENU (1<<4)
1768 #define XEMSW_RMENU (1<<5)
1769
1770 static int
1771 mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam,
1772                                    int downp, int keyp)
1773 {
1774   int mods = 0;
1775
1776   if (!modifier_keys_are_sticky) /* Optimize for non-sticky modifiers */
1777     return 0;
1778
1779   if (! (keyp &&
1780          (wParam == VK_CONTROL || wParam == VK_LCONTROL ||
1781           wParam == VK_RCONTROL ||
1782           wParam == VK_MENU || wParam == VK_LMENU ||
1783           wParam == VK_RMENU ||
1784           wParam == VK_SHIFT || wParam == VK_LSHIFT ||
1785           wParam == VK_RSHIFT)))
1786     { /* Not a modifier key */
1787       if (downp && keyp && !last_downkey)
1788         last_downkey = wParam;
1789       /* If I hold press-and-release the Control key and then press
1790          and hold down the right arrow, I want it to auto-repeat
1791          Control-Right.  On the other hand, if I do the same but
1792          manually press the Right arrow a bunch of times, I want
1793          to see one Control-Right and then a bunch of Rights.
1794          This means that we need to distinguish between an
1795          auto-repeated key and a key pressed and released a bunch
1796          of times. */
1797       else if ((downp && !keyp) ||
1798                (downp && keyp && last_downkey &&
1799                 (wParam != last_downkey ||
1800                  /* the "previous key state" bit indicates autorepeat */
1801                  ! (lParam & (1 << 30)))))
1802         {
1803           need_to_add_mask = 0;
1804           last_downkey = 0;
1805         }
1806       if (downp)
1807         down_mask = 0;
1808
1809       mods = need_to_add_mask;
1810     }
1811   else                          /* Modifier key pressed */
1812     {
1813       /* If a non-modifier key was pressed in the middle of a bunch
1814          of modifiers, then it unsticks all the modifiers that were
1815          previously pressed.  We cannot unstick the modifiers until
1816          now because we want to check for auto-repeat of the
1817          non-modifier key. */
1818
1819       if (last_downkey)
1820         {
1821           last_downkey = 0;
1822           need_to_add_mask = 0;
1823         }
1824
1825 #define FROB(mask)                              \
1826 do {                                            \
1827   if (downp && keyp)                            \
1828     {                                           \
1829       /* If modifier key is already sticky,     \
1830          then unstick it.  Note that we do      \
1831          not test down_mask to deal with the    \
1832          unlikely but possible case that the    \
1833          modifier key auto-repeats. */          \
1834       if (need_to_add_mask & mask)              \
1835         {                                       \
1836           need_to_add_mask &= ~mask;            \
1837           down_mask &= ~mask;                   \
1838         }                                       \
1839       else                                      \
1840         down_mask |= mask;                      \
1841     }                                           \
1842   else                                          \
1843     {                                           \
1844       if (down_mask & mask)                     \
1845         {                                       \
1846           down_mask &= ~mask;                   \
1847           need_to_add_mask |= mask;             \
1848         }                                       \
1849     }                                           \
1850 } while (0)
1851
1852       if ((wParam == VK_CONTROL && (lParam & 0x1000000))
1853           || wParam == VK_RCONTROL)
1854         FROB (XEMSW_RCONTROL);
1855       if ((wParam == VK_CONTROL && !(lParam & 0x1000000))
1856           || wParam == VK_LCONTROL)
1857         FROB (XEMSW_LCONTROL);
1858
1859       if ((wParam == VK_SHIFT && (lParam & 0x1000000))
1860           || wParam == VK_RSHIFT)
1861         FROB (XEMSW_RSHIFT);
1862       if ((wParam == VK_SHIFT && !(lParam & 0x1000000))
1863           || wParam == VK_LSHIFT)
1864         FROB (XEMSW_LSHIFT);
1865
1866       if ((wParam == VK_MENU && (lParam & 0x1000000))
1867           || wParam == VK_RMENU)
1868         FROB (XEMSW_RMENU);
1869       if ((wParam == VK_MENU && !(lParam & 0x1000000))
1870           || wParam == VK_LMENU)
1871         FROB (XEMSW_LMENU);
1872     }
1873 #undef FROB
1874
1875   if (mods && downp)
1876     {
1877       BYTE keymap[256];
1878
1879       GetKeyboardState (keymap);
1880
1881       if (mods & XEMSW_LCONTROL)
1882         {
1883           keymap [VK_CONTROL] |= 0x80;
1884           keymap [VK_LCONTROL] |= 0x80;
1885         }
1886       if (mods & XEMSW_RCONTROL)
1887         {
1888           keymap [VK_CONTROL] |= 0x80;
1889           keymap [VK_RCONTROL] |= 0x80;
1890         }
1891
1892       if (mods & XEMSW_LSHIFT)
1893         {
1894           keymap [VK_SHIFT] |= 0x80;
1895           keymap [VK_LSHIFT] |= 0x80;
1896         }
1897       if (mods & XEMSW_RSHIFT)
1898         {
1899           keymap [VK_SHIFT] |= 0x80;
1900           keymap [VK_RSHIFT] |= 0x80;
1901         }
1902
1903       if (mods & XEMSW_LMENU)
1904         {
1905           keymap [VK_MENU] |= 0x80;
1906           keymap [VK_LMENU] |= 0x80;
1907         }
1908       if (mods & XEMSW_RMENU)
1909         {
1910           keymap [VK_MENU] |= 0x80;
1911           keymap [VK_RMENU] |= 0x80;
1912         }
1913
1914       SetKeyboardState (keymap);
1915       return 1;
1916     }
1917
1918   return 0;
1919 }
1920
1921 static void
1922 clear_sticky_modifiers (void)
1923 {
1924   need_to_add_mask = 0;
1925   last_downkey     = 0;
1926   down_mask        = 0;
1927 }
1928
1929 #ifdef DEBUG_XEMACS
1930
1931 #if 0
1932
1933 static void
1934 output_modifier_keyboard_state (void)
1935 {
1936   BYTE keymap[256];
1937
1938   GetKeyboardState (keymap);
1939
1940   stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
1941               keymap[VK_MENU] & 0x80 ? 1 : 0,
1942               keymap[VK_MENU] & 0x1 ? 1 : 0,
1943               keymap[VK_LMENU] & 0x80 ? 1 : 0,
1944               keymap[VK_LMENU] & 0x1 ? 1 : 0,
1945               keymap[VK_RMENU] & 0x80 ? 1 : 0,
1946               keymap[VK_RMENU] & 0x1 ? 1 : 0);
1947   stderr_out ("GetKeyboardState VK_CONTROL %d %d VK_LCONTROL %d %d VK_RCONTROL %d %d\n",
1948               keymap[VK_CONTROL] & 0x80 ? 1 : 0,
1949               keymap[VK_CONTROL] & 0x1 ? 1 : 0,
1950               keymap[VK_LCONTROL] & 0x80 ? 1 : 0,
1951               keymap[VK_LCONTROL] & 0x1 ? 1 : 0,
1952               keymap[VK_RCONTROL] & 0x80 ? 1 : 0,
1953               keymap[VK_RCONTROL] & 0x1 ? 1 : 0);
1954   stderr_out ("GetKeyboardState VK_SHIFT %d %d VK_LSHIFT %d %d VK_RSHIFT %d %d\n",
1955               keymap[VK_SHIFT] & 0x80 ? 1 : 0,
1956               keymap[VK_SHIFT] & 0x1 ? 1 : 0,
1957               keymap[VK_LSHIFT] & 0x80 ? 1 : 0,
1958               keymap[VK_LSHIFT] & 0x1 ? 1 : 0,
1959               keymap[VK_RSHIFT] & 0x80 ? 1 : 0,
1960               keymap[VK_RSHIFT] & 0x1 ? 1 : 0);
1961 }
1962
1963 #endif
1964
1965 /* try to debug the stuck-alt-key problem.
1966
1967  #### this happens only inconsistently, and may only happen when using
1968  StickyKeys in the Win2000 accessibility section of the control panel,
1969  which is extremely broken for other reasons.  */
1970
1971 static void
1972 output_alt_keyboard_state (void)
1973 {
1974   BYTE keymap[256];
1975   SHORT keystate[3];
1976   // SHORT asyncstate[3];
1977
1978   GetKeyboardState (keymap);
1979   keystate[0] = GetKeyState (VK_MENU);
1980   keystate[1] = GetKeyState (VK_LMENU);
1981   keystate[2] = GetKeyState (VK_RMENU);
1982   /* Doing this interferes with key processing. */
1983 /*   asyncstate[0] = GetAsyncKeyState (VK_MENU); */
1984 /*   asyncstate[1] = GetAsyncKeyState (VK_LMENU); */
1985 /*   asyncstate[2] = GetAsyncKeyState (VK_RMENU); */
1986
1987   stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
1988               keymap[VK_MENU] & 0x80 ? 1 : 0,
1989               keymap[VK_MENU] & 0x1 ? 1 : 0,
1990               keymap[VK_LMENU] & 0x80 ? 1 : 0,
1991               keymap[VK_LMENU] & 0x1 ? 1 : 0,
1992               keymap[VK_RMENU] & 0x80 ? 1 : 0,
1993               keymap[VK_RMENU] & 0x1 ? 1 : 0);
1994   stderr_out ("GetKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
1995               keystate[0] & 0x8000 ? 1 : 0,
1996               keystate[0] & 0x1 ? 1 : 0,
1997               keystate[1] & 0x8000 ? 1 : 0,
1998               keystate[1] & 0x1 ? 1 : 0,
1999               keystate[2] & 0x8000 ? 1 : 0,
2000               keystate[2] & 0x1 ? 1 : 0);
2001 /*   stderr_out ("GetAsyncKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", */
2002 /*            asyncstate[0] & 0x8000 ? 1 : 0, */
2003 /*            asyncstate[0] & 0x1 ? 1 : 0, */
2004 /*            asyncstate[1] & 0x8000 ? 1 : 0, */
2005 /*            asyncstate[1] & 0x1 ? 1 : 0, */
2006 /*            asyncstate[2] & 0x8000 ? 1 : 0, */
2007 /*            asyncstate[2] & 0x1 ? 1 : 0); */
2008 }
2009
2010 #endif /* DEBUG_XEMACS */
2011
2012
2013 /*
2014  * The windows procedure for the window class XEMACS_CLASS
2015  */
2016 LRESULT WINAPI
2017 mswindows_wnd_proc (HWND hwnd, UINT message_, WPARAM wParam, LPARAM lParam)
2018 {
2019   /* Note: Remember to initialize emacs_event and event before use.
2020      This code calls code that can GC. You must GCPRO before calling such code. */
2021   Lisp_Object emacs_event = Qnil;
2022   Lisp_Object fobj = Qnil;
2023
2024   Lisp_Event *event;
2025   struct frame *frame;
2026   struct mswindows_frame* msframe;
2027
2028   /* Not perfect but avoids crashes. There is potential for wierd
2029      behavior here. */
2030   if (gc_in_progress)
2031     goto defproc;
2032
2033   assert (!GetWindowLong (hwnd, GWL_USERDATA));
2034   switch (message_)
2035     {
2036     case WM_DESTROYCLIPBOARD:
2037       /* We own the clipboard and someone else wants it.  Delete our
2038          cached copy of the clipboard contents so we'll ask for it from
2039          Windows again when someone does a paste, and destroy any memory
2040          objects we hold on the clipboard that are not in the list of types
2041          that Windows will delete itself. */
2042       mswindows_destroy_selection (QCLIPBOARD);
2043       handle_selection_clear (QCLIPBOARD);
2044       break;
2045
2046     case WM_ERASEBKGND:
2047       /* Erase background only during non-dynamic sizing */
2048       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2049       if (msframe->sizing && !mswindows_dynamic_frame_resize)
2050         goto defproc;
2051       return 1;
2052
2053     case WM_CLOSE:
2054       fobj = mswindows_find_frame (hwnd);
2055       mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt));
2056       break;
2057
2058     case WM_KEYUP:
2059     case WM_SYSKEYUP:
2060
2061       /* See Win95 comment under WM_KEYDOWN */
2062       {
2063         BYTE keymap[256];
2064         int should_set_keymap = 0;
2065
2066 #ifdef DEBUG_XEMACS
2067         if (debug_mswindows_events)
2068           {
2069             stderr_out ("%s wparam=%d lparam=%d\n",
2070                         message_ == WM_KEYUP ? "WM_KEYUP" : "WM_SYSKEYUP",
2071                         wParam, (int)lParam);
2072             output_alt_keyboard_state ();
2073           }
2074 #endif /* DEBUG_XEMACS */
2075
2076         mswindows_handle_sticky_modifiers (wParam, lParam, 0, 1);
2077         if (wParam == VK_CONTROL)
2078           {
2079             GetKeyboardState (keymap);
2080             keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80;
2081             should_set_keymap = 1;
2082           }
2083         else if (wParam == VK_MENU)
2084           {
2085             GetKeyboardState (keymap);
2086             keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80;
2087             should_set_keymap = 1;
2088           }
2089
2090         if (should_set_keymap)
2091           //        && (message_ != WM_SYSKEYUP
2092           //    || NILP (Vmenu_accelerator_enabled)))
2093           SetKeyboardState (keymap);
2094
2095       }
2096
2097       if (key_needs_default_processing_p (wParam))
2098         goto defproc;
2099       else
2100         break;
2101
2102     case WM_KEYDOWN:
2103     case WM_SYSKEYDOWN:
2104
2105       /* In some locales the right-hand Alt key is labelled AltGr. This key
2106        * should produce alternative characters when combined with another key.
2107        * eg on a German keyboard pressing AltGr+q should produce '@'.
2108        * AltGr generates exactly the same keystrokes as LCtrl+RAlt. But if
2109        * TranslateMessage() is called with *any* combination of Ctrl+Alt down,
2110        * it translates as if AltGr were down.
2111        * We get round this by removing all modifiers from the keymap before
2112        * calling TranslateMessage() unless AltGr is *really* down. */
2113       {
2114         BYTE keymap_trans[256];
2115         BYTE keymap_orig[256];
2116         BYTE keymap_sticky[256];
2117         int has_AltGr = mswindows_current_layout_has_AltGr ();
2118         int mods = 0;
2119         int extendedp = lParam & 0x1000000;
2120         Lisp_Object keysym;
2121         int sticky_changed;
2122
2123 #ifdef DEBUG_XEMACS
2124         if (debug_mswindows_events)
2125           {
2126             stderr_out ("%s wparam=%d lparam=%d\n",
2127                         message_ == WM_KEYDOWN ? "WM_KEYDOWN" : "WM_SYSKEYDOWN",
2128                         wParam, (int)lParam);
2129             output_alt_keyboard_state ();
2130           }
2131 #endif /* DEBUG_XEMACS */
2132
2133         GetKeyboardState (keymap_orig);
2134         frame = XFRAME (mswindows_find_frame (hwnd));
2135         if ((sticky_changed =
2136              mswindows_handle_sticky_modifiers (wParam, lParam, 1, 1)))
2137           {
2138             GetKeyboardState (keymap_sticky);
2139             if (keymap_sticky[VK_MENU] & 0x80)
2140               {
2141                 message_ = WM_SYSKEYDOWN;
2142                 /* We have to set the "context bit" so that the
2143                    TranslateMessage() call below that generates the
2144                    SYSCHAR message does its thing; see the documentation
2145                    on WM_SYSKEYDOWN */
2146                 lParam |= 1 << 29;
2147               }
2148           }
2149         else
2150           memcpy (keymap_sticky, keymap_orig, 256);
2151
2152         mods = mswindows_modifier_state (keymap_sticky, (DWORD) -1, has_AltGr);
2153
2154         /* Handle non-printables */
2155         if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods,
2156                                                            extendedp)))
2157           {
2158             mswindows_enqueue_keypress_event (hwnd, keysym, mods);
2159             if (sticky_changed)
2160               SetKeyboardState (keymap_orig);
2161           }
2162         else    /* Normal keys & modifiers */
2163           {
2164             Emchar quit_ch =
2165               CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd)));
2166             POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) };
2167             MSG msg, tranmsg;
2168             int potential_accelerator = 0;
2169             int got_accelerator = 0;
2170
2171             msg.hwnd = hwnd;
2172             msg.message = message_;
2173             msg.wParam = wParam;
2174             msg.lParam = lParam;
2175             msg.time = GetMessageTime();
2176             msg.pt = pnt;
2177
2178             /* GetKeyboardState() does not work as documented on Win95. We have
2179              * to loosely track Left and Right modifiers on behalf of the OS,
2180              * without screwing up Windows NT which tracks them properly. */
2181             if (wParam == VK_CONTROL)
2182               {
2183                 keymap_orig[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
2184                 keymap_sticky[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
2185               }
2186             else if (wParam == VK_MENU)
2187               {
2188                 keymap_orig[extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
2189                 keymap_sticky[extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
2190               }
2191
2192             if (!NILP (Vmenu_accelerator_enabled) &&
2193                 !(mods & XEMACS_MOD_SHIFT) && message_ == WM_SYSKEYDOWN)
2194               potential_accelerator = 1;
2195
2196             /* Remove shift modifier from an ascii character */
2197             mods &= ~XEMACS_MOD_SHIFT;
2198
2199             memcpy (keymap_trans, keymap_sticky, 256);
2200
2201             /* Clear control and alt modifiers unless AltGr is pressed */
2202             keymap_trans[VK_RCONTROL] = 0;
2203             keymap_trans[VK_LMENU] = 0;
2204             if (!has_AltGr || !(keymap_trans[VK_LCONTROL] & 0x80)
2205                 || !(keymap_trans[VK_RMENU] & 0x80))
2206               {
2207                 keymap_trans[VK_LCONTROL] = 0;
2208                 keymap_trans[VK_CONTROL] = 0;
2209                 keymap_trans[VK_RMENU] = 0;
2210                 keymap_trans[VK_MENU] = 0;
2211               }
2212             SetKeyboardState (keymap_trans);
2213
2214             /* Maybe generate some WM_[SYS]CHARs in the queue */
2215             TranslateMessage (&msg);
2216
2217             while (PeekMessage (&tranmsg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)
2218                    || PeekMessage (&tranmsg, hwnd, WM_SYSCHAR, WM_SYSCHAR,
2219                                    PM_REMOVE))
2220               {
2221                 int mods1 = mods;
2222                 WPARAM ch = tranmsg.wParam;
2223
2224                 /* If a quit char with no modifiers other than control and
2225                    shift, then mark it with a fake modifier, which is removed
2226                    upon dequeueing the event */
2227                 /* #### This might also not withstand localization, if
2228                    quit character is not a latin-1 symbol */
2229                 if (((quit_ch < ' ' && (mods & XEMACS_MOD_CONTROL)
2230                       && quit_ch + 'a' - 1 == ch)
2231                      || (quit_ch >= ' ' && !(mods & XEMACS_MOD_CONTROL)
2232                          && quit_ch == ch))
2233                     && ((mods  & ~(XEMACS_MOD_CONTROL | XEMACS_MOD_SHIFT))
2234                         == 0))
2235                   {
2236                     mods1 |= FAKE_MOD_QUIT;
2237                     ++mswindows_quit_chars_count;
2238                   }
2239                 else if (potential_accelerator && !got_accelerator &&
2240                          mswindows_char_is_accelerator (frame, ch))
2241                   {
2242                     got_accelerator = 1;
2243                     break;
2244                   }
2245                 mswindows_enqueue_keypress_event (hwnd, make_char (ch), mods1);
2246               } /* while */
2247
2248             /* This generates WM_SYSCHAR messages, which are interpreted
2249                by DefWindowProc as the menu selections. */
2250             if (got_accelerator)
2251               {
2252                 SetKeyboardState (keymap_sticky);
2253                 TranslateMessage (&msg);
2254                 SetKeyboardState (keymap_orig);
2255                 goto defproc;
2256               }
2257
2258             SetKeyboardState (keymap_orig);
2259           } /* else */
2260       }
2261
2262       if (key_needs_default_processing_p (wParam))
2263         goto defproc;
2264       else
2265         break;
2266
2267     case WM_MBUTTONDOWN:
2268     case WM_MBUTTONUP:
2269       /* Real middle mouse button has nothing to do with emulated one:
2270          if one wants to exercise fingers playing chords on the mouse,
2271          he is allowed to do that! */
2272       mswindows_enqueue_mouse_button_event (hwnd, message_,
2273                                             MAKEPOINTS (lParam),
2274                                             wParam &~ MK_MBUTTON,
2275                                             GetMessageTime());
2276       break;
2277
2278     case WM_LBUTTONUP:
2279       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2280       msframe->last_click_time =  GetMessageTime();
2281
2282       KillTimer (hwnd, BUTTON_2_TIMER_ID);
2283       msframe->button2_need_lbutton = 0;
2284       if (msframe->ignore_next_lbutton_up)
2285         {
2286           msframe->ignore_next_lbutton_up = 0;
2287         }
2288       else if (msframe->button2_is_down)
2289         {
2290           msframe->button2_is_down = 0;
2291           msframe->ignore_next_rbutton_up = 1;
2292           mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
2293                                                 MAKEPOINTS (lParam),
2294                                                 wParam
2295                                                 &~ (MK_LBUTTON | MK_MBUTTON
2296                                                     | MK_RBUTTON),
2297                                                 GetMessageTime());
2298         }
2299       else
2300         {
2301           if (msframe->button2_need_rbutton)
2302             {
2303               msframe->button2_need_rbutton = 0;
2304               mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
2305                                                     MAKEPOINTS (lParam),
2306                                                     wParam &~ MK_LBUTTON,
2307                                                     GetMessageTime());
2308             }
2309           mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
2310                                                 MAKEPOINTS (lParam),
2311                                                 wParam &~ MK_LBUTTON,
2312                                                 GetMessageTime());
2313         }
2314       break;
2315
2316     case WM_RBUTTONUP:
2317       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2318       msframe->last_click_time =  GetMessageTime();
2319
2320       KillTimer (hwnd, BUTTON_2_TIMER_ID);
2321       msframe->button2_need_rbutton = 0;
2322       if (msframe->ignore_next_rbutton_up)
2323         {
2324           msframe->ignore_next_rbutton_up = 0;
2325         }
2326       else if (msframe->button2_is_down)
2327         {
2328           msframe->button2_is_down = 0;
2329           msframe->ignore_next_lbutton_up = 1;
2330           mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
2331                                                 MAKEPOINTS (lParam),
2332                                                 wParam
2333                                                 &~ (MK_LBUTTON | MK_MBUTTON
2334                                                     | MK_RBUTTON),
2335                                                 GetMessageTime());
2336         }
2337       else
2338         {
2339           if (msframe->button2_need_lbutton)
2340             {
2341               msframe->button2_need_lbutton = 0;
2342               mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
2343                                                     MAKEPOINTS (lParam),
2344                                                     wParam &~ MK_RBUTTON,
2345                                                     GetMessageTime());
2346             }
2347           mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
2348                                                 MAKEPOINTS (lParam),
2349                                                 wParam &~ MK_RBUTTON,
2350                                                 GetMessageTime());
2351         }
2352       break;
2353
2354     case WM_LBUTTONDOWN:
2355       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2356
2357       if (msframe->button2_need_lbutton)
2358         {
2359           KillTimer (hwnd, BUTTON_2_TIMER_ID);
2360           msframe->button2_need_lbutton = 0;
2361           msframe->button2_need_rbutton = 0;
2362           if (mswindows_button2_near_enough (msframe->last_click_point,
2363                                              MAKEPOINTS (lParam)))
2364             {
2365               mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
2366                                                     MAKEPOINTS (lParam),
2367                                                     wParam
2368                                                     &~ (MK_LBUTTON | MK_MBUTTON
2369                                                         | MK_RBUTTON),
2370                                                     GetMessageTime());
2371               msframe->button2_is_down = 1;
2372             }
2373           else
2374             {
2375               mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
2376                                                     msframe->last_click_point,
2377                                                     msframe->last_click_mods
2378                                                     &~ MK_RBUTTON,
2379                                                     msframe->last_click_time);
2380               mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
2381                                                     MAKEPOINTS (lParam),
2382                                                     wParam &~ MK_LBUTTON,
2383                                                     GetMessageTime());
2384             }
2385         }
2386       else
2387         {
2388           mswindows_set_chord_timer (hwnd);
2389           msframe->button2_need_rbutton = 1;
2390           msframe->last_click_point = MAKEPOINTS (lParam);
2391           msframe->last_click_mods = wParam;
2392         }
2393       msframe->last_click_time =  GetMessageTime();
2394       break;
2395
2396     case WM_RBUTTONDOWN:
2397       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2398
2399       if (msframe->button2_need_rbutton)
2400         {
2401           KillTimer (hwnd, BUTTON_2_TIMER_ID);
2402           msframe->button2_need_lbutton = 0;
2403           msframe->button2_need_rbutton = 0;
2404           if (mswindows_button2_near_enough (msframe->last_click_point,
2405                                              MAKEPOINTS (lParam)))
2406             {
2407               mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
2408                                                     MAKEPOINTS (lParam),
2409                                                     wParam
2410                                                     &~ (MK_LBUTTON | MK_MBUTTON
2411                                                         | MK_RBUTTON),
2412                                                     GetMessageTime());
2413               msframe->button2_is_down = 1;
2414             }
2415           else
2416             {
2417               mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
2418                                                     msframe->last_click_point,
2419                                                     msframe->last_click_mods
2420                                                     &~ MK_LBUTTON,
2421                                                     msframe->last_click_time);
2422               mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
2423                                                     MAKEPOINTS (lParam),
2424                                                     wParam &~ MK_RBUTTON,
2425                                                     GetMessageTime());
2426             }
2427         }
2428       else
2429         {
2430           mswindows_set_chord_timer (hwnd);
2431           msframe->button2_need_lbutton = 1;
2432           msframe->last_click_point = MAKEPOINTS (lParam);
2433           msframe->last_click_mods = wParam;
2434         }
2435       msframe->last_click_time =  GetMessageTime();
2436       break;
2437
2438     case WM_TIMER:
2439       if (wParam == BUTTON_2_TIMER_ID)
2440         {
2441           msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2442           KillTimer (hwnd, BUTTON_2_TIMER_ID);
2443
2444           if (msframe->button2_need_lbutton)
2445             {
2446               msframe->button2_need_lbutton = 0;
2447               mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
2448                                                     msframe->last_click_point,
2449                                                     msframe->last_click_mods
2450                                                     &~ MK_RBUTTON,
2451                                                     msframe->last_click_time);
2452             }
2453           else if (msframe->button2_need_rbutton)
2454             {
2455               msframe->button2_need_rbutton = 0;
2456               mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
2457                                                     msframe->last_click_point,
2458                                                     msframe->last_click_mods
2459                                                     &~ MK_LBUTTON,
2460                                                     msframe->last_click_time);
2461             }
2462         }
2463       else
2464         assert ("Spurious timer fired" == 0);
2465       break;
2466
2467     case WM_MOUSEMOVE:
2468       /* Optimization: don't report mouse movement while size is changing */
2469       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2470       if (!msframe->sizing)
2471         {
2472           /* When waiting for the second mouse button to finish
2473              button2 emulation, and have moved too far, just pretend
2474              as if timer has expired. This improves drag-select feedback */
2475           if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton)
2476               && !mswindows_button2_near_enough (msframe->last_click_point,
2477                                                  MAKEPOINTS (lParam)))
2478             {
2479               KillTimer (hwnd, BUTTON_2_TIMER_ID);
2480               SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0);
2481             }
2482
2483           emacs_event = Fmake_event (Qnil, Qnil);
2484           event = XEVENT(emacs_event);
2485
2486           event->channel = mswindows_find_frame(hwnd);
2487           event->timestamp = GetMessageTime();
2488           event->event_type = pointer_motion_event;
2489           event->event.motion.x = MAKEPOINTS(lParam).x;
2490           event->event.motion.y = MAKEPOINTS(lParam).y;
2491           event->event.motion.modifiers =
2492             mswindows_modifier_state (NULL, wParam, 0);
2493
2494           mswindows_enqueue_dispatch_event (emacs_event);
2495         }
2496       break;
2497
2498     case WM_CANCELMODE:
2499       ReleaseCapture ();
2500       /* Queue a `cancel-mode-internal' misc user event, so mouse
2501          selection would be canceled if any */
2502       mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd),
2503                                          Qcancel_mode_internal, Qnil);
2504       break;
2505
2506     case WM_NOTIFY:
2507       {
2508         LPNMHDR nmhdr = (LPNMHDR)lParam;
2509
2510         if (nmhdr->code ==  TTN_NEEDTEXT)
2511           {
2512 #ifdef HAVE_TOOLBARS
2513             LPTOOLTIPTEXT tttext = (LPTOOLTIPTEXT)lParam;
2514             Lisp_Object btext;
2515
2516             /* find out which toolbar */
2517             frame = XFRAME (mswindows_find_frame (hwnd));
2518             btext = mswindows_get_toolbar_button_text ( frame,
2519                                                         nmhdr->idFrom );
2520
2521             tttext->lpszText = NULL;
2522             tttext->hinst = NULL;
2523
2524             if (!NILP(btext))
2525               {
2526                 /* I think this is safe since the text will only go away
2527                    when the toolbar does...*/
2528                 LISP_STRING_TO_EXTERNAL (btext, tttext->lpszText, Qnative);
2529               }
2530 #endif
2531           }
2532         /* handle tree view callbacks */
2533         else if (nmhdr->code == TVN_SELCHANGED)
2534           {
2535             NM_TREEVIEW* ptree = (NM_TREEVIEW*)lParam;
2536             frame = XFRAME (mswindows_find_frame (hwnd));
2537             mswindows_handle_gui_wm_command (frame, 0, ptree->itemNew.lParam);
2538           }
2539         /* handle tab control callbacks */
2540         else if (nmhdr->code == TCN_SELCHANGE)
2541           {
2542             TC_ITEM item;
2543             int idx = SendMessage (nmhdr->hwndFrom, TCM_GETCURSEL, 0, 0);
2544             frame = XFRAME (mswindows_find_frame (hwnd));
2545
2546             item.mask = TCIF_PARAM;
2547             SendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM)idx,
2548                          (LPARAM)&item);
2549
2550             mswindows_handle_gui_wm_command (frame, 0, item.lParam);
2551           }
2552       }
2553       break;
2554
2555     case WM_PAINT:
2556       /* hdc will be NULL unless this is a subwindow - in which case we
2557          shouldn't have received a paint message for it here. */
2558       assert (wParam == 0);
2559
2560       /* Can't queue a magic event because windows goes modal and sends paint
2561          messages directly to the windows procedure when doing solid drags
2562          and the message queue doesn't get processed. */
2563       mswindows_handle_paint (XFRAME (mswindows_find_frame (hwnd)));
2564       break;
2565
2566     case WM_SIZE:
2567       /* We only care about this message if our size has really changed */
2568       if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
2569         {
2570           RECT rect;
2571           int columns, rows;
2572
2573           fobj = mswindows_find_frame (hwnd);
2574           frame = XFRAME (fobj);
2575           msframe  = FRAME_MSWINDOWS_DATA (frame);
2576
2577           /* We cannot handle frame map and unmap hooks right in
2578              this routine, because these may throw. We queue
2579              magic events to run these hooks instead - kkm */
2580
2581           if (wParam==SIZE_MINIMIZED)
2582             {
2583               /* Iconified */
2584               FRAME_VISIBLE_P (frame) = 0;
2585               mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME);
2586             }
2587           else
2588             {
2589               GetClientRect(hwnd, &rect);
2590               FRAME_PIXWIDTH(frame) = rect.right;
2591               FRAME_PIXHEIGHT(frame) = rect.bottom;
2592
2593               pixel_to_real_char_size (frame, rect.right, rect.bottom,
2594                                        &FRAME_MSWINDOWS_CHARWIDTH (frame),
2595                                        &FRAME_MSWINDOWS_CHARHEIGHT (frame));
2596
2597               pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows);
2598               change_frame_size (frame, rows, columns, 1);
2599
2600               /* If we are inside frame creation, we have to apply geometric
2601                  properties now. */
2602               if (FRAME_MSWINDOWS_TARGET_RECT (frame))
2603                 {
2604                   /* Yes, we have to size again */
2605                   mswindows_size_frame_internal ( frame,
2606                                                   FRAME_MSWINDOWS_TARGET_RECT
2607                                                   (frame));
2608                   /* Reset so we do not get here again. The SetWindowPos call in
2609                    * mswindows_size_frame_internal can cause recursion here. */
2610                   if (FRAME_MSWINDOWS_TARGET_RECT (frame))
2611                     {
2612                       xfree (FRAME_MSWINDOWS_TARGET_RECT (frame));
2613                       FRAME_MSWINDOWS_TARGET_RECT (frame) = 0;
2614                     }
2615                 }
2616               else
2617                 {
2618                   if (!msframe->sizing && !FRAME_VISIBLE_P (frame))
2619                     mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME);
2620                   FRAME_VISIBLE_P (frame) = 1;
2621
2622                   if (!msframe->sizing || mswindows_dynamic_frame_resize)
2623                     redisplay ();
2624                 }
2625             }
2626         }
2627       break;
2628
2629     case WM_DISPLAYCHANGE:
2630       {
2631         struct device *d;
2632         DWORD message_tick = GetMessageTime ();
2633
2634         fobj = mswindows_find_frame (hwnd);
2635         frame = XFRAME (fobj);
2636         d = XDEVICE (FRAME_DEVICE (frame));
2637
2638         /* Do this only once per message. XEmacs can receive this message
2639            through as many frames as it currently has open. Message time
2640            will be the same for all these messages. Despite extreme
2641            efficiency, the code below has about one in 4 billion
2642            probability that the HDC is not recreated, provided that
2643            XEmacs is running sufficiently longer than 52 days. */
2644         if (DEVICE_MSWINDOWS_UPDATE_TICK(d) != message_tick)
2645           {
2646             DEVICE_MSWINDOWS_UPDATE_TICK(d) = message_tick;
2647             DeleteDC (DEVICE_MSWINDOWS_HCDC(d));
2648             DEVICE_MSWINDOWS_HCDC(d) = CreateCompatibleDC (NULL);
2649           }
2650       }
2651       break;
2652
2653       /* Misc magic events which only require that the frame be identified */
2654     case WM_SETFOCUS:
2655     case WM_KILLFOCUS:
2656       mswindows_enqueue_magic_event (hwnd, message_);
2657       break;
2658
2659     case WM_WINDOWPOSCHANGING:
2660       {
2661         WINDOWPOS *wp = (LPWINDOWPOS) lParam;
2662         WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) };
2663         GetWindowPlacement(hwnd, &wpl);
2664
2665         /* Only interested if size is changing and we're not being iconified */
2666         if (wpl.showCmd != SW_SHOWMINIMIZED
2667             && wpl.showCmd != SW_SHOWMAXIMIZED
2668             && !(wp->flags & SWP_NOSIZE))
2669           {
2670             RECT ncsize = { 0, 0, 0, 0 };
2671             int pixwidth, pixheight;
2672             AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE),
2673                                 GetMenu(hwnd) != NULL,
2674                                 GetWindowLong (hwnd, GWL_EXSTYLE));
2675
2676             round_size_to_real_char (XFRAME (mswindows_find_frame (hwnd)),
2677                                      wp->cx - (ncsize.right - ncsize.left),
2678                                      wp->cy - (ncsize.bottom - ncsize.top),
2679                                      &pixwidth, &pixheight);
2680
2681             /* Convert client sizes to window sizes */
2682             pixwidth += (ncsize.right - ncsize.left);
2683             pixheight += (ncsize.bottom - ncsize.top);
2684
2685             if (wpl.showCmd != SW_SHOWMAXIMIZED)
2686               {
2687                 /* Adjust so that the bottom or right doesn't move if it's
2688                  * the top or left that's being changed */
2689                 RECT rect;
2690                 GetWindowRect (hwnd, &rect);
2691
2692                 if (rect.left != wp->x)
2693                   wp->x += wp->cx - pixwidth;
2694                 if (rect.top != wp->y)
2695                   wp->y += wp->cy - pixheight;
2696               }
2697
2698             wp->cx = pixwidth;
2699             wp->cy = pixheight;
2700           }
2701         /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts
2702            window position if the user tries to track window too small */
2703       }
2704       goto defproc;
2705
2706     case WM_ENTERSIZEMOVE:
2707       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2708       msframe->sizing = 1;
2709       return 0;
2710
2711     case WM_EXITSIZEMOVE:
2712       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2713       msframe->sizing = 0;
2714       /* Queue noop event */
2715       mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
2716       return 0;
2717
2718 #ifdef HAVE_SCROLLBARS
2719     case WM_VSCROLL:
2720     case WM_HSCROLL:
2721       {
2722         /* Direction of scroll is determined by scrollbar instance. */
2723         int code = (int) LOWORD(wParam);
2724         int pos = (short int) HIWORD(wParam);
2725         HWND hwndScrollBar = (HWND) lParam;
2726         struct gcpro gcpro1, gcpro2;
2727
2728         mswindows_handle_scrollbar_event (hwndScrollBar, code,  pos);
2729         GCPRO2 (emacs_event, fobj);
2730         if (UNBOUNDP(mswindows_pump_outstanding_events()))      /* Can GC */
2731           {
2732             /* Error during event pumping - cancel scroll */
2733             SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
2734           }
2735         UNGCPRO;
2736         break;
2737       }
2738
2739     case WM_MOUSEWHEEL:
2740       {
2741         int keys = LOWORD (wParam); /* Modifier key flags */
2742         int delta = (short) HIWORD (wParam); /* Wheel rotation amount */
2743         struct gcpro gcpro1, gcpro2;
2744
2745         if (mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd), keys,  delta))
2746           {
2747             GCPRO2 (emacs_event, fobj);
2748             mswindows_pump_outstanding_events ();       /* Can GC */
2749             UNGCPRO;
2750           }
2751         else
2752           goto defproc;
2753         break;
2754       }
2755 #endif
2756
2757 #ifdef HAVE_MENUBARS
2758     case WM_INITMENU:
2759       if (UNBOUNDP (mswindows_handle_wm_initmenu (
2760                                                   (HMENU) wParam,
2761                                                   XFRAME (mswindows_find_frame (hwnd)))))
2762         SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2763       break;
2764
2765     case WM_INITMENUPOPUP:
2766       if (!HIWORD(lParam))
2767         {
2768           if (UNBOUNDP (mswindows_handle_wm_initmenupopup (
2769                                                            (HMENU) wParam,
2770                                                            XFRAME (mswindows_find_frame (hwnd)))))
2771             SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2772         }
2773       break;
2774
2775 #endif /* HAVE_MENUBARS */
2776
2777     case WM_COMMAND:
2778       {
2779         WORD id = LOWORD (wParam);
2780         WORD nid = HIWORD (wParam);
2781         HWND cid = (HWND)lParam;
2782         frame = XFRAME (mswindows_find_frame (hwnd));
2783
2784 #ifdef HAVE_TOOLBARS
2785         if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id)))
2786           break;
2787 #endif
2788         /* widgets in a buffer only eval a callback for suitable events.*/
2789         switch (nid)
2790           {
2791           case BN_CLICKED:
2792           case EN_CHANGE:
2793           case CBN_EDITCHANGE:
2794           case CBN_SELCHANGE:
2795             if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id)))
2796               return 0;
2797           }
2798         /* menubars always must come last since the hashtables do not
2799            always exist*/
2800 #ifdef HAVE_MENUBARS
2801         if (!NILP (mswindows_handle_wm_command (frame, id)))
2802           break;
2803 #endif
2804
2805         return DefWindowProc (hwnd, message_, wParam, lParam);
2806         /* Bite me - a spurious command. This used to not be able to
2807            happen but with the introduction of widgets its now
2808            possible. */
2809       }
2810       break;
2811
2812     case WM_CTLCOLORBTN:
2813     case WM_CTLCOLORLISTBOX:
2814     case WM_CTLCOLOREDIT:
2815     case WM_CTLCOLORSTATIC:
2816     case WM_CTLCOLORSCROLLBAR:
2817       {
2818         /* if we get an opportunity to paint a widget then do so if
2819            there is an appropriate face */
2820         HWND crtlwnd = (HWND)lParam;
2821         LONG ii = GetWindowLong (crtlwnd, GWL_USERDATA);
2822         if (ii)
2823           {
2824             Lisp_Object image_instance;
2825             VOID_TO_LISP (image_instance, ii);
2826             if (IMAGE_INSTANCEP (image_instance)
2827                 &&
2828                 IMAGE_INSTANCE_TYPE_P (image_instance, IMAGE_WIDGET))
2829               {
2830                 /* set colors for the buttons */
2831                 HDC hdc = (HDC)wParam;
2832                 if (last_widget_brushed != ii)
2833                   {
2834                     if (widget_brush)
2835                       DeleteObject (widget_brush);
2836                     widget_brush = CreateSolidBrush
2837                       (COLOR_INSTANCE_MSWINDOWS_COLOR
2838                        (XCOLOR_INSTANCE
2839                         (FACE_BACKGROUND
2840                          (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2841                           XIMAGE_INSTANCE_FRAME (image_instance)))));
2842                   }
2843                 last_widget_brushed = ii;
2844                 SetTextColor
2845                   (hdc,
2846                    COLOR_INSTANCE_MSWINDOWS_COLOR
2847                    (XCOLOR_INSTANCE
2848                     (FACE_FOREGROUND
2849                      (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2850                       XIMAGE_INSTANCE_FRAME (image_instance)))));
2851                 SetBkMode (hdc, OPAQUE);
2852                 SetBkColor
2853                   (hdc,
2854                    COLOR_INSTANCE_MSWINDOWS_COLOR
2855                    (XCOLOR_INSTANCE
2856                     (FACE_BACKGROUND
2857                      (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2858                       XIMAGE_INSTANCE_FRAME (image_instance)))));
2859                 return (LRESULT)widget_brush;
2860               }
2861           }
2862       }
2863       goto defproc;
2864
2865 #ifdef HAVE_DRAGNDROP
2866     case WM_DROPFILES:  /* implementation ripped-off from event-Xt.c */
2867       {
2868         UINT filecount, i, len;
2869         POINT point;
2870         char* filename;
2871         char* fname;
2872
2873         Lisp_Object l_dndlist = Qnil, l_item = Qnil;
2874         struct gcpro gcpro1, gcpro2, gcpro3;
2875
2876         emacs_event = Fmake_event (Qnil, Qnil);
2877         event = XEVENT(emacs_event);
2878
2879         GCPRO3 (emacs_event, l_dndlist, l_item);
2880
2881         if (!DragQueryPoint ((HDROP) wParam, &point))
2882           point.x = point.y = -1;               /* outside client area */
2883
2884         event->event_type = misc_user_event;
2885         event->channel = mswindows_find_frame(hwnd);
2886         event->timestamp = GetMessageTime();
2887         event->event.misc.button = 1;           /* #### Should try harder */
2888         event->event.misc.modifiers = mswindows_modifier_state (NULL,
2889                                                                 (DWORD) -1, 0);
2890         event->event.misc.x = point.x;
2891         event->event.misc.y = point.y;
2892         event->event.misc.function = Qdragdrop_drop_dispatch;
2893
2894         filecount = DragQueryFile ((HDROP) wParam, 0xffffffff, NULL, 0);
2895         for (i=0; i<filecount; i++)
2896           {
2897             len = DragQueryFile ((HDROP) wParam, i, NULL, 0);
2898             /* The URLs that we make here aren't correct according to section
2899              * 3.10 of rfc1738 because they're missing the //<host>/ part and
2900              * because they may contain reserved characters. But that's OK -
2901              * they just need to be good enough to keep dragdrop.el happy. */
2902             fname = (char *)xmalloc (len+1);
2903             DragQueryFile ((HANDLE) wParam, i, fname, len+1);
2904
2905             /* May be a shell link aka "shortcut" - replace fname if so */
2906 #if !(defined(CYGWIN) || defined(MINGW))
2907             /* cygwin doesn't define this COM stuff */
2908             if (!stricmp (fname + strlen (fname) - 4, ".LNK"))
2909               {
2910                 IShellLink* psl;
2911
2912                 if (CoCreateInstance (&CLSID_ShellLink, NULL,
2913                                       CLSCTX_INPROC_SERVER, &IID_IShellLink, &psl) == S_OK)
2914                   {
2915                     IPersistFile* ppf;
2916
2917                     if (psl->lpVtbl->QueryInterface (psl, &IID_IPersistFile,
2918                                                      &ppf) == S_OK)
2919                       {
2920                         WORD wsz[MAX_PATH];
2921                         WIN32_FIND_DATA wfd;
2922                         LPSTR resolved = (char *) xmalloc (MAX_PATH+1);
2923
2924                         MultiByteToWideChar (CP_ACP,0, fname, -1, wsz, MAX_PATH);
2925
2926                         if ((ppf->lpVtbl->Load (ppf, wsz, STGM_READ) == S_OK) &&
2927                             (psl->lpVtbl->GetPath (psl, resolved, MAX_PATH,
2928                                                    &wfd, 0)==S_OK))
2929                           {
2930                             xfree (fname);
2931                             fname = resolved;
2932                             len = strlen (fname);
2933                           }
2934
2935                         ppf->lpVtbl->Release (ppf);
2936                       }
2937
2938                     psl->lpVtbl->Release (psl);
2939                   }
2940               }
2941 #endif
2942
2943 #ifdef CYGWIN
2944             filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5);
2945             strcpy (filename, "file:");
2946             cygwin32_win32_to_posix_path_list (fname, filename+5);
2947 #else
2948             filename = (char *)xmalloc (len+6);
2949             strcat (strcpy (filename, "file:"), fname);
2950             dostounix_filename (filename+5);
2951 #endif
2952             xfree (fname);
2953             l_item = make_string (filename, strlen (filename));
2954             l_dndlist = Fcons (l_item, l_dndlist);
2955             xfree (filename);
2956           }
2957         DragFinish ((HDROP) wParam);
2958
2959         event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist);
2960         mswindows_enqueue_dispatch_event (emacs_event);
2961         UNGCPRO;
2962       }
2963       break;
2964 #endif
2965
2966     defproc:
2967     default:
2968       return DefWindowProc (hwnd, message_, wParam, lParam);
2969     }
2970   return (0);
2971 }
2972
2973
2974 /************************************************************************/
2975 /*      keyboard, mouse & other helpers for the windows procedure       */
2976 /************************************************************************/
2977 static void
2978 mswindows_set_chord_timer (HWND hwnd)
2979 {
2980   int interval;
2981
2982   /* We get one third half system double click threshold */
2983   if (mswindows_mouse_button_tolerance <= 0)
2984     interval = GetDoubleClickTime () / 3;
2985   else
2986     interval = mswindows_mouse_button_tolerance;
2987
2988   SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0);
2989 }
2990
2991 static int
2992 mswindows_button2_near_enough (POINTS p1, POINTS p2)
2993 {
2994   int dx, dy;
2995   if (mswindows_mouse_button_max_skew_x <= 0)
2996     dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2;
2997   else
2998     dx = mswindows_mouse_button_max_skew_x;
2999
3000   if (mswindows_mouse_button_max_skew_y <= 0)
3001     dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2;
3002   else
3003     dy = mswindows_mouse_button_max_skew_y;
3004
3005   return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy;
3006 }
3007
3008 static int
3009 mswindows_current_layout_has_AltGr (void)
3010 {
3011   /* This simple caching mechanism saves 10% of CPU
3012      time when a key typed at autorepeat rate of 30 cps! */
3013   static HKL last_hkl = 0;
3014   static int last_hkl_has_AltGr;
3015   HKL current_hkl = (HKL) -1;
3016
3017   if (xGetKeyboardLayout) /* not in NT 3.5 */
3018     current_hkl = xGetKeyboardLayout (0);
3019   if (current_hkl != last_hkl)
3020     {
3021       TCHAR c;
3022       last_hkl_has_AltGr = 0;
3023       /* In this loop, we query whether a character requires
3024          AltGr to be down to generate it. If at least such one
3025          found, this means that the layout does regard AltGr */
3026       for (c = ' '; c <= 0xFFU && c != 0 && !last_hkl_has_AltGr; ++c)
3027         if (HIBYTE (VkKeyScan (c)) == 6)
3028           last_hkl_has_AltGr = 1;
3029       last_hkl = current_hkl;
3030     }
3031   return last_hkl_has_AltGr;
3032 }
3033
3034
3035 /* Returns the state of the modifier keys in the format expected by the
3036  * Lisp_Event key_data, button_data and motion_data modifiers member */
3037 static int
3038 mswindows_modifier_state (BYTE* keymap, DWORD fwKeys, int has_AltGr)
3039 {
3040   int mods = 0;
3041   int keys_is_real = 0;
3042   BYTE keymap2[256];
3043
3044   if (fwKeys == (DWORD) -1)
3045     fwKeys = mswindows_last_mouse_button_state;
3046   else
3047     {
3048       keys_is_real = 1;
3049       mswindows_last_mouse_button_state = fwKeys;
3050     }
3051
3052   if (keymap == NULL)
3053     {
3054       keymap = keymap2;
3055       GetKeyboardState (keymap);
3056       has_AltGr = mswindows_current_layout_has_AltGr ();
3057     }
3058
3059   /* #### should look at fwKeys for MK_CONTROL.  I don't understand how
3060      AltGr works. */
3061   if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80))
3062     {
3063       mods |= (keymap [VK_LMENU] & 0x80) ? XEMACS_MOD_META : 0;
3064       mods |= (keymap [VK_RCONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
3065     }
3066   else
3067     {
3068       mods |= (keymap [VK_MENU] & 0x80) ? XEMACS_MOD_META : 0;
3069       mods |= (keymap [VK_CONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
3070     }
3071
3072   mods |= (keys_is_real ? fwKeys & MK_SHIFT : (keymap [VK_SHIFT] & 0x80))
3073     ? XEMACS_MOD_SHIFT : 0;
3074   mods |= fwKeys & MK_LBUTTON ? XEMACS_MOD_BUTTON1 : 0;
3075   mods |= fwKeys & MK_MBUTTON ? XEMACS_MOD_BUTTON2 : 0;
3076   mods |= fwKeys & MK_RBUTTON ? XEMACS_MOD_BUTTON3 : 0;
3077
3078   return mods;
3079 }
3080
3081 /*
3082  * Translate a mswindows virtual key to a keysym.
3083  * Only returns non-Qnil for keys that don't generate WM_CHAR messages
3084  * or whose ASCII codes (like space) xemacs doesn't like.
3085  */
3086 Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
3087                                            int extendedp)
3088 {
3089   if (extendedp)        /* Keys not present on a 82 key keyboard */
3090     {
3091       switch (mswindows_key)
3092         {
3093         case VK_CANCEL:         return KEYSYM ("pause");
3094         case VK_RETURN:         return KEYSYM ("kp-enter");
3095         case VK_PRIOR:          return KEYSYM ("prior");
3096         case VK_NEXT:           return KEYSYM ("next");
3097         case VK_END:            return KEYSYM ("end");
3098         case VK_HOME:           return KEYSYM ("home");
3099         case VK_LEFT:           return KEYSYM ("left");
3100         case VK_UP:             return KEYSYM ("up");
3101         case VK_RIGHT:          return KEYSYM ("right");
3102         case VK_DOWN:           return KEYSYM ("down");
3103         case VK_INSERT:         return KEYSYM ("insert");
3104         case VK_DELETE:         return QKdelete;
3105 #if 0   /* FSF Emacs allows these to return configurable syms/mods */
3106         case VK_LWIN            return KEYSYM ("");
3107         case VK_RWIN            return KEYSYM ("");
3108 #endif
3109         case VK_APPS:           return KEYSYM ("menu");
3110         }
3111     }
3112   else
3113     {
3114       switch (mswindows_key)
3115         {
3116         case VK_BACK:           return QKbackspace;
3117         case VK_TAB:            return QKtab;
3118         case '\n':              return QKlinefeed;
3119         case VK_CLEAR:          return KEYSYM ("clear");
3120         case VK_RETURN:         return QKreturn;
3121         case VK_PAUSE:          return KEYSYM ("pause");
3122         case VK_ESCAPE:         return QKescape;
3123         case VK_SPACE:          return QKspace;
3124         case VK_PRIOR:          return KEYSYM ("kp-prior");
3125         case VK_NEXT:           return KEYSYM ("kp-next");
3126         case VK_END:            return KEYSYM ("kp-end");
3127         case VK_HOME:           return KEYSYM ("kp-home");
3128         case VK_LEFT:           return KEYSYM ("kp-left");
3129         case VK_UP:             return KEYSYM ("kp-up");
3130         case VK_RIGHT:          return KEYSYM ("kp-right");
3131         case VK_DOWN:           return KEYSYM ("kp-down");
3132         case VK_SELECT:         return KEYSYM ("select");
3133         case VK_PRINT:          return KEYSYM ("print");
3134         case VK_EXECUTE:        return KEYSYM ("execute");
3135         case VK_SNAPSHOT:       return KEYSYM ("print");
3136         case VK_INSERT:         return KEYSYM ("kp-insert");
3137         case VK_DELETE:         return KEYSYM ("kp-delete");
3138         case VK_HELP:           return KEYSYM ("help");
3139         case VK_NUMPAD0:        return KEYSYM ("kp-0");
3140         case VK_NUMPAD1:        return KEYSYM ("kp-1");
3141         case VK_NUMPAD2:        return KEYSYM ("kp-2");
3142         case VK_NUMPAD3:        return KEYSYM ("kp-3");
3143         case VK_NUMPAD4:        return KEYSYM ("kp-4");
3144         case VK_NUMPAD5:        return KEYSYM ("kp-5");
3145         case VK_NUMPAD6:        return KEYSYM ("kp-6");
3146         case VK_NUMPAD7:        return KEYSYM ("kp-7");
3147         case VK_NUMPAD8:        return KEYSYM ("kp-8");
3148         case VK_NUMPAD9:        return KEYSYM ("kp-9");
3149         case VK_MULTIPLY:       return KEYSYM ("kp-multiply");
3150         case VK_ADD:            return KEYSYM ("kp-add");
3151         case VK_SEPARATOR:      return KEYSYM ("kp-separator");
3152         case VK_SUBTRACT:       return KEYSYM ("kp-subtract");
3153         case VK_DECIMAL:        return KEYSYM ("kp-decimal");
3154         case VK_DIVIDE:         return KEYSYM ("kp-divide");
3155         case VK_F1:             return KEYSYM ("f1");
3156         case VK_F2:             return KEYSYM ("f2");
3157         case VK_F3:             return KEYSYM ("f3");
3158         case VK_F4:             return KEYSYM ("f4");
3159         case VK_F5:             return KEYSYM ("f5");
3160         case VK_F6:             return KEYSYM ("f6");
3161         case VK_F7:             return KEYSYM ("f7");
3162         case VK_F8:             return KEYSYM ("f8");
3163         case VK_F9:             return KEYSYM ("f9");
3164         case VK_F10:            return KEYSYM ("f10");
3165         case VK_F11:            return KEYSYM ("f11");
3166         case VK_F12:            return KEYSYM ("f12");
3167         case VK_F13:            return KEYSYM ("f13");
3168         case VK_F14:            return KEYSYM ("f14");
3169         case VK_F15:            return KEYSYM ("f15");
3170         case VK_F16:            return KEYSYM ("f16");
3171         case VK_F17:            return KEYSYM ("f17");
3172         case VK_F18:            return KEYSYM ("f18");
3173         case VK_F19:            return KEYSYM ("f19");
3174         case VK_F20:            return KEYSYM ("f20");
3175         case VK_F21:            return KEYSYM ("f21");
3176         case VK_F22:            return KEYSYM ("f22");
3177         case VK_F23:            return KEYSYM ("f23");
3178         case VK_F24:            return KEYSYM ("f24");
3179         }
3180     }
3181   return Qnil;
3182 }
3183
3184 /*
3185  * Find the console that matches the supplied mswindows window handle
3186  */
3187 Lisp_Object
3188 mswindows_find_console (HWND hwnd)
3189 {
3190   /* We only support one console */
3191   return XCAR (Vconsole_list);
3192 }
3193
3194 /*
3195  * Find the frame that matches the supplied mswindows window handle
3196  */
3197 static Lisp_Object
3198 mswindows_find_frame (HWND hwnd)
3199 {
3200   LONG l = GetWindowLong (hwnd, XWL_FRAMEOBJ);
3201   Lisp_Object f;
3202   if (l == 0)
3203     {
3204       /* We are in progress of frame creation. Return the frame
3205          being created, as it still not remembered in the window
3206          extra storage. */
3207       assert (!NILP (Vmswindows_frame_being_created));
3208       return Vmswindows_frame_being_created;
3209     }
3210   VOID_TO_LISP (f, l);
3211   return f;
3212 }
3213
3214 \f
3215 /************************************************************************/
3216 /*                            methods                                   */
3217 /************************************************************************/
3218
3219 static int
3220 emacs_mswindows_add_timeout (EMACS_TIME thyme)
3221 {
3222   int milliseconds;
3223   EMACS_TIME current_time;
3224   EMACS_GET_TIME (current_time);
3225   EMACS_SUB_TIME (thyme, thyme, current_time);
3226   milliseconds = EMACS_SECS (thyme) * 1000 +
3227     (EMACS_USECS (thyme) + 500) / 1000;
3228   if (milliseconds < 1)
3229     milliseconds = 1;
3230   ++mswindows_pending_timers_count;
3231   return SetTimer (NULL, 0, milliseconds,
3232                    (TIMERPROC) mswindows_wm_timer_callback);
3233 }
3234
3235 static void
3236 emacs_mswindows_remove_timeout (int id)
3237 {
3238   Lisp_Event match_against;
3239   Lisp_Object emacs_event;
3240
3241   if (KillTimer (NULL, id))
3242     --mswindows_pending_timers_count;
3243
3244   /* If there is a dispatch event generated by this
3245      timeout in the queue, we have to remove it too. */
3246   match_against.event_type = timeout_event;
3247   match_against.event.timeout.interval_id = id;
3248   emacs_event = mswindows_cancel_dispatch_event (&match_against);
3249   if (!NILP (emacs_event))
3250     Fdeallocate_event(emacs_event);
3251 }
3252
3253 /* If `user_p' is false, then return whether there are any win32, timeout,
3254  * or subprocess events pending (that is, whether
3255  * emacs_mswindows_next_event() would return immediately without blocking).
3256  *
3257  * if `user_p' is true, then return whether there are any *user generated*
3258  * events available (that is, whether there are keyboard or mouse-click
3259  * events ready to be read).  This also implies that
3260  * emacs_mswindows_next_event() would not block.
3261  */
3262 static int
3263 emacs_mswindows_event_pending_p (int user_p)
3264 {
3265   mswindows_need_event (0);
3266   return (!NILP (mswindows_u_dispatch_event_queue)
3267           || (!user_p && !NILP (mswindows_s_dispatch_event_queue)));
3268 }
3269
3270 /*
3271  * Return the next event
3272  */
3273 static void
3274 emacs_mswindows_next_event (Lisp_Event *emacs_event)
3275 {
3276   Lisp_Object event, event2;
3277
3278   mswindows_need_event (1);
3279
3280   event = mswindows_dequeue_dispatch_event ();
3281   XSETEVENT (event2, emacs_event);
3282   Fcopy_event (event, event2);
3283   Fdeallocate_event (event);
3284 }
3285
3286 /*
3287  * Handle a magic event off the dispatch queue.
3288  */
3289 static void
3290 emacs_mswindows_handle_magic_event (Lisp_Event *emacs_event)
3291 {
3292   switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event))
3293     {
3294     case XM_BUMPQUEUE:
3295       break;
3296
3297     case WM_PAINT:
3298       {
3299         struct frame *f = XFRAME (EVENT_CHANNEL (emacs_event));
3300         mswindows_handle_paint (f);
3301         (FRAME_MSWINDOWS_DATA (f))->paint_pending = 0;
3302       }
3303       break;
3304
3305     case WM_SETFOCUS:
3306     case WM_KILLFOCUS:
3307       {
3308         Lisp_Object frame = EVENT_CHANNEL (emacs_event);
3309         struct frame *f = XFRAME (frame);
3310         int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS);
3311         Lisp_Object conser;
3312         struct gcpro gcpro1;
3313
3314         /* On focus change, clear all memory of sticky modifiers
3315            to avoid non-intuitive behavior. */
3316         clear_sticky_modifiers ();
3317
3318         conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil));
3319         GCPRO1 (conser);
3320         emacs_handle_focus_change_preliminary (conser);
3321         /* Under X the stuff up to here is done in the X event handler.
3322            I Don't know why */
3323         emacs_handle_focus_change_final (conser);
3324         UNGCPRO;
3325
3326       }
3327       break;
3328
3329     case XM_MAPFRAME:
3330     case XM_UNMAPFRAME:
3331       {
3332         Lisp_Object frame = EVENT_CHANNEL (emacs_event);
3333         va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)
3334                                == XM_MAPFRAME ?
3335                                Qmap_frame_hook : Qunmap_frame_hook,
3336                                1, frame);
3337       }
3338       break;
3339
3340       /* #### What about Enter & Leave */
3341 #if 0
3342       va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook :
3343                              Qmouse_leave_frame_hook, 1, frame);
3344 #endif
3345
3346     default:
3347       assert(0);
3348     }
3349 }
3350
3351 #ifndef HAVE_MSG_SELECT
3352 static HANDLE
3353 get_process_input_waitable (Lisp_Process *process)
3354 {
3355   Lisp_Object instr, outstr, p;
3356   XSETPROCESS (p, process);
3357   get_process_streams (process, &instr, &outstr);
3358   assert (!NILP (instr));
3359 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3360   return (network_connection_p (p)
3361           ? get_winsock_stream_waitable (XLSTREAM (instr))
3362           : get_ntpipe_input_stream_waitable (XLSTREAM (instr)));
3363 #else
3364   return get_ntpipe_input_stream_waitable (XLSTREAM (instr));
3365 #endif
3366 }
3367
3368 static void
3369 emacs_mswindows_select_process (Lisp_Process *process)
3370 {
3371   HANDLE hev = get_process_input_waitable (process);
3372
3373   if (!add_waitable_handle (hev))
3374     error ("Too many active processes");
3375
3376 #ifdef HAVE_WIN32_PROCESSES
3377   {
3378     Lisp_Object p;
3379     XSETPROCESS (p, process);
3380     if (!network_connection_p (p))
3381       {
3382         HANDLE hprocess = get_nt_process_handle (process);
3383         if (!add_waitable_handle (hprocess))
3384           {
3385             remove_waitable_handle (hev);
3386             error ("Too many active processes");
3387           }
3388       }
3389   }
3390 #endif
3391 }
3392
3393 static void
3394 emacs_mswindows_unselect_process (Lisp_Process *process)
3395 {
3396   /* Process handle is removed in the event loop as soon
3397      as it is signaled, so don't bother here about it */
3398   HANDLE hev = get_process_input_waitable (process);
3399   remove_waitable_handle (hev);
3400 }
3401 #endif /* HAVE_MSG_SELECT */
3402
3403 static void
3404 emacs_mswindows_select_console (struct console *con)
3405 {
3406 #ifdef HAVE_MSG_SELECT
3407   if (CONSOLE_MSWINDOWS_P (con))
3408     return; /* mswindows consoles are automatically selected */
3409
3410   event_stream_unixoid_select_console (con);
3411 #endif
3412 }
3413
3414 static void
3415 emacs_mswindows_unselect_console (struct console *con)
3416 {
3417 #ifdef HAVE_MSG_SELECT
3418   if (CONSOLE_MSWINDOWS_P (con))
3419     return; /* mswindows consoles are automatically selected */
3420
3421   event_stream_unixoid_unselect_console (con);
3422 #endif
3423 }
3424
3425 static void
3426 emacs_mswindows_quit_p (void)
3427 {
3428   /* Quit cannot happen in modal loop: all program
3429      input is dedicated to Windows. */
3430   if (mswindows_in_modal_loop)
3431     return;
3432
3433   /* Drain windows queue. This sets up number of quit characters in
3434      the queue */
3435   mswindows_drain_windows_queue ();
3436
3437   if (mswindows_quit_chars_count > 0)
3438     {
3439       /* Yes there's a hidden one... Throw it away */
3440       Lisp_Event match_against;
3441       Lisp_Object emacs_event;
3442       int critical_p = 0;
3443
3444       match_against.event_type = key_press_event;
3445       match_against.event.key.modifiers = FAKE_MOD_QUIT;
3446
3447       while (mswindows_quit_chars_count-- > 0)
3448         {
3449           emacs_event = mswindows_cancel_dispatch_event (&match_against);
3450           assert (!NILP (emacs_event));
3451
3452           if (XEVENT(emacs_event)->event.key.modifiers & XEMACS_MOD_SHIFT)
3453             critical_p = 1;
3454
3455           Fdeallocate_event(emacs_event);
3456         }
3457
3458       Vquit_flag = critical_p ? Qcritical : Qt;
3459     }
3460 }
3461
3462 USID
3463 emacs_mswindows_create_stream_pair (void* inhandle, void* outhandle,
3464                                     Lisp_Object* instream,
3465                                     Lisp_Object* outstream,
3466                                     int flags)
3467 {
3468   /* Handles for streams */
3469   HANDLE hin, hout;
3470   /* fds. These just stored along with the streams, and are closed in
3471      delete stream pair method, because we need to handle fake unices
3472      here. */
3473   int fdi, fdo;
3474
3475   /* Decode inhandle and outhandle. Their meaning depends on
3476      the process implementation being used. */
3477 #if defined (HAVE_WIN32_PROCESSES)
3478   /* We're passed in Windows handles. That's what we like most... */
3479   hin = (HANDLE) inhandle;
3480   hout = (HANDLE) outhandle;
3481   fdi = fdo = -1;
3482 #elif defined (HAVE_UNIX_PROCESSES)
3483   /* We are passed UNIX fds. This must be Cygwin.
3484      Fetch os handles */
3485   hin = inhandle >= 0 ? (HANDLE)get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE;
3486   hout = outhandle >= 0 ? (HANDLE)get_osfhandle ((int)outhandle) : INVALID_HANDLE_VALUE;
3487   fdi=(int)inhandle;
3488   fdo=(int)outhandle;
3489 #else
3490 #error "So, WHICH kind of processes do you want?"
3491 #endif
3492
3493   *instream = (hin == INVALID_HANDLE_VALUE
3494                ? Qnil
3495 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
3496                : flags & STREAM_NETWORK_CONNECTION
3497                ? make_winsock_input_stream ((SOCKET)hin, fdi)
3498 #endif
3499                : make_ntpipe_input_stream (hin, fdi));
3500
3501 #ifdef HAVE_WIN32_PROCESSES
3502   *outstream = (hout == INVALID_HANDLE_VALUE
3503                 ? Qnil
3504 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
3505                 : flags & STREAM_NETWORK_CONNECTION
3506                 ? make_winsock_output_stream ((SOCKET)hout, fdo)
3507 #endif
3508                 : make_ntpipe_output_stream (hout, fdo));
3509 #elif defined (HAVE_UNIX_PROCESSES)
3510   *outstream = (fdo >= 0
3511                 ? make_filedesc_output_stream (fdo, 0, -1, LSTR_BLOCKED_OK)
3512                 : Qnil);
3513
3514 #if defined(HAVE_UNIX_PROCESSES) && defined(HAVE_PTYS)
3515   /* FLAGS is process->pty_flag for UNIX_PROCESSES */
3516   if ((flags & STREAM_PTY_FLUSHING) && fdo >= 0)
3517     {
3518       Bufbyte eof_char = get_eof_char (fdo);
3519       int pty_max_bytes = get_pty_max_bytes (fdo);
3520       filedesc_stream_set_pty_flushing (XLSTREAM(*outstream), pty_max_bytes, eof_char);
3521     }
3522 #endif
3523 #endif
3524
3525   return (NILP (*instream)
3526           ? USID_ERROR
3527 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3528           : flags & STREAM_NETWORK_CONNECTION
3529           ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream)))
3530 #endif
3531           : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (*instream))));
3532 }
3533
3534 USID
3535 emacs_mswindows_delete_stream_pair (Lisp_Object instream,
3536                                     Lisp_Object outstream)
3537 {
3538   /* Oh nothing special here for Win32 at all */
3539 #if defined (HAVE_UNIX_PROCESSES)
3540   int in = (NILP(instream)
3541             ? -1
3542 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3543             : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
3544             ? get_winsock_stream_param (XLSTREAM (instream))
3545 #endif
3546             : get_ntpipe_input_stream_param (XLSTREAM (instream)));
3547   int out = (NILP(outstream) ? -1
3548              : filedesc_stream_fd (XLSTREAM (outstream)));
3549
3550   if (in >= 0)
3551     close (in);
3552   if (out != in && out >= 0)
3553     close (out);
3554 #endif
3555
3556   return (NILP (instream)
3557           ? USID_DONTHASH
3558 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3559           : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
3560           ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream)))
3561 #endif
3562           : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream))));
3563 }
3564
3565 static int
3566 emacs_mswindows_current_event_timestamp (struct console *c)
3567 {
3568   return GetTickCount ();
3569 }
3570
3571 #ifndef HAVE_X_WINDOWS
3572 /* This is called from GC when a process object is about to be freed.
3573    If we've still got pointers to it in this file, we're gonna lose hard.
3574  */
3575 void
3576 debug_process_finalization (Lisp_Process *p)
3577 {
3578 #if 0 /* #### */
3579   Lisp_Object instr, outstr;
3580
3581   get_process_streams (p, &instr, &outstr);
3582   /* if it still has fds, then it hasn't been killed yet. */
3583   assert (NILP(instr));
3584   assert (NILP(outstr));
3585
3586   /* #### More checks here */
3587 #endif
3588 }
3589 #endif
3590
3591 /************************************************************************/
3592 /*                            initialization                            */
3593 /************************************************************************/
3594
3595 void
3596 reinit_vars_of_event_mswindows (void)
3597 {
3598   mswindows_in_modal_loop = 0;
3599   mswindows_pending_timers_count = 0;
3600
3601   mswindows_event_stream = xnew (struct event_stream);
3602
3603   mswindows_event_stream->event_pending_p       = emacs_mswindows_event_pending_p;
3604   mswindows_event_stream->force_event_pending = 0;
3605   mswindows_event_stream->next_event_cb         = emacs_mswindows_next_event;
3606   mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event;
3607   mswindows_event_stream->add_timeout_cb        = emacs_mswindows_add_timeout;
3608   mswindows_event_stream->remove_timeout_cb     = emacs_mswindows_remove_timeout;
3609   mswindows_event_stream->quit_p_cb             = emacs_mswindows_quit_p;
3610   mswindows_event_stream->select_console_cb     = emacs_mswindows_select_console;
3611   mswindows_event_stream->unselect_console_cb   = emacs_mswindows_unselect_console;
3612 #ifdef HAVE_MSG_SELECT
3613   mswindows_event_stream->select_process_cb     =
3614     (void (*)(Lisp_Process*))event_stream_unixoid_select_process;
3615   mswindows_event_stream->unselect_process_cb   =
3616     (void (*)(Lisp_Process*))event_stream_unixoid_unselect_process;
3617   mswindows_event_stream->create_stream_pair_cb = event_stream_unixoid_create_stream_pair;
3618   mswindows_event_stream->delete_stream_pair_cb = event_stream_unixoid_delete_stream_pair;
3619 #else
3620   mswindows_event_stream->select_process_cb     = emacs_mswindows_select_process;
3621   mswindows_event_stream->unselect_process_cb   = emacs_mswindows_unselect_process;
3622   mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair;
3623   mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair;
3624 #endif
3625   mswindows_event_stream->current_event_timestamp_cb =
3626     emacs_mswindows_current_event_timestamp;
3627 }
3628
3629 void
3630 vars_of_event_mswindows (void)
3631 {
3632   reinit_vars_of_event_mswindows ();
3633
3634   mswindows_u_dispatch_event_queue = Qnil;
3635   staticpro (&mswindows_u_dispatch_event_queue);
3636   mswindows_u_dispatch_event_queue_tail = Qnil;
3637   dump_add_root_object (&mswindows_u_dispatch_event_queue_tail);
3638
3639   mswindows_s_dispatch_event_queue = Qnil;
3640   staticpro (&mswindows_s_dispatch_event_queue);
3641   mswindows_s_dispatch_event_queue_tail = Qnil;
3642   dump_add_root_object (&mswindows_s_dispatch_event_queue_tail);
3643
3644   mswindows_error_caught_in_modal_loop = Qnil;
3645   staticpro (&mswindows_error_caught_in_modal_loop);
3646
3647
3648 #ifdef DEBUG_XEMACS
3649   DEFVAR_INT ("debug-mswindows-events", &debug_mswindows_events /*
3650 If non-zero, display debug information about Windows events that XEmacs sees.
3651 Information is displayed in a console window.  Currently defined values are:
3652
3653 1 == non-verbose output
3654 2 == verbose output
3655
3656 #### Unfortunately, not yet implemented.
3657 */ );
3658   debug_mswindows_events = 0;
3659 #endif
3660
3661   DEFVAR_BOOL ("mswindows-alt-by-itself-activates-menu",
3662                &mswindows_alt_by_itself_activates_menu /*
3663 *Controls whether pressing and releasing the Alt key activates the menubar.
3664 This applies only if no intervening key was pressed.  See also
3665 `menu-accelerator-enabled', which is probably the behavior you actually want.
3666 Default is t.
3667 */ );
3668
3669   DEFVAR_BOOL ("mswindows-dynamic-frame-resize",
3670                &mswindows_dynamic_frame_resize /*
3671 *Controls redrawing frame contents during mouse-drag or keyboard resize
3672 operation. When non-nil, the frame is redrawn while being resized. When
3673 nil, frame is not redrawn, and exposed areas are filled with default
3674 MDI application background color. Note that this option only has effect
3675 if "Show window contents while dragging" is on in system Display/Plus!
3676 settings.
3677 Default is t on fast machines, nil on slow.
3678 */ );
3679
3680   DEFVAR_INT ("mswindows-mouse-button-tolerance",
3681               &mswindows_mouse_button_tolerance /*
3682 *Analogue of double click interval for faking middle mouse events.
3683 The value is the minimum time in milliseconds that must elapse between
3684 left/right button down events before they are considered distinct events.
3685 If both mouse buttons are depressed within this interval, a middle mouse
3686 button down event is generated instead.
3687 If negative or zero, currently set system default is used instead.
3688 */ );
3689
3690   DEFVAR_INT ("mswindows-num-mouse-buttons", &mswindows_num_mouse_buttons /*
3691 Number of physical mouse buttons.
3692 */ );
3693
3694   DEFVAR_INT ("mswindows-mouse-button-max-skew-x",
3695               &mswindows_mouse_button_max_skew_x /*
3696 *Maximum horizontal distance in pixels between points in which left and
3697 right button clicks occurred for them to be translated into single
3698 middle button event. Clicks must occur in time not longer than defined
3699 by the variable `mswindows-mouse-button-tolerance'.
3700 If negative or zero, currently set system default is used instead.
3701 */ );
3702
3703   DEFVAR_INT ("mswindows-mouse-button-max-skew-y",
3704               &mswindows_mouse_button_max_skew_y /*
3705 *Maximum vertical distance in pixels between points in which left and
3706 right button clicks occurred for them to be translated into single
3707 middle button event. Clicks must occur in time not longer than defined
3708 by the variable `mswindows-mouse-button-tolerance'.
3709 If negative or zero, currently set system default is used instead.
3710 */ );
3711
3712   mswindows_mouse_button_max_skew_x = 0;
3713   mswindows_mouse_button_max_skew_y = 0;
3714   mswindows_mouse_button_tolerance = 0;
3715   mswindows_alt_by_itself_activates_menu = 1;
3716 }
3717
3718 void
3719 syms_of_event_mswindows (void)
3720 {
3721 }
3722
3723 void
3724 lstream_type_create_mswindows_selectable (void)
3725 {
3726   init_slurp_stream ();
3727   init_shove_stream ();
3728 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3729   init_winsock_stream ();
3730 #endif
3731 }
3732
3733 void
3734 init_event_mswindows_late (void)
3735 {
3736 #ifdef HAVE_MSG_SELECT
3737   windows_fd = open("/dev/windows", O_RDONLY | O_NONBLOCK, 0);
3738   assert (windows_fd>=0);
3739   FD_SET (windows_fd, &input_wait_mask);
3740   FD_ZERO(&zero_mask);
3741 #endif
3742
3743   event_stream = mswindows_event_stream;
3744
3745   mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE);
3746   mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);
3747 }