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