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