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