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