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