1350d26f0317c9deee86990a1177d4073540289b
[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),
2749                                                keys, delta,
2750                                                MAKEPOINTS (lParam)))
2751           {
2752             GCPRO2 (emacs_event, fobj);
2753             mswindows_pump_outstanding_events ();       /* Can GC */
2754             UNGCPRO;
2755           }
2756         else
2757           goto defproc;
2758         break;
2759       }
2760 #endif
2761
2762 #ifdef HAVE_MENUBARS
2763     case WM_INITMENU:
2764       if (UNBOUNDP (mswindows_handle_wm_initmenu (
2765                                                   (HMENU) wParam,
2766                                                   XFRAME (mswindows_find_frame (hwnd)))))
2767         SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2768       break;
2769
2770     case WM_INITMENUPOPUP:
2771       if (!HIWORD(lParam))
2772         {
2773           if (UNBOUNDP (mswindows_handle_wm_initmenupopup (
2774                                                            (HMENU) wParam,
2775                                                            XFRAME (mswindows_find_frame (hwnd)))))
2776             SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2777         }
2778       break;
2779
2780 #endif /* HAVE_MENUBARS */
2781
2782     case WM_COMMAND:
2783       {
2784         WORD id = LOWORD (wParam);
2785         WORD nid = HIWORD (wParam);
2786         HWND cid = (HWND)lParam;
2787         frame = XFRAME (mswindows_find_frame (hwnd));
2788
2789 #ifdef HAVE_TOOLBARS
2790         if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id)))
2791           break;
2792 #endif
2793         /* widgets in a buffer only eval a callback for suitable events.*/
2794         switch (nid)
2795           {
2796           case BN_CLICKED:
2797           case EN_CHANGE:
2798           case CBN_EDITCHANGE:
2799           case CBN_SELCHANGE:
2800             if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id)))
2801               return 0;
2802           }
2803         /* menubars always must come last since the hashtables do not
2804            always exist*/
2805 #ifdef HAVE_MENUBARS
2806         if (!NILP (mswindows_handle_wm_command (frame, id)))
2807           break;
2808 #endif
2809
2810         return DefWindowProc (hwnd, message_, wParam, lParam);
2811         /* Bite me - a spurious command. This used to not be able to
2812            happen but with the introduction of widgets its now
2813            possible. */
2814       }
2815       break;
2816
2817     case WM_CTLCOLORBTN:
2818     case WM_CTLCOLORLISTBOX:
2819     case WM_CTLCOLOREDIT:
2820     case WM_CTLCOLORSTATIC:
2821     case WM_CTLCOLORSCROLLBAR:
2822       {
2823         /* if we get an opportunity to paint a widget then do so if
2824            there is an appropriate face */
2825         HWND crtlwnd = (HWND)lParam;
2826         LONG ii = GetWindowLong (crtlwnd, GWL_USERDATA);
2827         if (ii)
2828           {
2829             Lisp_Object image_instance;
2830             VOID_TO_LISP (image_instance, ii);
2831             if (IMAGE_INSTANCEP (image_instance)
2832                 &&
2833                 IMAGE_INSTANCE_TYPE_P (image_instance, IMAGE_WIDGET))
2834               {
2835                 /* set colors for the buttons */
2836                 HDC hdc = (HDC)wParam;
2837                 if (last_widget_brushed != ii)
2838                   {
2839                     if (widget_brush)
2840                       DeleteObject (widget_brush);
2841                     widget_brush = CreateSolidBrush
2842                       (COLOR_INSTANCE_MSWINDOWS_COLOR
2843                        (XCOLOR_INSTANCE
2844                         (FACE_BACKGROUND
2845                          (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2846                           XIMAGE_INSTANCE_FRAME (image_instance)))));
2847                   }
2848                 last_widget_brushed = ii;
2849                 SetTextColor
2850                   (hdc,
2851                    COLOR_INSTANCE_MSWINDOWS_COLOR
2852                    (XCOLOR_INSTANCE
2853                     (FACE_FOREGROUND
2854                      (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2855                       XIMAGE_INSTANCE_FRAME (image_instance)))));
2856                 SetBkMode (hdc, OPAQUE);
2857                 SetBkColor
2858                   (hdc,
2859                    COLOR_INSTANCE_MSWINDOWS_COLOR
2860                    (XCOLOR_INSTANCE
2861                     (FACE_BACKGROUND
2862                      (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2863                       XIMAGE_INSTANCE_FRAME (image_instance)))));
2864                 return (LRESULT)widget_brush;
2865               }
2866           }
2867       }
2868       goto defproc;
2869
2870 #ifdef HAVE_DRAGNDROP
2871     case WM_DROPFILES:  /* implementation ripped-off from event-Xt.c */
2872       {
2873         UINT filecount, i, len;
2874         POINT point;
2875         char* filename;
2876         char* fname;
2877
2878         Lisp_Object l_dndlist = Qnil, l_item = Qnil;
2879         struct gcpro gcpro1, gcpro2, gcpro3;
2880
2881         emacs_event = Fmake_event (Qnil, Qnil);
2882         event = XEVENT(emacs_event);
2883
2884         GCPRO3 (emacs_event, l_dndlist, l_item);
2885
2886         if (!DragQueryPoint ((HDROP) wParam, &point))
2887           point.x = point.y = -1;               /* outside client area */
2888
2889         event->event_type = misc_user_event;
2890         event->channel = mswindows_find_frame(hwnd);
2891         event->timestamp = GetMessageTime();
2892         event->event.misc.button = 1;           /* #### Should try harder */
2893         event->event.misc.modifiers = mswindows_modifier_state (NULL,
2894                                                                 (DWORD) -1, 0);
2895         event->event.misc.x = point.x;
2896         event->event.misc.y = point.y;
2897         event->event.misc.function = Qdragdrop_drop_dispatch;
2898
2899         filecount = DragQueryFile ((HDROP) wParam, 0xffffffff, NULL, 0);
2900         for (i=0; i<filecount; i++)
2901           {
2902             len = DragQueryFile ((HDROP) wParam, i, NULL, 0);
2903             /* The URLs that we make here aren't correct according to section
2904              * 3.10 of rfc1738 because they're missing the //<host>/ part and
2905              * because they may contain reserved characters. But that's OK -
2906              * they just need to be good enough to keep dragdrop.el happy. */
2907             fname = (char *)xmalloc (len+1);
2908             DragQueryFile ((HANDLE) wParam, i, fname, len+1);
2909
2910             /* May be a shell link aka "shortcut" - replace fname if so */
2911 #if !(defined(CYGWIN) || defined(MINGW))
2912             /* cygwin doesn't define this COM stuff */
2913             if (!stricmp (fname + strlen (fname) - 4, ".LNK"))
2914               {
2915                 IShellLink* psl;
2916
2917                 if (CoCreateInstance (&CLSID_ShellLink, NULL,
2918                                       CLSCTX_INPROC_SERVER, &IID_IShellLink, &psl) == S_OK)
2919                   {
2920                     IPersistFile* ppf;
2921
2922                     if (psl->lpVtbl->QueryInterface (psl, &IID_IPersistFile,
2923                                                      &ppf) == S_OK)
2924                       {
2925                         WORD wsz[MAX_PATH];
2926                         WIN32_FIND_DATA wfd;
2927                         LPSTR resolved = (char *) xmalloc (MAX_PATH+1);
2928
2929                         MultiByteToWideChar (CP_ACP,0, fname, -1, wsz, MAX_PATH);
2930
2931                         if ((ppf->lpVtbl->Load (ppf, wsz, STGM_READ) == S_OK) &&
2932                             (psl->lpVtbl->GetPath (psl, resolved, MAX_PATH,
2933                                                    &wfd, 0)==S_OK))
2934                           {
2935                             xfree (fname);
2936                             fname = resolved;
2937                             len = strlen (fname);
2938                           }
2939
2940                         ppf->lpVtbl->Release (ppf);
2941                       }
2942
2943                     psl->lpVtbl->Release (psl);
2944                   }
2945               }
2946 #endif
2947
2948 #ifdef CYGWIN
2949             filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5);
2950             strcpy (filename, "file:");
2951             cygwin32_win32_to_posix_path_list (fname, filename+5);
2952 #else
2953             filename = (char *)xmalloc (len+6);
2954             strcat (strcpy (filename, "file:"), fname);
2955             dostounix_filename (filename+5);
2956 #endif
2957             xfree (fname);
2958             l_item = make_string (filename, strlen (filename));
2959             l_dndlist = Fcons (l_item, l_dndlist);
2960             xfree (filename);
2961           }
2962         DragFinish ((HDROP) wParam);
2963
2964         event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist);
2965         mswindows_enqueue_dispatch_event (emacs_event);
2966         UNGCPRO;
2967       }
2968       break;
2969 #endif
2970
2971     defproc:
2972     default:
2973       return DefWindowProc (hwnd, message_, wParam, lParam);
2974     }
2975   return (0);
2976 }
2977
2978
2979 /************************************************************************/
2980 /*      keyboard, mouse & other helpers for the windows procedure       */
2981 /************************************************************************/
2982 static void
2983 mswindows_set_chord_timer (HWND hwnd)
2984 {
2985   int interval;
2986
2987   /* We get one third half system double click threshold */
2988   if (mswindows_mouse_button_tolerance <= 0)
2989     interval = GetDoubleClickTime () / 3;
2990   else
2991     interval = mswindows_mouse_button_tolerance;
2992
2993   SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0);
2994 }
2995
2996 static int
2997 mswindows_button2_near_enough (POINTS p1, POINTS p2)
2998 {
2999   int dx, dy;
3000   if (mswindows_mouse_button_max_skew_x <= 0)
3001     dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2;
3002   else
3003     dx = mswindows_mouse_button_max_skew_x;
3004
3005   if (mswindows_mouse_button_max_skew_y <= 0)
3006     dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2;
3007   else
3008     dy = mswindows_mouse_button_max_skew_y;
3009
3010   return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy;
3011 }
3012
3013 static int
3014 mswindows_current_layout_has_AltGr (void)
3015 {
3016   /* This simple caching mechanism saves 10% of CPU
3017      time when a key typed at autorepeat rate of 30 cps! */
3018   static HKL last_hkl = 0;
3019   static int last_hkl_has_AltGr;
3020   HKL current_hkl = (HKL) -1;
3021
3022   if (xGetKeyboardLayout) /* not in NT 3.5 */
3023     current_hkl = xGetKeyboardLayout (0);
3024   if (current_hkl != last_hkl)
3025     {
3026       TCHAR c;
3027       last_hkl_has_AltGr = 0;
3028       /* In this loop, we query whether a character requires
3029          AltGr to be down to generate it. If at least such one
3030          found, this means that the layout does regard AltGr */
3031       for (c = ' '; c <= 0xFFU && c != 0 && !last_hkl_has_AltGr; ++c)
3032         if (HIBYTE (VkKeyScan (c)) == 6)
3033           last_hkl_has_AltGr = 1;
3034       last_hkl = current_hkl;
3035     }
3036   return last_hkl_has_AltGr;
3037 }
3038
3039
3040 /* Returns the state of the modifier keys in the format expected by the
3041  * Lisp_Event key_data, button_data and motion_data modifiers member */
3042 static int
3043 mswindows_modifier_state (BYTE* keymap, DWORD fwKeys, int has_AltGr)
3044 {
3045   int mods = 0;
3046   int keys_is_real = 0;
3047   BYTE keymap2[256];
3048
3049   if (fwKeys == (DWORD) -1)
3050     fwKeys = mswindows_last_mouse_button_state;
3051   else
3052     {
3053       keys_is_real = 1;
3054       mswindows_last_mouse_button_state = fwKeys;
3055     }
3056
3057   if (keymap == NULL)
3058     {
3059       keymap = keymap2;
3060       GetKeyboardState (keymap);
3061       has_AltGr = mswindows_current_layout_has_AltGr ();
3062     }
3063
3064   /* #### should look at fwKeys for MK_CONTROL.  I don't understand how
3065      AltGr works. */
3066   if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80))
3067     {
3068       mods |= (keymap [VK_LMENU] & 0x80) ? XEMACS_MOD_META : 0;
3069       mods |= (keymap [VK_RCONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
3070     }
3071   else
3072     {
3073       mods |= (keymap [VK_MENU] & 0x80) ? XEMACS_MOD_META : 0;
3074       mods |= (keymap [VK_CONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
3075     }
3076
3077   mods |= (keys_is_real ? fwKeys & MK_SHIFT : (keymap [VK_SHIFT] & 0x80))
3078     ? XEMACS_MOD_SHIFT : 0;
3079   mods |= fwKeys & MK_LBUTTON ? XEMACS_MOD_BUTTON1 : 0;
3080   mods |= fwKeys & MK_MBUTTON ? XEMACS_MOD_BUTTON2 : 0;
3081   mods |= fwKeys & MK_RBUTTON ? XEMACS_MOD_BUTTON3 : 0;
3082
3083   return mods;
3084 }
3085
3086 /*
3087  * Translate a mswindows virtual key to a keysym.
3088  * Only returns non-Qnil for keys that don't generate WM_CHAR messages
3089  * or whose ASCII codes (like space) xemacs doesn't like.
3090  */
3091 Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
3092                                            int extendedp)
3093 {
3094   if (extendedp)        /* Keys not present on a 82 key keyboard */
3095     {
3096       switch (mswindows_key)
3097         {
3098         case VK_CANCEL:         return KEYSYM ("pause");
3099         case VK_RETURN:         return KEYSYM ("kp-enter");
3100         case VK_PRIOR:          return KEYSYM ("prior");
3101         case VK_NEXT:           return KEYSYM ("next");
3102         case VK_END:            return KEYSYM ("end");
3103         case VK_HOME:           return KEYSYM ("home");
3104         case VK_LEFT:           return KEYSYM ("left");
3105         case VK_UP:             return KEYSYM ("up");
3106         case VK_RIGHT:          return KEYSYM ("right");
3107         case VK_DOWN:           return KEYSYM ("down");
3108         case VK_INSERT:         return KEYSYM ("insert");
3109         case VK_DELETE:         return QKdelete;
3110 #if 0   /* FSF Emacs allows these to return configurable syms/mods */
3111         case VK_LWIN            return KEYSYM ("");
3112         case VK_RWIN            return KEYSYM ("");
3113 #endif
3114         case VK_APPS:           return KEYSYM ("menu");
3115         }
3116     }
3117   else
3118     {
3119       switch (mswindows_key)
3120         {
3121         case VK_BACK:           return QKbackspace;
3122         case VK_TAB:            return QKtab;
3123         case '\n':              return QKlinefeed;
3124         case VK_CLEAR:          return KEYSYM ("clear");
3125         case VK_RETURN:         return QKreturn;
3126         case VK_PAUSE:          return KEYSYM ("pause");
3127         case VK_ESCAPE:         return QKescape;
3128         case VK_SPACE:          return QKspace;
3129         case VK_PRIOR:          return KEYSYM ("kp-prior");
3130         case VK_NEXT:           return KEYSYM ("kp-next");
3131         case VK_END:            return KEYSYM ("kp-end");
3132         case VK_HOME:           return KEYSYM ("kp-home");
3133         case VK_LEFT:           return KEYSYM ("kp-left");
3134         case VK_UP:             return KEYSYM ("kp-up");
3135         case VK_RIGHT:          return KEYSYM ("kp-right");
3136         case VK_DOWN:           return KEYSYM ("kp-down");
3137         case VK_SELECT:         return KEYSYM ("select");
3138         case VK_PRINT:          return KEYSYM ("print");
3139         case VK_EXECUTE:        return KEYSYM ("execute");
3140         case VK_SNAPSHOT:       return KEYSYM ("print");
3141         case VK_INSERT:         return KEYSYM ("kp-insert");
3142         case VK_DELETE:         return KEYSYM ("kp-delete");
3143         case VK_HELP:           return KEYSYM ("help");
3144         case VK_NUMPAD0:        return KEYSYM ("kp-0");
3145         case VK_NUMPAD1:        return KEYSYM ("kp-1");
3146         case VK_NUMPAD2:        return KEYSYM ("kp-2");
3147         case VK_NUMPAD3:        return KEYSYM ("kp-3");
3148         case VK_NUMPAD4:        return KEYSYM ("kp-4");
3149         case VK_NUMPAD5:        return KEYSYM ("kp-5");
3150         case VK_NUMPAD6:        return KEYSYM ("kp-6");
3151         case VK_NUMPAD7:        return KEYSYM ("kp-7");
3152         case VK_NUMPAD8:        return KEYSYM ("kp-8");
3153         case VK_NUMPAD9:        return KEYSYM ("kp-9");
3154         case VK_MULTIPLY:       return KEYSYM ("kp-multiply");
3155         case VK_ADD:            return KEYSYM ("kp-add");
3156         case VK_SEPARATOR:      return KEYSYM ("kp-separator");
3157         case VK_SUBTRACT:       return KEYSYM ("kp-subtract");
3158         case VK_DECIMAL:        return KEYSYM ("kp-decimal");
3159         case VK_DIVIDE:         return KEYSYM ("kp-divide");
3160         case VK_F1:             return KEYSYM ("f1");
3161         case VK_F2:             return KEYSYM ("f2");
3162         case VK_F3:             return KEYSYM ("f3");
3163         case VK_F4:             return KEYSYM ("f4");
3164         case VK_F5:             return KEYSYM ("f5");
3165         case VK_F6:             return KEYSYM ("f6");
3166         case VK_F7:             return KEYSYM ("f7");
3167         case VK_F8:             return KEYSYM ("f8");
3168         case VK_F9:             return KEYSYM ("f9");
3169         case VK_F10:            return KEYSYM ("f10");
3170         case VK_F11:            return KEYSYM ("f11");
3171         case VK_F12:            return KEYSYM ("f12");
3172         case VK_F13:            return KEYSYM ("f13");
3173         case VK_F14:            return KEYSYM ("f14");
3174         case VK_F15:            return KEYSYM ("f15");
3175         case VK_F16:            return KEYSYM ("f16");
3176         case VK_F17:            return KEYSYM ("f17");
3177         case VK_F18:            return KEYSYM ("f18");
3178         case VK_F19:            return KEYSYM ("f19");
3179         case VK_F20:            return KEYSYM ("f20");
3180         case VK_F21:            return KEYSYM ("f21");
3181         case VK_F22:            return KEYSYM ("f22");
3182         case VK_F23:            return KEYSYM ("f23");
3183         case VK_F24:            return KEYSYM ("f24");
3184         }
3185     }
3186   return Qnil;
3187 }
3188
3189 /*
3190  * Find the console that matches the supplied mswindows window handle
3191  */
3192 Lisp_Object
3193 mswindows_find_console (HWND hwnd)
3194 {
3195   /* We only support one console */
3196   return XCAR (Vconsole_list);
3197 }
3198
3199 /*
3200  * Find the frame that matches the supplied mswindows window handle
3201  */
3202 static Lisp_Object
3203 mswindows_find_frame (HWND hwnd)
3204 {
3205   LONG l = GetWindowLong (hwnd, XWL_FRAMEOBJ);
3206   Lisp_Object f;
3207   if (l == 0)
3208     {
3209       /* We are in progress of frame creation. Return the frame
3210          being created, as it still not remembered in the window
3211          extra storage. */
3212       assert (!NILP (Vmswindows_frame_being_created));
3213       return Vmswindows_frame_being_created;
3214     }
3215   VOID_TO_LISP (f, l);
3216   return f;
3217 }
3218
3219 \f
3220 /************************************************************************/
3221 /*                            methods                                   */
3222 /************************************************************************/
3223
3224 static int
3225 emacs_mswindows_add_timeout (EMACS_TIME thyme)
3226 {
3227   int milliseconds;
3228   EMACS_TIME current_time;
3229   EMACS_GET_TIME (current_time);
3230   EMACS_SUB_TIME (thyme, thyme, current_time);
3231   milliseconds = EMACS_SECS (thyme) * 1000 +
3232     (EMACS_USECS (thyme) + 500) / 1000;
3233   if (milliseconds < 1)
3234     milliseconds = 1;
3235   ++mswindows_pending_timers_count;
3236   return SetTimer (NULL, 0, milliseconds,
3237                    (TIMERPROC) mswindows_wm_timer_callback);
3238 }
3239
3240 static void
3241 emacs_mswindows_remove_timeout (int id)
3242 {
3243   Lisp_Event match_against;
3244   Lisp_Object emacs_event;
3245
3246   if (KillTimer (NULL, id))
3247     --mswindows_pending_timers_count;
3248
3249   /* If there is a dispatch event generated by this
3250      timeout in the queue, we have to remove it too. */
3251   match_against.event_type = timeout_event;
3252   match_against.event.timeout.interval_id = id;
3253   emacs_event = mswindows_cancel_dispatch_event (&match_against);
3254   if (!NILP (emacs_event))
3255     Fdeallocate_event(emacs_event);
3256 }
3257
3258 /* If `user_p' is false, then return whether there are any win32, timeout,
3259  * or subprocess events pending (that is, whether
3260  * emacs_mswindows_next_event() would return immediately without blocking).
3261  *
3262  * if `user_p' is true, then return whether there are any *user generated*
3263  * events available (that is, whether there are keyboard or mouse-click
3264  * events ready to be read).  This also implies that
3265  * emacs_mswindows_next_event() would not block.
3266  */
3267 static int
3268 emacs_mswindows_event_pending_p (int user_p)
3269 {
3270   mswindows_need_event (0);
3271   return (!NILP (mswindows_u_dispatch_event_queue)
3272           || (!user_p && !NILP (mswindows_s_dispatch_event_queue)));
3273 }
3274
3275 /*
3276  * Return the next event
3277  */
3278 static void
3279 emacs_mswindows_next_event (Lisp_Event *emacs_event)
3280 {
3281   Lisp_Object event, event2;
3282
3283   mswindows_need_event (1);
3284
3285   event = mswindows_dequeue_dispatch_event ();
3286   XSETEVENT (event2, emacs_event);
3287   Fcopy_event (event, event2);
3288   Fdeallocate_event (event);
3289 }
3290
3291 /*
3292  * Handle a magic event off the dispatch queue.
3293  */
3294 static void
3295 emacs_mswindows_handle_magic_event (Lisp_Event *emacs_event)
3296 {
3297   switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event))
3298     {
3299     case XM_BUMPQUEUE:
3300       break;
3301
3302     case WM_PAINT:
3303       {
3304         struct frame *f = XFRAME (EVENT_CHANNEL (emacs_event));
3305         mswindows_handle_paint (f);
3306         (FRAME_MSWINDOWS_DATA (f))->paint_pending = 0;
3307       }
3308       break;
3309
3310     case WM_SETFOCUS:
3311     case WM_KILLFOCUS:
3312       {
3313         Lisp_Object frame = EVENT_CHANNEL (emacs_event);
3314         struct frame *f = XFRAME (frame);
3315         int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS);
3316         Lisp_Object conser;
3317         struct gcpro gcpro1;
3318
3319         /* On focus change, clear all memory of sticky modifiers
3320            to avoid non-intuitive behavior. */
3321         clear_sticky_modifiers ();
3322
3323         conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil));
3324         GCPRO1 (conser);
3325         emacs_handle_focus_change_preliminary (conser);
3326         /* Under X the stuff up to here is done in the X event handler.
3327            I Don't know why */
3328         emacs_handle_focus_change_final (conser);
3329         UNGCPRO;
3330
3331       }
3332       break;
3333
3334     case XM_MAPFRAME:
3335     case XM_UNMAPFRAME:
3336       {
3337         Lisp_Object frame = EVENT_CHANNEL (emacs_event);
3338         va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)
3339                                == XM_MAPFRAME ?
3340                                Qmap_frame_hook : Qunmap_frame_hook,
3341                                1, frame);
3342       }
3343       break;
3344
3345       /* #### What about Enter & Leave */
3346 #if 0
3347       va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook :
3348                              Qmouse_leave_frame_hook, 1, frame);
3349 #endif
3350
3351     default:
3352       assert(0);
3353     }
3354 }
3355
3356 #ifndef HAVE_MSG_SELECT
3357 static HANDLE
3358 get_process_input_waitable (Lisp_Process *process)
3359 {
3360   Lisp_Object instr, outstr, p;
3361   XSETPROCESS (p, process);
3362   get_process_streams (process, &instr, &outstr);
3363   assert (!NILP (instr));
3364 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3365   return (network_connection_p (p)
3366           ? get_winsock_stream_waitable (XLSTREAM (instr))
3367           : get_ntpipe_input_stream_waitable (XLSTREAM (instr)));
3368 #else
3369   return get_ntpipe_input_stream_waitable (XLSTREAM (instr));
3370 #endif
3371 }
3372
3373 static void
3374 emacs_mswindows_select_process (Lisp_Process *process)
3375 {
3376   HANDLE hev = get_process_input_waitable (process);
3377
3378   if (!add_waitable_handle (hev))
3379     error ("Too many active processes");
3380
3381 #ifdef HAVE_WIN32_PROCESSES
3382   {
3383     Lisp_Object p;
3384     XSETPROCESS (p, process);
3385     if (!network_connection_p (p))
3386       {
3387         HANDLE hprocess = get_nt_process_handle (process);
3388         if (!add_waitable_handle (hprocess))
3389           {
3390             remove_waitable_handle (hev);
3391             error ("Too many active processes");
3392           }
3393       }
3394   }
3395 #endif
3396 }
3397
3398 static void
3399 emacs_mswindows_unselect_process (Lisp_Process *process)
3400 {
3401   /* Process handle is removed in the event loop as soon
3402      as it is signaled, so don't bother here about it */
3403   HANDLE hev = get_process_input_waitable (process);
3404   remove_waitable_handle (hev);
3405 }
3406 #endif /* HAVE_MSG_SELECT */
3407
3408 static void
3409 emacs_mswindows_select_console (struct console *con)
3410 {
3411 #ifdef HAVE_MSG_SELECT
3412   if (CONSOLE_MSWINDOWS_P (con))
3413     return; /* mswindows consoles are automatically selected */
3414
3415   event_stream_unixoid_select_console (con);
3416 #endif
3417 }
3418
3419 static void
3420 emacs_mswindows_unselect_console (struct console *con)
3421 {
3422 #ifdef HAVE_MSG_SELECT
3423   if (CONSOLE_MSWINDOWS_P (con))
3424     return; /* mswindows consoles are automatically selected */
3425
3426   event_stream_unixoid_unselect_console (con);
3427 #endif
3428 }
3429
3430 static void
3431 emacs_mswindows_quit_p (void)
3432 {
3433   /* Quit cannot happen in modal loop: all program
3434      input is dedicated to Windows. */
3435   if (mswindows_in_modal_loop)
3436     return;
3437
3438   /* Drain windows queue. This sets up number of quit characters in
3439      the queue */
3440   mswindows_drain_windows_queue ();
3441
3442   if (mswindows_quit_chars_count > 0)
3443     {
3444       /* Yes there's a hidden one... Throw it away */
3445       Lisp_Event match_against;
3446       Lisp_Object emacs_event;
3447       int critical_p = 0;
3448
3449       match_against.event_type = key_press_event;
3450       match_against.event.key.modifiers = FAKE_MOD_QUIT;
3451
3452       while (mswindows_quit_chars_count-- > 0)
3453         {
3454           emacs_event = mswindows_cancel_dispatch_event (&match_against);
3455           assert (!NILP (emacs_event));
3456
3457           if (XEVENT(emacs_event)->event.key.modifiers & XEMACS_MOD_SHIFT)
3458             critical_p = 1;
3459
3460           Fdeallocate_event(emacs_event);
3461         }
3462
3463       Vquit_flag = critical_p ? Qcritical : Qt;
3464     }
3465 }
3466
3467 USID
3468 emacs_mswindows_create_stream_pair (void* inhandle, void* outhandle,
3469                                     Lisp_Object* instream,
3470                                     Lisp_Object* outstream,
3471                                     int flags)
3472 {
3473   /* Handles for streams */
3474   HANDLE hin, hout;
3475   /* fds. These just stored along with the streams, and are closed in
3476      delete stream pair method, because we need to handle fake unices
3477      here. */
3478   int fdi, fdo;
3479
3480   /* Decode inhandle and outhandle. Their meaning depends on
3481      the process implementation being used. */
3482 #if defined (HAVE_WIN32_PROCESSES)
3483   /* We're passed in Windows handles. That's what we like most... */
3484   hin = (HANDLE) inhandle;
3485   hout = (HANDLE) outhandle;
3486   fdi = fdo = -1;
3487 #elif defined (HAVE_UNIX_PROCESSES)
3488   /* We are passed UNIX fds. This must be Cygwin.
3489      Fetch os handles */
3490   hin = inhandle >= 0 ? (HANDLE)get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE;
3491   hout = outhandle >= 0 ? (HANDLE)get_osfhandle ((int)outhandle) : INVALID_HANDLE_VALUE;
3492   fdi=(int)inhandle;
3493   fdo=(int)outhandle;
3494 #else
3495 #error "So, WHICH kind of processes do you want?"
3496 #endif
3497
3498   *instream = (hin == INVALID_HANDLE_VALUE
3499                ? Qnil
3500 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
3501                : flags & STREAM_NETWORK_CONNECTION
3502                ? make_winsock_input_stream ((SOCKET)hin, fdi)
3503 #endif
3504                : make_ntpipe_input_stream (hin, fdi));
3505
3506 #ifdef HAVE_WIN32_PROCESSES
3507   *outstream = (hout == INVALID_HANDLE_VALUE
3508                 ? Qnil
3509 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
3510                 : flags & STREAM_NETWORK_CONNECTION
3511                 ? make_winsock_output_stream ((SOCKET)hout, fdo)
3512 #endif
3513                 : make_ntpipe_output_stream (hout, fdo));
3514 #elif defined (HAVE_UNIX_PROCESSES)
3515   *outstream = (fdo >= 0
3516                 ? make_filedesc_output_stream (fdo, 0, -1, LSTR_BLOCKED_OK)
3517                 : Qnil);
3518
3519 #if defined(HAVE_UNIX_PROCESSES) && defined(HAVE_PTYS)
3520   /* FLAGS is process->pty_flag for UNIX_PROCESSES */
3521   if ((flags & STREAM_PTY_FLUSHING) && fdo >= 0)
3522     {
3523       Bufbyte eof_char = get_eof_char (fdo);
3524       int pty_max_bytes = get_pty_max_bytes (fdo);
3525       filedesc_stream_set_pty_flushing (XLSTREAM(*outstream), pty_max_bytes, eof_char);
3526     }
3527 #endif
3528 #endif
3529
3530   return (NILP (*instream)
3531           ? USID_ERROR
3532 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3533           : flags & STREAM_NETWORK_CONNECTION
3534           ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream)))
3535 #endif
3536           : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (*instream))));
3537 }
3538
3539 USID
3540 emacs_mswindows_delete_stream_pair (Lisp_Object instream,
3541                                     Lisp_Object outstream)
3542 {
3543   /* Oh nothing special here for Win32 at all */
3544 #if defined (HAVE_UNIX_PROCESSES)
3545   int in = (NILP(instream)
3546             ? -1
3547 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3548             : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
3549             ? get_winsock_stream_param (XLSTREAM (instream))
3550 #endif
3551             : get_ntpipe_input_stream_param (XLSTREAM (instream)));
3552   int out = (NILP(outstream) ? -1
3553              : filedesc_stream_fd (XLSTREAM (outstream)));
3554
3555   if (in >= 0)
3556     close (in);
3557   if (out != in && out >= 0)
3558     close (out);
3559 #endif
3560
3561   return (NILP (instream)
3562           ? USID_DONTHASH
3563 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3564           : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
3565           ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream)))
3566 #endif
3567           : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream))));
3568 }
3569
3570 static int
3571 emacs_mswindows_current_event_timestamp (struct console *c)
3572 {
3573   return GetTickCount ();
3574 }
3575
3576 #ifndef HAVE_X_WINDOWS
3577 /* This is called from GC when a process object is about to be freed.
3578    If we've still got pointers to it in this file, we're gonna lose hard.
3579  */
3580 void
3581 debug_process_finalization (Lisp_Process *p)
3582 {
3583 #if 0 /* #### */
3584   Lisp_Object instr, outstr;
3585
3586   get_process_streams (p, &instr, &outstr);
3587   /* if it still has fds, then it hasn't been killed yet. */
3588   assert (NILP(instr));
3589   assert (NILP(outstr));
3590
3591   /* #### More checks here */
3592 #endif
3593 }
3594 #endif
3595
3596 /************************************************************************/
3597 /*                            initialization                            */
3598 /************************************************************************/
3599
3600 void
3601 reinit_vars_of_event_mswindows (void)
3602 {
3603   mswindows_in_modal_loop = 0;
3604   mswindows_pending_timers_count = 0;
3605
3606   mswindows_event_stream = xnew (struct event_stream);
3607
3608   mswindows_event_stream->event_pending_p       = emacs_mswindows_event_pending_p;
3609   mswindows_event_stream->force_event_pending = 0;
3610   mswindows_event_stream->next_event_cb         = emacs_mswindows_next_event;
3611   mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event;
3612   mswindows_event_stream->add_timeout_cb        = emacs_mswindows_add_timeout;
3613   mswindows_event_stream->remove_timeout_cb     = emacs_mswindows_remove_timeout;
3614   mswindows_event_stream->quit_p_cb             = emacs_mswindows_quit_p;
3615   mswindows_event_stream->select_console_cb     = emacs_mswindows_select_console;
3616   mswindows_event_stream->unselect_console_cb   = emacs_mswindows_unselect_console;
3617 #ifdef HAVE_MSG_SELECT
3618   mswindows_event_stream->select_process_cb     =
3619     (void (*)(Lisp_Process*))event_stream_unixoid_select_process;
3620   mswindows_event_stream->unselect_process_cb   =
3621     (void (*)(Lisp_Process*))event_stream_unixoid_unselect_process;
3622   mswindows_event_stream->create_stream_pair_cb = event_stream_unixoid_create_stream_pair;
3623   mswindows_event_stream->delete_stream_pair_cb = event_stream_unixoid_delete_stream_pair;
3624 #else
3625   mswindows_event_stream->select_process_cb     = emacs_mswindows_select_process;
3626   mswindows_event_stream->unselect_process_cb   = emacs_mswindows_unselect_process;
3627   mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair;
3628   mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair;
3629 #endif
3630   mswindows_event_stream->current_event_timestamp_cb =
3631     emacs_mswindows_current_event_timestamp;
3632 }
3633
3634 void
3635 vars_of_event_mswindows (void)
3636 {
3637   reinit_vars_of_event_mswindows ();
3638
3639   mswindows_u_dispatch_event_queue = Qnil;
3640   staticpro (&mswindows_u_dispatch_event_queue);
3641   mswindows_u_dispatch_event_queue_tail = Qnil;
3642   dump_add_root_object (&mswindows_u_dispatch_event_queue_tail);
3643
3644   mswindows_s_dispatch_event_queue = Qnil;
3645   staticpro (&mswindows_s_dispatch_event_queue);
3646   mswindows_s_dispatch_event_queue_tail = Qnil;
3647   dump_add_root_object (&mswindows_s_dispatch_event_queue_tail);
3648
3649   mswindows_error_caught_in_modal_loop = Qnil;
3650   staticpro (&mswindows_error_caught_in_modal_loop);
3651
3652
3653 #ifdef DEBUG_XEMACS
3654   DEFVAR_INT ("debug-mswindows-events", &debug_mswindows_events /*
3655 If non-zero, display debug information about Windows events that XEmacs sees.
3656 Information is displayed in a console window.  Currently defined values are:
3657
3658 1 == non-verbose output
3659 2 == verbose output
3660
3661 #### Unfortunately, not yet implemented.
3662 */ );
3663   debug_mswindows_events = 0;
3664 #endif
3665
3666   DEFVAR_BOOL ("mswindows-alt-by-itself-activates-menu",
3667                &mswindows_alt_by_itself_activates_menu /*
3668 *Controls whether pressing and releasing the Alt key activates the menubar.
3669 This applies only if no intervening key was pressed.  See also
3670 `menu-accelerator-enabled', which is probably the behavior you actually want.
3671 Default is t.
3672 */ );
3673
3674   DEFVAR_BOOL ("mswindows-dynamic-frame-resize",
3675                &mswindows_dynamic_frame_resize /*
3676 *Controls redrawing frame contents during mouse-drag or keyboard resize
3677 operation. When non-nil, the frame is redrawn while being resized. When
3678 nil, frame is not redrawn, and exposed areas are filled with default
3679 MDI application background color. Note that this option only has effect
3680 if "Show window contents while dragging" is on in system Display/Plus!
3681 settings.
3682 Default is t on fast machines, nil on slow.
3683 */ );
3684
3685   DEFVAR_INT ("mswindows-mouse-button-tolerance",
3686               &mswindows_mouse_button_tolerance /*
3687 *Analogue of double click interval for faking middle mouse events.
3688 The value is the minimum time in milliseconds that must elapse between
3689 left/right button down events before they are considered distinct events.
3690 If both mouse buttons are depressed within this interval, a middle mouse
3691 button down event is generated instead.
3692 If negative or zero, currently set system default is used instead.
3693 */ );
3694
3695   DEFVAR_INT ("mswindows-num-mouse-buttons", &mswindows_num_mouse_buttons /*
3696 Number of physical mouse buttons.
3697 */ );
3698
3699   DEFVAR_INT ("mswindows-mouse-button-max-skew-x",
3700               &mswindows_mouse_button_max_skew_x /*
3701 *Maximum horizontal distance in pixels between points in which left and
3702 right button clicks occurred for them to be translated into single
3703 middle button event. Clicks must occur in time not longer than defined
3704 by the variable `mswindows-mouse-button-tolerance'.
3705 If negative or zero, currently set system default is used instead.
3706 */ );
3707
3708   DEFVAR_INT ("mswindows-mouse-button-max-skew-y",
3709               &mswindows_mouse_button_max_skew_y /*
3710 *Maximum vertical distance in pixels between points in which left and
3711 right button clicks occurred for them to be translated into single
3712 middle button event. Clicks must occur in time not longer than defined
3713 by the variable `mswindows-mouse-button-tolerance'.
3714 If negative or zero, currently set system default is used instead.
3715 */ );
3716
3717   mswindows_mouse_button_max_skew_x = 0;
3718   mswindows_mouse_button_max_skew_y = 0;
3719   mswindows_mouse_button_tolerance = 0;
3720   mswindows_alt_by_itself_activates_menu = 1;
3721 }
3722
3723 void
3724 syms_of_event_mswindows (void)
3725 {
3726 }
3727
3728 void
3729 lstream_type_create_mswindows_selectable (void)
3730 {
3731   init_slurp_stream ();
3732   init_shove_stream ();
3733 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3734   init_winsock_stream ();
3735 #endif
3736 }
3737
3738 void
3739 init_event_mswindows_late (void)
3740 {
3741 #ifdef HAVE_MSG_SELECT
3742   windows_fd = open("/dev/windows", O_RDONLY | O_NONBLOCK, 0);
3743   assert (windows_fd>=0);
3744   FD_SET (windows_fd, &input_wait_mask);
3745   FD_ZERO(&zero_mask);
3746 #endif
3747
3748   event_stream = mswindows_event_stream;
3749
3750   mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE);
3751   mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);
3752 }