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