XEmacs 21.4.5 "Civil Service".
[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 extern int mswindows_dde_enable;
1614
1615 HDDEDATA CALLBACK
1616 mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
1617                         HSZ hszTopic, HSZ hszItem, HDDEDATA hdata,
1618                         DWORD dwData1, DWORD dwData2)
1619 {
1620   switch (uType)
1621     {
1622     case XTYP_CONNECT:
1623       if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
1624         return (HDDEDATA)TRUE;
1625       return (HDDEDATA)FALSE;
1626
1627     case XTYP_WILDCONNECT:
1628       {
1629         /* We only support one {service,topic} pair */
1630         HSZPAIR pairs[2] = {
1631           { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } };
1632
1633         if (!(hszItem  || DdeCmpStringHandles (hszItem, mswindows_dde_service)) &&
1634             !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)))
1635           return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs,
1636                                        sizeof (pairs), 0L, 0, uFmt, 0));
1637       }
1638       return (HDDEDATA)NULL;
1639
1640     case XTYP_EXECUTE:
1641       if (!mswindows_dde_enable)
1642         return (HDDEDATA) DDE_FBUSY;
1643
1644       if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
1645         {
1646           DWORD len = DdeGetData (hdata, NULL, 0, 0);
1647           LPBYTE cmd = (LPBYTE) alloca (len+1);
1648           char *end;
1649           char *filename;
1650           struct gcpro gcpro1, gcpro2;
1651           Lisp_Object l_dndlist = Qnil;
1652           Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1653           Lisp_Object frmcons, devcons, concons;
1654           Lisp_Event *event = XEVENT (emacs_event);
1655
1656           DdeGetData (hdata, cmd, len, 0);
1657           cmd[len] = '\0';
1658           DdeFreeDataHandle (hdata);
1659
1660           /* Check syntax & that it's an [Open("foo")] command, which we
1661            * treat like a file drop */
1662           /* #### Ought to be generalised and accept some other commands */
1663           if (*cmd == '[')
1664             cmd++;
1665           if (strnicmp (cmd, MSWINDOWS_DDE_ITEM_OPEN,
1666                         strlen (MSWINDOWS_DDE_ITEM_OPEN)))
1667             return DDE_FNOTPROCESSED;
1668           cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN);
1669           while (*cmd==' ')
1670             cmd++;
1671           if (*cmd!='(' || *(cmd+1)!='\"')
1672             return DDE_FNOTPROCESSED;
1673           end = (cmd+=2);
1674           while (*end && *end!='\"')
1675             end++;
1676           if (!*end)
1677             return DDE_FNOTPROCESSED;
1678           *end = '\0';
1679           if (*(++end)!=')')
1680             return DDE_FNOTPROCESSED;
1681           if (*(++end)==']')
1682             end++;
1683           if (*end)
1684             return DDE_FNOTPROCESSED;
1685
1686 #ifdef CYGWIN
1687           filename = alloca (cygwin_win32_to_posix_path_list_buf_size (cmd) + 5);
1688           strcpy (filename, "file:");
1689           cygwin_win32_to_posix_path_list (cmd, filename+5);
1690 #else
1691           dostounix_filename (cmd);
1692           filename = alloca (strlen (cmd)+6);
1693           strcpy (filename, "file:");
1694           strcat (filename, cmd);
1695 #endif
1696           GCPRO2 (emacs_event, l_dndlist);
1697           l_dndlist = make_string (filename, strlen (filename));
1698
1699           /* Find a mswindows frame */
1700           event->channel = Qnil;
1701           FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
1702             {
1703               Lisp_Object frame = XCAR (frmcons);
1704               if (FRAME_TYPE_P (XFRAME (frame), mswindows))
1705                 event->channel = frame;
1706             };
1707           assert (!NILP (event->channel));
1708
1709           event->timestamp = GetTickCount();
1710           event->event_type = misc_user_event;
1711           event->event.misc.button = 1;
1712           event->event.misc.modifiers = 0;
1713           event->event.misc.x = -1;
1714           event->event.misc.y = -1;
1715           event->event.misc.function = Qdragdrop_drop_dispatch;
1716           event->event.misc.object = Fcons (Qdragdrop_URL,
1717                                             Fcons (l_dndlist, Qnil));
1718           mswindows_enqueue_dispatch_event (emacs_event);
1719           UNGCPRO;
1720           return (HDDEDATA) DDE_FACK;
1721         }
1722       DdeFreeDataHandle (hdata);
1723       return (HDDEDATA) DDE_FNOTPROCESSED;
1724
1725     default:
1726       return (HDDEDATA) NULL;
1727     }
1728 }
1729 #endif
1730
1731 /*
1732  * Helper to do repainting - repaints can happen both from the windows
1733  * procedure and from magic events
1734  */
1735 static void
1736 mswindows_handle_paint (struct frame *frame)
1737 {
1738   HWND hwnd = FRAME_MSWINDOWS_HANDLE (frame);
1739
1740   /* According to the docs we need to check GetUpdateRect() before
1741      actually doing a WM_PAINT */
1742   if (GetUpdateRect (hwnd, NULL, FALSE))
1743     {
1744       PAINTSTRUCT paintStruct;
1745       int x, y, width, height;
1746
1747       BeginPaint (hwnd, &paintStruct);
1748       x = paintStruct.rcPaint.left;
1749       y = paintStruct.rcPaint.top;
1750       width = paintStruct.rcPaint.right - paintStruct.rcPaint.left;
1751       height = paintStruct.rcPaint.bottom - paintStruct.rcPaint.top;
1752       /* Normally we want to ignore expose events when child
1753          windows are unmapped, however once we are in the guts of
1754          WM_PAINT we need to make sure that we don't register
1755          unmaps then because they will not actually occur. */
1756       /* #### commenting out the next line seems to fix some problems
1757          but not all.  only andy currently understands this stuff and
1758          he needs to review it more carefully. --ben */
1759       if (!check_for_ignored_expose (frame, x, y, width, height))
1760         {
1761           hold_ignored_expose_registration = 1;
1762           mswindows_redraw_exposed_area (frame, x, y, width, height);
1763           hold_ignored_expose_registration = 0;
1764         }
1765       EndPaint (hwnd, &paintStruct);
1766     }
1767 }
1768
1769 /*
1770  * Returns 1 if a key is a real modifier or special key, which
1771  * is better handled by DefWindowProc
1772  */
1773 static int
1774 key_needs_default_processing_p (UINT vkey)
1775 {
1776   if (mswindows_alt_by_itself_activates_menu && vkey == VK_MENU
1777       /* if we let ALT activate the menu like this, then sticky ALT-modified
1778          keystrokes become impossible. */
1779       && !modifier_keys_are_sticky)
1780     return 1;
1781
1782   return 0;
1783 }
1784
1785 /* key-handling code is always ugly.  It just ends up working out
1786    that way.
1787
1788    #### Most of the sticky-modifier code below is copied from similar
1789    code in event-Xt.c.  They should somehow or other be merged.
1790
1791    Here are some pointers:
1792
1793    -- DOWN_MASK indicates which modifiers should be treated as "down"
1794       when the corresponding upstroke happens.  It gets reset for
1795       a particular modifier when that modifier goes up, and reset
1796       for all modifiers when a non-modifier key is pressed.  Example:
1797
1798       I press Control-A-Shift and then release Control-A-Shift.
1799       I want the Shift key to be sticky but not the Control key.
1800
1801    -- If a modifier key is sticky, I can unstick it by pressing
1802       the modifier key again. */
1803
1804 static WPARAM last_downkey;
1805 static int need_to_add_mask, down_mask;
1806
1807 #define XEMSW_LCONTROL (1<<0)
1808 #define XEMSW_RCONTROL (1<<1)
1809 #define XEMSW_LSHIFT (1<<2)
1810 #define XEMSW_RSHIFT (1<<3)
1811 #define XEMSW_LMENU (1<<4)
1812 #define XEMSW_RMENU (1<<5)
1813
1814 static int
1815 mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam,
1816                                    int downp, int keyp)
1817 {
1818   int mods = 0;
1819
1820   if (!modifier_keys_are_sticky) /* Optimize for non-sticky modifiers */
1821     return 0;
1822
1823   if (! (keyp &&
1824          (wParam == VK_CONTROL || wParam == VK_LCONTROL ||
1825           wParam == VK_RCONTROL ||
1826           wParam == VK_MENU || wParam == VK_LMENU ||
1827           wParam == VK_RMENU ||
1828           wParam == VK_SHIFT || wParam == VK_LSHIFT ||
1829           wParam == VK_RSHIFT)))
1830     { /* Not a modifier key */
1831       if (downp && keyp && !last_downkey)
1832         last_downkey = wParam;
1833       /* If I hold press-and-release the Control key and then press
1834          and hold down the right arrow, I want it to auto-repeat
1835          Control-Right.  On the other hand, if I do the same but
1836          manually press the Right arrow a bunch of times, I want
1837          to see one Control-Right and then a bunch of Rights.
1838          This means that we need to distinguish between an
1839          auto-repeated key and a key pressed and released a bunch
1840          of times. */
1841       else if ((downp && !keyp) ||
1842                (downp && keyp && last_downkey &&
1843                 (wParam != last_downkey ||
1844                  /* the "previous key state" bit indicates autorepeat */
1845                  ! (lParam & (1 << 30)))))
1846         {
1847           need_to_add_mask = 0;
1848           last_downkey = 0;
1849         }
1850       if (downp)
1851         down_mask = 0;
1852
1853       mods = need_to_add_mask;
1854     }
1855   else                          /* Modifier key pressed */
1856     {
1857       /* If a non-modifier key was pressed in the middle of a bunch
1858          of modifiers, then it unsticks all the modifiers that were
1859          previously pressed.  We cannot unstick the modifiers until
1860          now because we want to check for auto-repeat of the
1861          non-modifier key. */
1862
1863       if (last_downkey)
1864         {
1865           last_downkey = 0;
1866           need_to_add_mask = 0;
1867         }
1868
1869 #define FROB(mask)                              \
1870 do {                                            \
1871   if (downp && keyp)                            \
1872     {                                           \
1873       /* If modifier key is already sticky,     \
1874          then unstick it.  Note that we do      \
1875          not test down_mask to deal with the    \
1876          unlikely but possible case that the    \
1877          modifier key auto-repeats. */          \
1878       if (need_to_add_mask & mask)              \
1879         {                                       \
1880           need_to_add_mask &= ~mask;            \
1881           down_mask &= ~mask;                   \
1882         }                                       \
1883       else                                      \
1884         down_mask |= mask;                      \
1885     }                                           \
1886   else                                          \
1887     {                                           \
1888       if (down_mask & mask)                     \
1889         {                                       \
1890           down_mask &= ~mask;                   \
1891           need_to_add_mask |= mask;             \
1892         }                                       \
1893     }                                           \
1894 } while (0)
1895
1896       if ((wParam == VK_CONTROL && (lParam & 0x1000000))
1897           || wParam == VK_RCONTROL)
1898         FROB (XEMSW_RCONTROL);
1899       if ((wParam == VK_CONTROL && !(lParam & 0x1000000))
1900           || wParam == VK_LCONTROL)
1901         FROB (XEMSW_LCONTROL);
1902
1903       if ((wParam == VK_SHIFT && (lParam & 0x1000000))
1904           || wParam == VK_RSHIFT)
1905         FROB (XEMSW_RSHIFT);
1906       if ((wParam == VK_SHIFT && !(lParam & 0x1000000))
1907           || wParam == VK_LSHIFT)
1908         FROB (XEMSW_LSHIFT);
1909
1910       if ((wParam == VK_MENU && (lParam & 0x1000000))
1911           || wParam == VK_RMENU)
1912         FROB (XEMSW_RMENU);
1913       if ((wParam == VK_MENU && !(lParam & 0x1000000))
1914           || wParam == VK_LMENU)
1915         FROB (XEMSW_LMENU);
1916     }
1917 #undef FROB
1918
1919   if (mods && downp)
1920     {
1921       BYTE keymap[256];
1922
1923       GetKeyboardState (keymap);
1924
1925       if (mods & XEMSW_LCONTROL)
1926         {
1927           keymap [VK_CONTROL] |= 0x80;
1928           keymap [VK_LCONTROL] |= 0x80;
1929         }
1930       if (mods & XEMSW_RCONTROL)
1931         {
1932           keymap [VK_CONTROL] |= 0x80;
1933           keymap [VK_RCONTROL] |= 0x80;
1934         }
1935
1936       if (mods & XEMSW_LSHIFT)
1937         {
1938           keymap [VK_SHIFT] |= 0x80;
1939           keymap [VK_LSHIFT] |= 0x80;
1940         }
1941       if (mods & XEMSW_RSHIFT)
1942         {
1943           keymap [VK_SHIFT] |= 0x80;
1944           keymap [VK_RSHIFT] |= 0x80;
1945         }
1946
1947       if (mods & XEMSW_LMENU)
1948         {
1949           keymap [VK_MENU] |= 0x80;
1950           keymap [VK_LMENU] |= 0x80;
1951         }
1952       if (mods & XEMSW_RMENU)
1953         {
1954           keymap [VK_MENU] |= 0x80;
1955           keymap [VK_RMENU] |= 0x80;
1956         }
1957
1958       SetKeyboardState (keymap);
1959       return 1;
1960     }
1961
1962   return 0;
1963 }
1964
1965 static void
1966 clear_sticky_modifiers (void)
1967 {
1968   need_to_add_mask = 0;
1969   last_downkey     = 0;
1970   down_mask        = 0;
1971 }
1972
1973 #ifdef DEBUG_XEMACS
1974
1975 #if 0
1976
1977 static void
1978 output_modifier_keyboard_state (void)
1979 {
1980   BYTE keymap[256];
1981
1982   GetKeyboardState (keymap);
1983
1984   stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
1985               keymap[VK_MENU] & 0x80 ? 1 : 0,
1986               keymap[VK_MENU] & 0x1 ? 1 : 0,
1987               keymap[VK_LMENU] & 0x80 ? 1 : 0,
1988               keymap[VK_LMENU] & 0x1 ? 1 : 0,
1989               keymap[VK_RMENU] & 0x80 ? 1 : 0,
1990               keymap[VK_RMENU] & 0x1 ? 1 : 0);
1991   stderr_out ("GetKeyboardState VK_CONTROL %d %d VK_LCONTROL %d %d VK_RCONTROL %d %d\n",
1992               keymap[VK_CONTROL] & 0x80 ? 1 : 0,
1993               keymap[VK_CONTROL] & 0x1 ? 1 : 0,
1994               keymap[VK_LCONTROL] & 0x80 ? 1 : 0,
1995               keymap[VK_LCONTROL] & 0x1 ? 1 : 0,
1996               keymap[VK_RCONTROL] & 0x80 ? 1 : 0,
1997               keymap[VK_RCONTROL] & 0x1 ? 1 : 0);
1998   stderr_out ("GetKeyboardState VK_SHIFT %d %d VK_LSHIFT %d %d VK_RSHIFT %d %d\n",
1999               keymap[VK_SHIFT] & 0x80 ? 1 : 0,
2000               keymap[VK_SHIFT] & 0x1 ? 1 : 0,
2001               keymap[VK_LSHIFT] & 0x80 ? 1 : 0,
2002               keymap[VK_LSHIFT] & 0x1 ? 1 : 0,
2003               keymap[VK_RSHIFT] & 0x80 ? 1 : 0,
2004               keymap[VK_RSHIFT] & 0x1 ? 1 : 0);
2005 }
2006
2007 #endif
2008
2009 /* try to debug the stuck-alt-key problem.
2010
2011  #### this happens only inconsistently, and may only happen when using
2012  StickyKeys in the Win2000 accessibility section of the control panel,
2013  which is extremely broken for other reasons.  */
2014
2015 static void
2016 output_alt_keyboard_state (void)
2017 {
2018   BYTE keymap[256];
2019   SHORT keystate[3];
2020   // SHORT asyncstate[3];
2021
2022   GetKeyboardState (keymap);
2023   keystate[0] = GetKeyState (VK_MENU);
2024   keystate[1] = GetKeyState (VK_LMENU);
2025   keystate[2] = GetKeyState (VK_RMENU);
2026   /* Doing this interferes with key processing. */
2027 /*   asyncstate[0] = GetAsyncKeyState (VK_MENU); */
2028 /*   asyncstate[1] = GetAsyncKeyState (VK_LMENU); */
2029 /*   asyncstate[2] = GetAsyncKeyState (VK_RMENU); */
2030
2031   stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
2032               keymap[VK_MENU] & 0x80 ? 1 : 0,
2033               keymap[VK_MENU] & 0x1 ? 1 : 0,
2034               keymap[VK_LMENU] & 0x80 ? 1 : 0,
2035               keymap[VK_LMENU] & 0x1 ? 1 : 0,
2036               keymap[VK_RMENU] & 0x80 ? 1 : 0,
2037               keymap[VK_RMENU] & 0x1 ? 1 : 0);
2038   stderr_out ("GetKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
2039               keystate[0] & 0x8000 ? 1 : 0,
2040               keystate[0] & 0x1 ? 1 : 0,
2041               keystate[1] & 0x8000 ? 1 : 0,
2042               keystate[1] & 0x1 ? 1 : 0,
2043               keystate[2] & 0x8000 ? 1 : 0,
2044               keystate[2] & 0x1 ? 1 : 0);
2045 /*   stderr_out ("GetAsyncKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", */
2046 /*            asyncstate[0] & 0x8000 ? 1 : 0, */
2047 /*            asyncstate[0] & 0x1 ? 1 : 0, */
2048 /*            asyncstate[1] & 0x8000 ? 1 : 0, */
2049 /*            asyncstate[1] & 0x1 ? 1 : 0, */
2050 /*            asyncstate[2] & 0x8000 ? 1 : 0, */
2051 /*            asyncstate[2] & 0x1 ? 1 : 0); */
2052 }
2053
2054 #endif /* DEBUG_XEMACS */
2055
2056
2057 /*
2058  * The windows procedure for the window class XEMACS_CLASS
2059  */
2060 LRESULT WINAPI
2061 mswindows_wnd_proc (HWND hwnd, UINT message_, WPARAM wParam, LPARAM lParam)
2062 {
2063   /* Note: Remember to initialize emacs_event and event before use.
2064      This code calls code that can GC. You must GCPRO before calling such code. */
2065   Lisp_Object emacs_event = Qnil;
2066   Lisp_Object fobj = Qnil;
2067
2068   Lisp_Event *event;
2069   struct frame *frame;
2070   struct mswindows_frame* msframe;
2071
2072   /* If you hit this, rewrite the offending API call to occur after GC,
2073      using register_post_gc_action(). */
2074   assert (!gc_in_progress);
2075
2076 #ifdef DEBUG_XEMACS
2077   if (debug_mswindows_events)
2078     debug_output_mswin_message (hwnd, message_, wParam, lParam);
2079 #endif /* DEBUG_XEMACS */
2080
2081   assert (!GetWindowLong (hwnd, GWL_USERDATA));
2082   switch (message_)
2083     {
2084     case WM_DESTROYCLIPBOARD:
2085       /* We own the clipboard and someone else wants it.  Delete our
2086          cached copy of the clipboard contents so we'll ask for it from
2087          Windows again when someone does a paste, and destroy any memory
2088          objects we hold on the clipboard that are not in the list of types
2089          that Windows will delete itself. */
2090       mswindows_destroy_selection (QCLIPBOARD);
2091       handle_selection_clear (QCLIPBOARD);
2092       break;
2093
2094     case WM_ERASEBKGND:
2095       /* Erase background only during non-dynamic sizing */
2096       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2097       if (msframe->sizing && !mswindows_dynamic_frame_resize)
2098         goto defproc;
2099       return 1;
2100
2101     case WM_CLOSE:
2102       fobj = mswindows_find_frame (hwnd);
2103       mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt));
2104       break;
2105
2106     case WM_KEYUP:
2107     case WM_SYSKEYUP:
2108
2109       /* See Win95 comment under WM_KEYDOWN */
2110       {
2111         BYTE keymap[256];
2112         int should_set_keymap = 0;
2113
2114 #ifdef DEBUG_XEMACS
2115         if (debug_mswindows_events > 2)
2116           output_alt_keyboard_state ();
2117 #endif /* DEBUG_XEMACS */
2118
2119         mswindows_handle_sticky_modifiers (wParam, lParam, 0, 1);
2120         if (wParam == VK_CONTROL)
2121           {
2122             GetKeyboardState (keymap);
2123             keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80;
2124             should_set_keymap = 1;
2125           }
2126         else if (wParam == VK_MENU)
2127           {
2128             GetKeyboardState (keymap);
2129             keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80;
2130             should_set_keymap = 1;
2131           }
2132
2133         if (should_set_keymap)
2134           //        && (message_ != WM_SYSKEYUP
2135           //    || NILP (Vmenu_accelerator_enabled)))
2136           SetKeyboardState (keymap);
2137
2138       }
2139
2140       if (key_needs_default_processing_p (wParam))
2141         goto defproc;
2142       else
2143         break;
2144
2145     case WM_KEYDOWN:
2146     case WM_SYSKEYDOWN:
2147
2148       /* In some locales the right-hand Alt key is labelled AltGr. This key
2149        * should produce alternative characters when combined with another key.
2150        * eg on a German keyboard pressing AltGr+q should produce '@'.
2151        * AltGr generates exactly the same keystrokes as LCtrl+RAlt. But if
2152        * TranslateMessage() is called with *any* combination of Ctrl+Alt down,
2153        * it translates as if AltGr were down.
2154        * We get round this by removing all modifiers from the keymap before
2155        * calling TranslateMessage() unless AltGr is *really* down. */
2156       {
2157         BYTE keymap_trans[256];
2158         BYTE keymap_orig[256];
2159         BYTE keymap_sticky[256];
2160         int has_AltGr = mswindows_current_layout_has_AltGr ();
2161         int mods = 0, mods_with_shift = 0;
2162         int extendedp = lParam & 0x1000000;
2163         Lisp_Object keysym;
2164         int sticky_changed;
2165
2166 #ifdef DEBUG_XEMACS
2167         if (debug_mswindows_events > 2)
2168           output_alt_keyboard_state ();
2169 #endif /* DEBUG_XEMACS */
2170
2171         GetKeyboardState (keymap_orig);
2172         frame = XFRAME (mswindows_find_frame (hwnd));
2173         if ((sticky_changed =
2174              mswindows_handle_sticky_modifiers (wParam, lParam, 1, 1)))
2175           {
2176             GetKeyboardState (keymap_sticky);
2177             if (keymap_sticky[VK_MENU] & 0x80)
2178               {
2179                 message_ = WM_SYSKEYDOWN;
2180                 /* We have to set the "context bit" so that the
2181                    TranslateMessage() call below that generates the
2182                    SYSCHAR message does its thing; see the documentation
2183                    on WM_SYSKEYDOWN */
2184                 lParam |= 1 << 29;
2185               }
2186           }
2187         else
2188           memcpy (keymap_sticky, keymap_orig, 256);
2189
2190         mods = mswindows_modifier_state (keymap_sticky, (DWORD) -1, has_AltGr);
2191         mods_with_shift = mods;
2192
2193         /* Handle non-printables */
2194         if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods,
2195                                                            extendedp)))
2196           {
2197             mswindows_enqueue_keypress_event (hwnd, keysym, mods);
2198             if (sticky_changed)
2199               SetKeyboardState (keymap_orig);
2200           }
2201         else    /* Normal keys & modifiers */
2202           {
2203             Emchar quit_ch =
2204               CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd)));
2205             POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) };
2206             MSG msg, tranmsg;
2207             int potential_accelerator = 0;
2208             int got_accelerator = 0;
2209
2210             msg.hwnd = hwnd;
2211             msg.message = message_;
2212             msg.wParam = wParam;
2213             msg.lParam = lParam;
2214             msg.time = GetMessageTime();
2215             msg.pt = pnt;
2216
2217             /* GetKeyboardState() does not work as documented on Win95. We have
2218              * to loosely track Left and Right modifiers on behalf of the OS,
2219              * without screwing up Windows NT which tracks them properly. */
2220             if (wParam == VK_CONTROL)
2221               {
2222                 keymap_orig[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
2223                 keymap_sticky[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
2224               }
2225             else if (wParam == VK_MENU)
2226               {
2227                 keymap_orig[extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
2228                 keymap_sticky[extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
2229               }
2230
2231             if (!NILP (Vmenu_accelerator_enabled) &&
2232                 !(mods & XEMACS_MOD_SHIFT) && message_ == WM_SYSKEYDOWN)
2233               potential_accelerator = 1;
2234
2235             /* Remove shift modifier from an ascii character */
2236             mods &= ~XEMACS_MOD_SHIFT;
2237
2238             memcpy (keymap_trans, keymap_sticky, 256);
2239
2240             /* Clear control and alt modifiers unless AltGr is pressed */
2241             keymap_trans[VK_RCONTROL] = 0;
2242             keymap_trans[VK_LMENU] = 0;
2243             if (!has_AltGr || !(keymap_trans[VK_LCONTROL] & 0x80)
2244                 || !(keymap_trans[VK_RMENU] & 0x80))
2245               {
2246                 keymap_trans[VK_LCONTROL] = 0;
2247                 keymap_trans[VK_CONTROL] = 0;
2248                 keymap_trans[VK_RMENU] = 0;
2249                 keymap_trans[VK_MENU] = 0;
2250               }
2251             SetKeyboardState (keymap_trans);
2252
2253             /* Maybe generate some WM_[SYS]CHARs in the queue */
2254             TranslateMessage (&msg);
2255
2256             while (PeekMessage (&tranmsg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)
2257                    || PeekMessage (&tranmsg, hwnd, WM_SYSCHAR, WM_SYSCHAR,
2258                                    PM_REMOVE))
2259               {
2260                 int mods_with_quit = mods;
2261                 WPARAM ch = tranmsg.wParam;
2262
2263 #ifdef DEBUG_XEMACS
2264                 if (debug_mswindows_events)
2265                   {
2266                     stderr_out ("-> ");
2267                     debug_output_mswin_message (tranmsg.hwnd, tranmsg.message,
2268                                                 tranmsg.wParam,
2269                                                 tranmsg.lParam);
2270                   }
2271 #endif /* DEBUG_XEMACS */
2272
2273                 /* If a quit char with no modifiers other than control and
2274                    shift, then mark it with a fake modifier, which is removed
2275                    upon dequeueing the event */
2276                 /* !!#### Fix this in my mule ws -- replace current_buffer
2277                    with 0 */
2278                 if (((quit_ch < ' ' && (mods & XEMACS_MOD_CONTROL)
2279                       && DOWNCASE (current_buffer, quit_ch + 'a' - 1) ==
2280                       DOWNCASE (current_buffer, ch))
2281                      || (quit_ch >= ' ' && !(mods & XEMACS_MOD_CONTROL)
2282                          && DOWNCASE (current_buffer, quit_ch) ==
2283                          DOWNCASE (current_buffer, ch)))
2284                     && ((mods_with_shift &
2285                          ~(XEMACS_MOD_CONTROL | XEMACS_MOD_SHIFT))
2286                         == 0))
2287                   {
2288                     mods_with_quit |= FAKE_MOD_QUIT;
2289                     if (mods_with_shift & XEMACS_MOD_SHIFT)
2290                       mods_with_quit |= FAKE_MOD_QUIT_CRITICAL;
2291                     mswindows_quit_chars_count++;
2292                   }
2293                 else if (potential_accelerator && !got_accelerator &&
2294                          mswindows_char_is_accelerator (frame, ch))
2295                   {
2296                     got_accelerator = 1;
2297                     break;
2298                   }
2299                 mswindows_enqueue_keypress_event (hwnd, make_char (ch),
2300                                                   mods_with_quit);
2301               } /* while */
2302
2303             /* This generates WM_SYSCHAR messages, which are interpreted
2304                by DefWindowProc as the menu selections. */
2305             if (got_accelerator)
2306               {
2307                 SetKeyboardState (keymap_sticky);
2308                 TranslateMessage (&msg);
2309                 SetKeyboardState (keymap_orig);
2310                 goto defproc;
2311               }
2312
2313             SetKeyboardState (keymap_orig);
2314           } /* else */
2315       }
2316
2317       if (key_needs_default_processing_p (wParam))
2318         goto defproc;
2319       else
2320         break;
2321
2322     case WM_MBUTTONDOWN:
2323     case WM_MBUTTONUP:
2324       /* Real middle mouse button has nothing to do with emulated one:
2325          if one wants to exercise fingers playing chords on the mouse,
2326          he is allowed to do that! */
2327       mswindows_enqueue_mouse_button_event (hwnd, message_,
2328                                             MAKEPOINTS (lParam),
2329                                             wParam &~ MK_MBUTTON,
2330                                             GetMessageTime());
2331       break;
2332
2333     case WM_LBUTTONUP:
2334       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2335       msframe->last_click_time =  GetMessageTime();
2336
2337       KillTimer (hwnd, BUTTON_2_TIMER_ID);
2338       msframe->button2_need_lbutton = 0;
2339       if (msframe->ignore_next_lbutton_up)
2340         {
2341           msframe->ignore_next_lbutton_up = 0;
2342         }
2343       else if (msframe->button2_is_down)
2344         {
2345           msframe->button2_is_down = 0;
2346           msframe->ignore_next_rbutton_up = 1;
2347           mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
2348                                                 MAKEPOINTS (lParam),
2349                                                 wParam
2350                                                 &~ (MK_LBUTTON | MK_MBUTTON
2351                                                     | MK_RBUTTON),
2352                                                 GetMessageTime());
2353         }
2354       else
2355         {
2356           if (msframe->button2_need_rbutton)
2357             {
2358               msframe->button2_need_rbutton = 0;
2359               mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
2360                                                     MAKEPOINTS (lParam),
2361                                                     wParam &~ MK_LBUTTON,
2362                                                     GetMessageTime());
2363             }
2364           mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
2365                                                 MAKEPOINTS (lParam),
2366                                                 wParam &~ MK_LBUTTON,
2367                                                 GetMessageTime());
2368         }
2369       break;
2370
2371     case WM_RBUTTONUP:
2372       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2373       msframe->last_click_time =  GetMessageTime();
2374
2375       KillTimer (hwnd, BUTTON_2_TIMER_ID);
2376       msframe->button2_need_rbutton = 0;
2377       if (msframe->ignore_next_rbutton_up)
2378         {
2379           msframe->ignore_next_rbutton_up = 0;
2380         }
2381       else if (msframe->button2_is_down)
2382         {
2383           msframe->button2_is_down = 0;
2384           msframe->ignore_next_lbutton_up = 1;
2385           mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
2386                                                 MAKEPOINTS (lParam),
2387                                                 wParam
2388                                                 &~ (MK_LBUTTON | MK_MBUTTON
2389                                                     | MK_RBUTTON),
2390                                                 GetMessageTime());
2391         }
2392       else
2393         {
2394           if (msframe->button2_need_lbutton)
2395             {
2396               msframe->button2_need_lbutton = 0;
2397               mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
2398                                                     MAKEPOINTS (lParam),
2399                                                     wParam &~ MK_RBUTTON,
2400                                                     GetMessageTime());
2401             }
2402           mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
2403                                                 MAKEPOINTS (lParam),
2404                                                 wParam &~ MK_RBUTTON,
2405                                                 GetMessageTime());
2406         }
2407       break;
2408
2409     case WM_LBUTTONDOWN:
2410       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2411
2412       if (msframe->button2_need_lbutton)
2413         {
2414           KillTimer (hwnd, BUTTON_2_TIMER_ID);
2415           msframe->button2_need_lbutton = 0;
2416           msframe->button2_need_rbutton = 0;
2417           if (mswindows_button2_near_enough (msframe->last_click_point,
2418                                              MAKEPOINTS (lParam)))
2419             {
2420               mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
2421                                                     MAKEPOINTS (lParam),
2422                                                     wParam
2423                                                     &~ (MK_LBUTTON | MK_MBUTTON
2424                                                         | MK_RBUTTON),
2425                                                     GetMessageTime());
2426               msframe->button2_is_down = 1;
2427             }
2428           else
2429             {
2430               mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
2431                                                     msframe->last_click_point,
2432                                                     msframe->last_click_mods
2433                                                     &~ MK_RBUTTON,
2434                                                     msframe->last_click_time);
2435               mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
2436                                                     MAKEPOINTS (lParam),
2437                                                     wParam &~ MK_LBUTTON,
2438                                                     GetMessageTime());
2439             }
2440         }
2441       else
2442         {
2443           mswindows_set_chord_timer (hwnd);
2444           msframe->button2_need_rbutton = 1;
2445           msframe->last_click_point = MAKEPOINTS (lParam);
2446           msframe->last_click_mods = wParam;
2447         }
2448       msframe->last_click_time =  GetMessageTime();
2449       break;
2450
2451     case WM_RBUTTONDOWN:
2452       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2453
2454       if (msframe->button2_need_rbutton)
2455         {
2456           KillTimer (hwnd, BUTTON_2_TIMER_ID);
2457           msframe->button2_need_lbutton = 0;
2458           msframe->button2_need_rbutton = 0;
2459           if (mswindows_button2_near_enough (msframe->last_click_point,
2460                                              MAKEPOINTS (lParam)))
2461             {
2462               mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
2463                                                     MAKEPOINTS (lParam),
2464                                                     wParam
2465                                                     &~ (MK_LBUTTON | MK_MBUTTON
2466                                                         | MK_RBUTTON),
2467                                                     GetMessageTime());
2468               msframe->button2_is_down = 1;
2469             }
2470           else
2471             {
2472               mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
2473                                                     msframe->last_click_point,
2474                                                     msframe->last_click_mods
2475                                                     &~ MK_LBUTTON,
2476                                                     msframe->last_click_time);
2477               mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
2478                                                     MAKEPOINTS (lParam),
2479                                                     wParam &~ MK_RBUTTON,
2480                                                     GetMessageTime());
2481             }
2482         }
2483       else
2484         {
2485           mswindows_set_chord_timer (hwnd);
2486           msframe->button2_need_lbutton = 1;
2487           msframe->last_click_point = MAKEPOINTS (lParam);
2488           msframe->last_click_mods = wParam;
2489         }
2490       msframe->last_click_time =  GetMessageTime();
2491       break;
2492
2493     case WM_TIMER:
2494       if (wParam == BUTTON_2_TIMER_ID)
2495         {
2496           msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2497           KillTimer (hwnd, BUTTON_2_TIMER_ID);
2498
2499           if (msframe->button2_need_lbutton)
2500             {
2501               msframe->button2_need_lbutton = 0;
2502               mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
2503                                                     msframe->last_click_point,
2504                                                     msframe->last_click_mods
2505                                                     &~ MK_RBUTTON,
2506                                                     msframe->last_click_time);
2507             }
2508           else if (msframe->button2_need_rbutton)
2509             {
2510               msframe->button2_need_rbutton = 0;
2511               mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
2512                                                     msframe->last_click_point,
2513                                                     msframe->last_click_mods
2514                                                     &~ MK_LBUTTON,
2515                                                     msframe->last_click_time);
2516             }
2517         }
2518       else
2519         assert ("Spurious timer fired" == 0);
2520       break;
2521
2522     case WM_MOUSEMOVE:
2523       /* Optimization: don't report mouse movement while size is changing */
2524       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2525       if (!msframe->sizing)
2526         {
2527           /* When waiting for the second mouse button to finish
2528              button2 emulation, and have moved too far, just pretend
2529              as if timer has expired. This improves drag-select feedback */
2530           if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton)
2531               && !mswindows_button2_near_enough (msframe->last_click_point,
2532                                                  MAKEPOINTS (lParam)))
2533             {
2534               KillTimer (hwnd, BUTTON_2_TIMER_ID);
2535               SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0);
2536             }
2537
2538           emacs_event = Fmake_event (Qnil, Qnil);
2539           event = XEVENT(emacs_event);
2540
2541           event->channel = mswindows_find_frame(hwnd);
2542           event->timestamp = GetMessageTime();
2543           event->event_type = pointer_motion_event;
2544           event->event.motion.x = MAKEPOINTS(lParam).x;
2545           event->event.motion.y = MAKEPOINTS(lParam).y;
2546           event->event.motion.modifiers =
2547             mswindows_modifier_state (NULL, wParam, 0);
2548
2549           mswindows_enqueue_dispatch_event (emacs_event);
2550         }
2551       break;
2552
2553     case WM_CANCELMODE:
2554       ReleaseCapture ();
2555       /* Queue a `cancel-mode-internal' misc user event, so mouse
2556          selection would be canceled if any */
2557       mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd),
2558                                          Qcancel_mode_internal, Qnil);
2559       break;
2560
2561     case WM_NOTIFY:
2562       {
2563         LPNMHDR nmhdr = (LPNMHDR)lParam;
2564
2565         if (nmhdr->code ==  TTN_NEEDTEXT)
2566           {
2567 #ifdef HAVE_TOOLBARS
2568             LPTOOLTIPTEXT tttext = (LPTOOLTIPTEXT)lParam;
2569             Lisp_Object btext;
2570
2571             /* find out which toolbar */
2572             frame = XFRAME (mswindows_find_frame (hwnd));
2573             btext = mswindows_get_toolbar_button_text ( frame,
2574                                                         nmhdr->idFrom );
2575
2576             tttext->lpszText = NULL;
2577             tttext->hinst = NULL;
2578
2579             if (!NILP(btext))
2580               {
2581                 /* I think this is safe since the text will only go away
2582                    when the toolbar does...*/
2583                 LISP_STRING_TO_EXTERNAL (btext, tttext->lpszText, Qnative);
2584               }
2585 #endif
2586           }
2587         /* handle tree view callbacks */
2588         else if (nmhdr->code == TVN_SELCHANGED)
2589           {
2590             NM_TREEVIEW* ptree = (NM_TREEVIEW*)lParam;
2591             frame = XFRAME (mswindows_find_frame (hwnd));
2592             mswindows_handle_gui_wm_command (frame, 0, ptree->itemNew.lParam);
2593           }
2594         /* handle tab control callbacks */
2595         else if (nmhdr->code == TCN_SELCHANGE)
2596           {
2597             TC_ITEM item;
2598             int idx = SendMessage (nmhdr->hwndFrom, TCM_GETCURSEL, 0, 0);
2599             frame = XFRAME (mswindows_find_frame (hwnd));
2600
2601             item.mask = TCIF_PARAM;
2602             SendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM)idx,
2603                          (LPARAM)&item);
2604
2605             mswindows_handle_gui_wm_command (frame, 0, item.lParam);
2606           }
2607       }
2608       break;
2609
2610     case WM_PAINT:
2611       /* hdc will be NULL unless this is a subwindow - in which case we
2612          shouldn't have received a paint message for it here. */
2613       assert (wParam == 0);
2614
2615       /* Can't queue a magic event because windows goes modal and sends paint
2616          messages directly to the windows procedure when doing solid drags
2617          and the message queue doesn't get processed. */
2618       mswindows_handle_paint (XFRAME (mswindows_find_frame (hwnd)));
2619       break;
2620
2621     case WM_WINDOWPOSCHANGED:
2622       /* This is sent before WM_SIZE; in fact, the processing of this
2623          by DefWindowProc() sends WM_SIZE.  But WM_SIZE is not sent when
2624          a window is hidden (make-frame-invisible), so we need to process
2625          this and update the state flags. */
2626       {
2627         fobj = mswindows_find_frame (hwnd);
2628         frame = XFRAME (fobj);
2629         if (IsIconic (hwnd))
2630           {
2631             FRAME_VISIBLE_P (frame) = 0;
2632             FRAME_ICONIFIED_P (frame) = 1;
2633           }
2634         else if (IsWindowVisible (hwnd))
2635           {
2636             FRAME_VISIBLE_P (frame) = 1;
2637             FRAME_ICONIFIED_P (frame) = 0;
2638           }
2639         else
2640           {
2641             FRAME_VISIBLE_P (frame) = 0;
2642             FRAME_ICONIFIED_P (frame) = 0;
2643           }         
2644
2645         return DefWindowProc (hwnd, message_, wParam, lParam);
2646       }
2647
2648     case WM_SIZE:
2649       /* We only care about this message if our size has really changed */
2650       if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
2651         {
2652           RECT rect;
2653           int columns, rows;
2654
2655           fobj = mswindows_find_frame (hwnd);
2656           frame = XFRAME (fobj);
2657           msframe  = FRAME_MSWINDOWS_DATA (frame);
2658
2659           /* We cannot handle frame map and unmap hooks right in
2660              this routine, because these may throw. We queue
2661              magic events to run these hooks instead - kkm */
2662
2663           if (wParam==SIZE_MINIMIZED)
2664             {
2665               /* Iconified */
2666               mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME);
2667             }
2668           else
2669             {
2670               GetClientRect(hwnd, &rect);
2671               FRAME_PIXWIDTH(frame) = rect.right;
2672               FRAME_PIXHEIGHT(frame) = rect.bottom;
2673
2674               pixel_to_real_char_size (frame, rect.right, rect.bottom,
2675                                        &FRAME_MSWINDOWS_CHARWIDTH (frame),
2676                                        &FRAME_MSWINDOWS_CHARHEIGHT (frame));
2677
2678               pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows);
2679               change_frame_size (frame, rows, columns, 1);
2680
2681               /* If we are inside frame creation, we have to apply geometric
2682                  properties now. */
2683               if (FRAME_MSWINDOWS_TARGET_RECT (frame))
2684                 {
2685                   /* Yes, we have to size again */
2686                   mswindows_size_frame_internal ( frame,
2687                                                   FRAME_MSWINDOWS_TARGET_RECT
2688                                                   (frame));
2689                   /* Reset so we do not get here again. The SetWindowPos call in
2690                    * mswindows_size_frame_internal can cause recursion here. */
2691                   if (FRAME_MSWINDOWS_TARGET_RECT (frame))
2692                     {
2693                       xfree (FRAME_MSWINDOWS_TARGET_RECT (frame));
2694                       FRAME_MSWINDOWS_TARGET_RECT (frame) = 0;
2695                     }
2696                 }
2697               else
2698                 {
2699                   if (!msframe->sizing && !FRAME_VISIBLE_P (frame))
2700                     mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME);
2701
2702                   if (!msframe->sizing || mswindows_dynamic_frame_resize)
2703                     redisplay ();
2704                 }
2705             }
2706         }
2707       break;
2708
2709     case WM_DISPLAYCHANGE:
2710       {
2711         struct device *d;
2712         DWORD message_tick = GetMessageTime ();
2713
2714         fobj = mswindows_find_frame (hwnd);
2715         frame = XFRAME (fobj);
2716         d = XDEVICE (FRAME_DEVICE (frame));
2717
2718         /* Do this only once per message. XEmacs can receive this message
2719            through as many frames as it currently has open. Message time
2720            will be the same for all these messages. Despite extreme
2721            efficiency, the code below has about one in 4 billion
2722            probability that the HDC is not recreated, provided that
2723            XEmacs is running sufficiently longer than 52 days. */
2724         if (DEVICE_MSWINDOWS_UPDATE_TICK(d) != message_tick)
2725           {
2726             DEVICE_MSWINDOWS_UPDATE_TICK(d) = message_tick;
2727             DeleteDC (DEVICE_MSWINDOWS_HCDC(d));
2728             DEVICE_MSWINDOWS_HCDC(d) = CreateCompatibleDC (NULL);
2729           }
2730       }
2731       break;
2732
2733       /* Misc magic events which only require that the frame be identified */
2734     case WM_SETFOCUS:
2735     case WM_KILLFOCUS:
2736       mswindows_enqueue_magic_event (hwnd, message_);
2737       break;
2738
2739     case WM_WINDOWPOSCHANGING:
2740       {
2741         WINDOWPOS *wp = (LPWINDOWPOS) lParam;
2742         WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) };
2743         GetWindowPlacement(hwnd, &wpl);
2744
2745         /* Only interested if size is changing and we're not being iconified */
2746         if (wpl.showCmd != SW_SHOWMINIMIZED
2747             && wpl.showCmd != SW_SHOWMAXIMIZED
2748             && !(wp->flags & SWP_NOSIZE))
2749           {
2750             RECT ncsize = { 0, 0, 0, 0 };
2751             int pixwidth, pixheight;
2752             AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE),
2753                                 GetMenu(hwnd) != NULL,
2754                                 GetWindowLong (hwnd, GWL_EXSTYLE));
2755
2756             round_size_to_real_char (XFRAME (mswindows_find_frame (hwnd)),
2757                                      wp->cx - (ncsize.right - ncsize.left),
2758                                      wp->cy - (ncsize.bottom - ncsize.top),
2759                                      &pixwidth, &pixheight);
2760
2761             /* Convert client sizes to window sizes */
2762             pixwidth += (ncsize.right - ncsize.left);
2763             pixheight += (ncsize.bottom - ncsize.top);
2764
2765             if (wpl.showCmd != SW_SHOWMAXIMIZED)
2766               {
2767                 /* Adjust so that the bottom or right doesn't move if it's
2768                  * the top or left that's being changed */
2769                 RECT rect;
2770                 GetWindowRect (hwnd, &rect);
2771
2772                 if (rect.left != wp->x)
2773                   wp->x += wp->cx - pixwidth;
2774                 if (rect.top != wp->y)
2775                   wp->y += wp->cy - pixheight;
2776               }
2777
2778             wp->cx = pixwidth;
2779             wp->cy = pixheight;
2780           }
2781         /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts
2782            window position if the user tries to track window too small */
2783       }
2784       goto defproc;
2785
2786     case WM_ENTERSIZEMOVE:
2787       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2788       msframe->sizing = 1;
2789       return 0;
2790
2791     case WM_EXITSIZEMOVE:
2792       msframe  = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2793       msframe->sizing = 0;
2794       /* Queue noop event */
2795       mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
2796       return 0;
2797
2798 #ifdef HAVE_SCROLLBARS
2799     case WM_VSCROLL:
2800     case WM_HSCROLL:
2801       {
2802         /* Direction of scroll is determined by scrollbar instance. */
2803         int code = (int) LOWORD(wParam);
2804         int pos = (short int) HIWORD(wParam);
2805         HWND hwndScrollBar = (HWND) lParam;
2806         struct gcpro gcpro1, gcpro2;
2807
2808         mswindows_handle_scrollbar_event (hwndScrollBar, code,  pos);
2809         GCPRO2 (emacs_event, fobj);
2810         if (UNBOUNDP(mswindows_pump_outstanding_events()))      /* Can GC */
2811           {
2812             /* Error during event pumping - cancel scroll */
2813             SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
2814           }
2815         UNGCPRO;
2816         break;
2817       }
2818
2819     case WM_MOUSEWHEEL:
2820       {
2821         int keys = LOWORD (wParam); /* Modifier key flags */
2822         int delta = (short) HIWORD (wParam); /* Wheel rotation amount */
2823         struct gcpro gcpro1, gcpro2;
2824
2825         if (mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd),
2826                                                keys, delta,
2827                                                MAKEPOINTS (lParam)))
2828           {
2829             GCPRO2 (emacs_event, fobj);
2830             mswindows_pump_outstanding_events ();       /* Can GC */
2831             UNGCPRO;
2832           }
2833         else
2834           goto defproc;
2835         break;
2836       }
2837 #endif
2838
2839 #ifdef HAVE_MENUBARS
2840     case WM_INITMENU:
2841       if (UNBOUNDP (mswindows_handle_wm_initmenu (
2842                                                   (HMENU) wParam,
2843                                                   XFRAME (mswindows_find_frame (hwnd)))))
2844         SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2845       break;
2846
2847     case WM_INITMENUPOPUP:
2848       if (!HIWORD(lParam))
2849         {
2850           if (UNBOUNDP (mswindows_handle_wm_initmenupopup (
2851                                                            (HMENU) wParam,
2852                                                            XFRAME (mswindows_find_frame (hwnd)))))
2853             SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2854         }
2855       break;
2856
2857 #endif /* HAVE_MENUBARS */
2858
2859     case WM_COMMAND:
2860       {
2861         WORD id = LOWORD (wParam);
2862         WORD nid = HIWORD (wParam);
2863         HWND cid = (HWND)lParam;
2864         frame = XFRAME (mswindows_find_frame (hwnd));
2865
2866 #ifdef HAVE_TOOLBARS
2867         if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id)))
2868           break;
2869 #endif
2870         /* widgets in a buffer only eval a callback for suitable events.*/
2871         switch (nid)
2872           {
2873           case BN_CLICKED:
2874           case EN_CHANGE:
2875           case CBN_EDITCHANGE:
2876           case CBN_SELCHANGE:
2877             if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id)))
2878               return 0;
2879           }
2880         /* menubars always must come last since the hashtables do not
2881            always exist*/
2882 #ifdef HAVE_MENUBARS
2883         if (!NILP (mswindows_handle_wm_command (frame, id)))
2884           break;
2885 #endif
2886
2887         return DefWindowProc (hwnd, message_, wParam, lParam);
2888         /* Bite me - a spurious command. This used to not be able to
2889            happen but with the introduction of widgets its now
2890            possible. */
2891       }
2892       break;
2893
2894     case WM_CTLCOLORBTN:
2895     case WM_CTLCOLORLISTBOX:
2896     case WM_CTLCOLOREDIT:
2897     case WM_CTLCOLORSTATIC:
2898     case WM_CTLCOLORSCROLLBAR:
2899       {
2900         /* if we get an opportunity to paint a widget then do so if
2901            there is an appropriate face */
2902         HWND crtlwnd = (HWND)lParam;
2903         LONG ii = GetWindowLong (crtlwnd, GWL_USERDATA);
2904         if (ii)
2905           {
2906             Lisp_Object image_instance;
2907             VOID_TO_LISP (image_instance, ii);
2908             if (IMAGE_INSTANCEP (image_instance)
2909                 &&
2910                 IMAGE_INSTANCE_TYPE_P (image_instance, IMAGE_WIDGET))
2911               {
2912                 /* set colors for the buttons */
2913                 HDC hdc = (HDC)wParam;
2914                 if (last_widget_brushed != ii)
2915                   {
2916                     if (widget_brush)
2917                       DeleteObject (widget_brush);
2918                     widget_brush = CreateSolidBrush
2919                       (COLOR_INSTANCE_MSWINDOWS_COLOR
2920                        (XCOLOR_INSTANCE
2921                         (FACE_BACKGROUND
2922                          (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2923                           XIMAGE_INSTANCE_FRAME (image_instance)))));
2924                   }
2925                 last_widget_brushed = ii;
2926                 SetTextColor
2927                   (hdc,
2928                    COLOR_INSTANCE_MSWINDOWS_COLOR
2929                    (XCOLOR_INSTANCE
2930                     (FACE_FOREGROUND
2931                      (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2932                       XIMAGE_INSTANCE_FRAME (image_instance)))));
2933                 SetBkMode (hdc, OPAQUE);
2934                 SetBkColor
2935                   (hdc,
2936                    COLOR_INSTANCE_MSWINDOWS_COLOR
2937                    (XCOLOR_INSTANCE
2938                     (FACE_BACKGROUND
2939                      (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2940                       XIMAGE_INSTANCE_FRAME (image_instance)))));
2941                 return (LRESULT)widget_brush;
2942               }
2943           }
2944       }
2945       goto defproc;
2946
2947 #ifdef HAVE_DRAGNDROP
2948     case WM_DROPFILES:  /* implementation ripped-off from event-Xt.c */
2949       {
2950         UINT filecount, i, len;
2951         POINT point;
2952         char* filename;
2953         char* fname;
2954
2955         Lisp_Object l_dndlist = Qnil, l_item = Qnil;
2956         struct gcpro gcpro1, gcpro2, gcpro3;
2957
2958         emacs_event = Fmake_event (Qnil, Qnil);
2959         event = XEVENT(emacs_event);
2960
2961         GCPRO3 (emacs_event, l_dndlist, l_item);
2962
2963         if (!DragQueryPoint ((HDROP) wParam, &point))
2964           point.x = point.y = -1;               /* outside client area */
2965
2966         event->event_type = misc_user_event;
2967         event->channel = mswindows_find_frame(hwnd);
2968         event->timestamp = GetMessageTime();
2969         event->event.misc.button = 1;           /* #### Should try harder */
2970         event->event.misc.modifiers = mswindows_modifier_state (NULL,
2971                                                                 (DWORD) -1, 0);
2972         event->event.misc.x = point.x;
2973         event->event.misc.y = point.y;
2974         event->event.misc.function = Qdragdrop_drop_dispatch;
2975
2976         filecount = DragQueryFile ((HDROP) wParam, 0xffffffff, NULL, 0);
2977         for (i=0; i<filecount; i++)
2978           {
2979             len = DragQueryFile ((HDROP) wParam, i, NULL, 0);
2980             /* The URLs that we make here aren't correct according to section
2981              * 3.10 of rfc1738 because they're missing the //<host>/ part and
2982              * because they may contain reserved characters. But that's OK -
2983              * they just need to be good enough to keep dragdrop.el happy. */
2984             fname = (char *)xmalloc (len+1);
2985             DragQueryFile ((HANDLE) wParam, i, fname, len+1);
2986
2987             /* May be a shell link aka "shortcut" - replace fname if so */
2988 #if !(defined(CYGWIN) || defined(MINGW))
2989             /* cygwin doesn't define this COM stuff */
2990             if (!stricmp (fname + strlen (fname) - 4, ".LNK"))
2991               {
2992                 IShellLink* psl;
2993
2994                 if (CoCreateInstance (&CLSID_ShellLink, NULL,
2995                                       CLSCTX_INPROC_SERVER, &IID_IShellLink, &psl) == S_OK)
2996                   {
2997                     IPersistFile* ppf;
2998
2999                     if (psl->lpVtbl->QueryInterface (psl, &IID_IPersistFile,
3000                                                      &ppf) == S_OK)
3001                       {
3002                         OLECHAR wsz[PATH_MAX];
3003                         WIN32_FIND_DATA wfd;
3004                         LPSTR resolved = (char *) xmalloc (PATH_MAX+1);
3005
3006                         MultiByteToWideChar (CP_ACP,0, fname, -1, wsz, PATH_MAX);
3007
3008                         if ((ppf->lpVtbl->Load (ppf, wsz, STGM_READ) == S_OK) &&
3009                             (psl->lpVtbl->GetPath (psl, resolved, PATH_MAX,
3010                                                    &wfd, 0)==S_OK))
3011                           {
3012                             xfree (fname);
3013                             fname = resolved;
3014                             len = strlen (fname);
3015                           }
3016
3017                         ppf->lpVtbl->Release (ppf);
3018                       }
3019
3020                     psl->lpVtbl->Release (psl);
3021                   }
3022               }
3023 #endif
3024
3025 #ifdef CYGWIN
3026             filename = xmalloc (cygwin_win32_to_posix_path_list_buf_size (fname) + 5);
3027             strcpy (filename, "file:");
3028             cygwin_win32_to_posix_path_list (fname, filename+5);
3029 #else
3030             filename = (char *)xmalloc (len+6);
3031             strcat (strcpy (filename, "file:"), fname);
3032             dostounix_filename (filename+5);
3033 #endif
3034             xfree (fname);
3035             l_item = make_string (filename, strlen (filename));
3036             l_dndlist = Fcons (l_item, l_dndlist);
3037             xfree (filename);
3038           }
3039         DragFinish ((HDROP) wParam);
3040
3041         event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist);
3042         mswindows_enqueue_dispatch_event (emacs_event);
3043         UNGCPRO;
3044       }
3045       break;
3046 #endif
3047
3048     defproc:
3049     default:
3050       return DefWindowProc (hwnd, message_, wParam, lParam);
3051     }
3052   return (0);
3053 }
3054
3055
3056 /************************************************************************/
3057 /*      keyboard, mouse & other helpers for the windows procedure       */
3058 /************************************************************************/
3059 static void
3060 mswindows_set_chord_timer (HWND hwnd)
3061 {
3062   int interval;
3063
3064   /* We get one third half system double click threshold */
3065   if (mswindows_mouse_button_tolerance <= 0)
3066     interval = GetDoubleClickTime () / 3;
3067   else
3068     interval = mswindows_mouse_button_tolerance;
3069
3070   SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0);
3071 }
3072
3073 static int
3074 mswindows_button2_near_enough (POINTS p1, POINTS p2)
3075 {
3076   int dx, dy;
3077   if (mswindows_mouse_button_max_skew_x <= 0)
3078     dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2;
3079   else
3080     dx = mswindows_mouse_button_max_skew_x;
3081
3082   if (mswindows_mouse_button_max_skew_y <= 0)
3083     dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2;
3084   else
3085     dy = mswindows_mouse_button_max_skew_y;
3086
3087   return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy;
3088 }
3089
3090 static int
3091 mswindows_current_layout_has_AltGr (void)
3092 {
3093   /* This simple caching mechanism saves 10% of CPU
3094      time when a key typed at autorepeat rate of 30 cps! */
3095   static HKL last_hkl = 0;
3096   static int last_hkl_has_AltGr;
3097   HKL current_hkl = (HKL) -1;
3098
3099   if (xGetKeyboardLayout) /* not in NT 3.5 */
3100     current_hkl = xGetKeyboardLayout (0);
3101   if (current_hkl != last_hkl)
3102     {
3103       TCHAR c;
3104       last_hkl_has_AltGr = 0;
3105       /* In this loop, we query whether a character requires
3106          AltGr to be down to generate it. If at least such one
3107          found, this means that the layout does regard AltGr */
3108       for (c = ' '; c <= 0xFFU && c != 0 && !last_hkl_has_AltGr; ++c)
3109         if (HIBYTE (VkKeyScan (c)) == 6)
3110           last_hkl_has_AltGr = 1;
3111       last_hkl = current_hkl;
3112     }
3113   return last_hkl_has_AltGr;
3114 }
3115
3116
3117 /* Returns the state of the modifier keys in the format expected by the
3118  * Lisp_Event key_data, button_data and motion_data modifiers member */
3119 static int
3120 mswindows_modifier_state (BYTE* keymap, DWORD fwKeys, int has_AltGr)
3121 {
3122   int mods = 0;
3123   int keys_is_real = 0;
3124   BYTE keymap2[256];
3125
3126   if (fwKeys == (DWORD) -1)
3127     fwKeys = mswindows_last_mouse_button_state;
3128   else
3129     {
3130       keys_is_real = 1;
3131       mswindows_last_mouse_button_state = fwKeys;
3132     }
3133
3134   if (keymap == NULL)
3135     {
3136       keymap = keymap2;
3137       GetKeyboardState (keymap);
3138       has_AltGr = mswindows_current_layout_has_AltGr ();
3139     }
3140
3141   /* #### should look at fwKeys for MK_CONTROL.  I don't understand how
3142      AltGr works. */
3143   if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80))
3144     {
3145       mods |= (keymap [VK_LMENU] & 0x80) ? XEMACS_MOD_META : 0;
3146       mods |= (keymap [VK_RCONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
3147     }
3148   else
3149     {
3150       mods |= (keymap [VK_MENU] & 0x80) ? XEMACS_MOD_META : 0;
3151       mods |= (keymap [VK_CONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0;
3152     }
3153
3154   mods |= (keys_is_real ? fwKeys & MK_SHIFT : (keymap [VK_SHIFT] & 0x80))
3155     ? XEMACS_MOD_SHIFT : 0;
3156   mods |= fwKeys & MK_LBUTTON ? XEMACS_MOD_BUTTON1 : 0;
3157   mods |= fwKeys & MK_MBUTTON ? XEMACS_MOD_BUTTON2 : 0;
3158   mods |= fwKeys & MK_RBUTTON ? XEMACS_MOD_BUTTON3 : 0;
3159
3160   return mods;
3161 }
3162
3163 /*
3164  * Translate a mswindows virtual key to a keysym.
3165  * Only returns non-Qnil for keys that don't generate WM_CHAR messages
3166  * or whose ASCII codes (like space) xemacs doesn't like.
3167  */
3168 Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
3169                                            int extendedp)
3170 {
3171   if (extendedp)        /* Keys not present on a 82 key keyboard */
3172     {
3173       switch (mswindows_key)
3174         {
3175         case VK_CANCEL:         return KEYSYM ("pause");
3176         case VK_RETURN:         return KEYSYM ("kp-enter");
3177         case VK_PRIOR:          return KEYSYM ("prior");
3178         case VK_NEXT:           return KEYSYM ("next");
3179         case VK_END:            return KEYSYM ("end");
3180         case VK_HOME:           return KEYSYM ("home");
3181         case VK_LEFT:           return KEYSYM ("left");
3182         case VK_UP:             return KEYSYM ("up");
3183         case VK_RIGHT:          return KEYSYM ("right");
3184         case VK_DOWN:           return KEYSYM ("down");
3185         case VK_INSERT:         return KEYSYM ("insert");
3186         case VK_DELETE:         return QKdelete;
3187 #if 0   /* FSF Emacs allows these to return configurable syms/mods */
3188         case VK_LWIN            return KEYSYM ("");
3189         case VK_RWIN            return KEYSYM ("");
3190 #endif
3191         case VK_APPS:           return KEYSYM ("menu");
3192         }
3193     }
3194   else
3195     {
3196       switch (mswindows_key)
3197         {
3198         case VK_BACK:           return QKbackspace;
3199         case VK_TAB:            return QKtab;
3200         case '\n':              return QKlinefeed;
3201         case VK_CLEAR:          return KEYSYM ("clear");
3202         case VK_RETURN:         return QKreturn;
3203         case VK_PAUSE:          return KEYSYM ("pause");
3204         case VK_ESCAPE:         return QKescape;
3205         case VK_SPACE:          return QKspace;
3206         case VK_PRIOR:          return KEYSYM ("kp-prior");
3207         case VK_NEXT:           return KEYSYM ("kp-next");
3208         case VK_END:            return KEYSYM ("kp-end");
3209         case VK_HOME:           return KEYSYM ("kp-home");
3210         case VK_LEFT:           return KEYSYM ("kp-left");
3211         case VK_UP:             return KEYSYM ("kp-up");
3212         case VK_RIGHT:          return KEYSYM ("kp-right");
3213         case VK_DOWN:           return KEYSYM ("kp-down");
3214         case VK_SELECT:         return KEYSYM ("select");
3215         case VK_PRINT:          return KEYSYM ("print");
3216         case VK_EXECUTE:        return KEYSYM ("execute");
3217         case VK_SNAPSHOT:       return KEYSYM ("print");
3218         case VK_INSERT:         return KEYSYM ("kp-insert");
3219         case VK_DELETE:         return KEYSYM ("kp-delete");
3220         case VK_HELP:           return KEYSYM ("help");
3221         case VK_NUMPAD0:        return KEYSYM ("kp-0");
3222         case VK_NUMPAD1:        return KEYSYM ("kp-1");
3223         case VK_NUMPAD2:        return KEYSYM ("kp-2");
3224         case VK_NUMPAD3:        return KEYSYM ("kp-3");
3225         case VK_NUMPAD4:        return KEYSYM ("kp-4");
3226         case VK_NUMPAD5:        return KEYSYM ("kp-5");
3227         case VK_NUMPAD6:        return KEYSYM ("kp-6");
3228         case VK_NUMPAD7:        return KEYSYM ("kp-7");
3229         case VK_NUMPAD8:        return KEYSYM ("kp-8");
3230         case VK_NUMPAD9:        return KEYSYM ("kp-9");
3231         case VK_MULTIPLY:       return KEYSYM ("kp-multiply");
3232         case VK_ADD:            return KEYSYM ("kp-add");
3233         case VK_SEPARATOR:      return KEYSYM ("kp-separator");
3234         case VK_SUBTRACT:       return KEYSYM ("kp-subtract");
3235         case VK_DECIMAL:        return KEYSYM ("kp-decimal");
3236         case VK_DIVIDE:         return KEYSYM ("kp-divide");
3237         case VK_F1:             return KEYSYM ("f1");
3238         case VK_F2:             return KEYSYM ("f2");
3239         case VK_F3:             return KEYSYM ("f3");
3240         case VK_F4:             return KEYSYM ("f4");
3241         case VK_F5:             return KEYSYM ("f5");
3242         case VK_F6:             return KEYSYM ("f6");
3243         case VK_F7:             return KEYSYM ("f7");
3244         case VK_F8:             return KEYSYM ("f8");
3245         case VK_F9:             return KEYSYM ("f9");
3246         case VK_F10:            return KEYSYM ("f10");
3247         case VK_F11:            return KEYSYM ("f11");
3248         case VK_F12:            return KEYSYM ("f12");
3249         case VK_F13:            return KEYSYM ("f13");
3250         case VK_F14:            return KEYSYM ("f14");
3251         case VK_F15:            return KEYSYM ("f15");
3252         case VK_F16:            return KEYSYM ("f16");
3253         case VK_F17:            return KEYSYM ("f17");
3254         case VK_F18:            return KEYSYM ("f18");
3255         case VK_F19:            return KEYSYM ("f19");
3256         case VK_F20:            return KEYSYM ("f20");
3257         case VK_F21:            return KEYSYM ("f21");
3258         case VK_F22:            return KEYSYM ("f22");
3259         case VK_F23:            return KEYSYM ("f23");
3260         case VK_F24:            return KEYSYM ("f24");
3261         }
3262     }
3263   return Qnil;
3264 }
3265
3266 /*
3267  * Find the console that matches the supplied mswindows window handle
3268  */
3269 Lisp_Object
3270 mswindows_find_console (HWND hwnd)
3271 {
3272   /* We only support one console */
3273   return XCAR (Vconsole_list);
3274 }
3275
3276 /*
3277  * Find the frame that matches the supplied mswindows window handle
3278  */
3279 static Lisp_Object
3280 mswindows_find_frame (HWND hwnd)
3281 {
3282   LONG l = GetWindowLong (hwnd, XWL_FRAMEOBJ);
3283   Lisp_Object f;
3284   if (l == 0)
3285     {
3286       /* We are in progress of frame creation. Return the frame
3287          being created, as it still not remembered in the window
3288          extra storage. */
3289       assert (!NILP (Vmswindows_frame_being_created));
3290       return Vmswindows_frame_being_created;
3291     }
3292   VOID_TO_LISP (f, l);
3293   return f;
3294 }
3295
3296 \f
3297 /************************************************************************/
3298 /*                            methods                                   */
3299 /************************************************************************/
3300
3301 static int
3302 emacs_mswindows_add_timeout (EMACS_TIME thyme)
3303 {
3304   int milliseconds;
3305   EMACS_TIME current_time;
3306   EMACS_GET_TIME (current_time);
3307   EMACS_SUB_TIME (thyme, thyme, current_time);
3308   milliseconds = EMACS_SECS (thyme) * 1000 +
3309     (EMACS_USECS (thyme) + 500) / 1000;
3310   if (milliseconds < 1)
3311     milliseconds = 1;
3312   ++mswindows_pending_timers_count;
3313   return SetTimer (NULL, 0, milliseconds,
3314                    (TIMERPROC) mswindows_wm_timer_callback);
3315 }
3316
3317 static void
3318 emacs_mswindows_remove_timeout (int id)
3319 {
3320   Lisp_Event match_against;
3321   Lisp_Object emacs_event;
3322
3323   if (KillTimer (NULL, id))
3324     --mswindows_pending_timers_count;
3325
3326   /* If there is a dispatch event generated by this
3327      timeout in the queue, we have to remove it too. */
3328   match_against.event_type = timeout_event;
3329   match_against.event.timeout.interval_id = id;
3330   emacs_event = mswindows_cancel_dispatch_event (&match_against);
3331   if (!NILP (emacs_event))
3332     Fdeallocate_event(emacs_event);
3333 }
3334
3335 /* If `user_p' is false, then return whether there are any win32, timeout,
3336  * or subprocess events pending (that is, whether
3337  * emacs_mswindows_next_event() would return immediately without blocking).
3338  *
3339  * if `user_p' is true, then return whether there are any *user generated*
3340  * events available (that is, whether there are keyboard or mouse-click
3341  * events ready to be read).  This also implies that
3342  * emacs_mswindows_next_event() would not block.
3343  */
3344 static int
3345 emacs_mswindows_event_pending_p (int user_p)
3346 {
3347   mswindows_need_event (0);
3348   return (!NILP (mswindows_u_dispatch_event_queue)
3349           || (!user_p && !NILP (mswindows_s_dispatch_event_queue)));
3350 }
3351
3352 /*
3353  * Return the next event
3354  */
3355 static void
3356 emacs_mswindows_next_event (Lisp_Event *emacs_event)
3357 {
3358   Lisp_Object event, event2;
3359
3360   mswindows_need_event (1);
3361
3362   event = mswindows_dequeue_dispatch_event ();
3363   XSETEVENT (event2, emacs_event);
3364   Fcopy_event (event, event2);
3365   Fdeallocate_event (event);
3366 }
3367
3368 /*
3369  * Handle a magic event off the dispatch queue.
3370  */
3371 static void
3372 emacs_mswindows_handle_magic_event (Lisp_Event *emacs_event)
3373 {
3374   switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event))
3375     {
3376     case XM_BUMPQUEUE:
3377       break;
3378
3379     case WM_PAINT:
3380       {
3381         struct frame *f = XFRAME (EVENT_CHANNEL (emacs_event));
3382         mswindows_handle_paint (f);
3383         (FRAME_MSWINDOWS_DATA (f))->paint_pending = 0;
3384       }
3385       break;
3386
3387     case WM_SETFOCUS:
3388     case WM_KILLFOCUS:
3389       {
3390         Lisp_Object frame = EVENT_CHANNEL (emacs_event);
3391         struct frame *f = XFRAME (frame);
3392         int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS);
3393         Lisp_Object conser;
3394         struct gcpro gcpro1;
3395
3396         /* On focus change, clear all memory of sticky modifiers
3397            to avoid non-intuitive behavior. */
3398         clear_sticky_modifiers ();
3399
3400         conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil));
3401         GCPRO1 (conser);
3402         emacs_handle_focus_change_preliminary (conser);
3403         /* Under X the stuff up to here is done in the X event handler.
3404            I Don't know why */
3405         emacs_handle_focus_change_final (conser);
3406         UNGCPRO;
3407
3408       }
3409       break;
3410
3411     case XM_MAPFRAME:
3412     case XM_UNMAPFRAME:
3413       {
3414         Lisp_Object frame = EVENT_CHANNEL (emacs_event);
3415         va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)
3416                                == XM_MAPFRAME ?
3417                                Qmap_frame_hook : Qunmap_frame_hook,
3418                                1, frame);
3419       }
3420       break;
3421
3422       /* #### What about Enter & Leave */
3423 #if 0
3424       va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook :
3425                              Qmouse_leave_frame_hook, 1, frame);
3426 #endif
3427
3428     default:
3429       assert(0);
3430     }
3431 }
3432
3433 #ifndef HAVE_MSG_SELECT
3434 static HANDLE
3435 get_process_input_waitable (Lisp_Process *process)
3436 {
3437   Lisp_Object instr, outstr, p;
3438   XSETPROCESS (p, process);
3439   get_process_streams (process, &instr, &outstr);
3440   assert (!NILP (instr));
3441 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3442   return (network_connection_p (p)
3443           ? get_winsock_stream_waitable (XLSTREAM (instr))
3444           : get_ntpipe_input_stream_waitable (XLSTREAM (instr)));
3445 #else
3446   return get_ntpipe_input_stream_waitable (XLSTREAM (instr));
3447 #endif
3448 }
3449
3450 static void
3451 emacs_mswindows_select_process (Lisp_Process *process)
3452 {
3453   HANDLE hev = get_process_input_waitable (process);
3454
3455   if (!add_waitable_handle (hev))
3456     error ("Too many active processes");
3457
3458 #ifdef HAVE_WIN32_PROCESSES
3459   {
3460     Lisp_Object p;
3461     XSETPROCESS (p, process);
3462     if (!network_connection_p (p))
3463       {
3464         HANDLE hprocess = get_nt_process_handle (process);
3465         if (!add_waitable_handle (hprocess))
3466           {
3467             remove_waitable_handle (hev);
3468             error ("Too many active processes");
3469           }
3470       }
3471   }
3472 #endif
3473 }
3474
3475 static void
3476 emacs_mswindows_unselect_process (Lisp_Process *process)
3477 {
3478   /* Process handle is removed in the event loop as soon
3479      as it is signaled, so don't bother here about it */
3480   HANDLE hev = get_process_input_waitable (process);
3481   remove_waitable_handle (hev);
3482 }
3483 #endif /* HAVE_MSG_SELECT */
3484
3485 static void
3486 emacs_mswindows_select_console (struct console *con)
3487 {
3488 #ifdef HAVE_MSG_SELECT
3489   if (CONSOLE_MSWINDOWS_P (con))
3490     return; /* mswindows consoles are automatically selected */
3491
3492   event_stream_unixoid_select_console (con);
3493 #endif
3494 }
3495
3496 static void
3497 emacs_mswindows_unselect_console (struct console *con)
3498 {
3499 #ifdef HAVE_MSG_SELECT
3500   if (CONSOLE_MSWINDOWS_P (con))
3501     return; /* mswindows consoles are automatically selected */
3502
3503   event_stream_unixoid_unselect_console (con);
3504 #endif
3505 }
3506
3507 static void
3508 emacs_mswindows_quit_p (void)
3509 {
3510   /* Quit cannot happen in modal loop: all program
3511      input is dedicated to Windows. */
3512   if (mswindows_in_modal_loop)
3513     return;
3514
3515   mswindows_quit_chars_count = 0;
3516   /* Drain windows queue.  This sets up number of quit characters in
3517      the queue. */
3518   mswindows_drain_windows_queue ();
3519
3520   if (mswindows_quit_chars_count > 0)
3521     {
3522       /* Yes there's a hidden one... Throw it away */
3523       Lisp_Event match_against;
3524       Lisp_Object emacs_event;
3525       int critical_p = 0;
3526
3527       match_against.event_type = key_press_event;
3528       match_against.event.key.modifiers = FAKE_MOD_QUIT;
3529
3530       while (mswindows_quit_chars_count > 0)
3531         {
3532           emacs_event = mswindows_cancel_dispatch_event (&match_against);
3533           assert (!NILP (emacs_event));
3534
3535           if (XEVENT (emacs_event)->event.key.modifiers &
3536               FAKE_MOD_QUIT_CRITICAL)
3537             critical_p = 1;
3538
3539           Fdeallocate_event (emacs_event);
3540           mswindows_quit_chars_count--;
3541         }
3542
3543       Vquit_flag = critical_p ? Qcritical : Qt;
3544     }
3545 }
3546
3547 USID
3548 emacs_mswindows_create_stream_pair (void* inhandle, void* outhandle,
3549                                     Lisp_Object* instream,
3550                                     Lisp_Object* outstream,
3551                                     int flags)
3552 {
3553   /* Handles for streams */
3554   HANDLE hin, hout;
3555   /* fds. These just stored along with the streams, and are closed in
3556      delete stream pair method, because we need to handle fake unices
3557      here. */
3558   int fdi, fdo;
3559
3560   /* Decode inhandle and outhandle. Their meaning depends on
3561      the process implementation being used. */
3562 #if defined (HAVE_WIN32_PROCESSES)
3563   /* We're passed in Windows handles. That's what we like most... */
3564   hin = (HANDLE) inhandle;
3565   hout = (HANDLE) outhandle;
3566   fdi = fdo = -1;
3567 #elif defined (HAVE_UNIX_PROCESSES)
3568   /* We are passed UNIX fds. This must be Cygwin.
3569      Fetch os handles */
3570   hin = inhandle >= 0 ? (HANDLE)get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE;
3571   hout = outhandle >= 0 ? (HANDLE)get_osfhandle ((int)outhandle) : INVALID_HANDLE_VALUE;
3572   fdi=(int)inhandle;
3573   fdo=(int)outhandle;
3574 #else
3575 #error "So, WHICH kind of processes do you want?"
3576 #endif
3577
3578   *instream = (hin == INVALID_HANDLE_VALUE
3579                ? Qnil
3580 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
3581                : flags & STREAM_NETWORK_CONNECTION
3582                ? make_winsock_input_stream ((SOCKET)hin, fdi)
3583 #endif
3584                : make_ntpipe_input_stream (hin, fdi));
3585
3586 #ifdef HAVE_WIN32_PROCESSES
3587   *outstream = (hout == INVALID_HANDLE_VALUE
3588                 ? Qnil
3589 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
3590                 : flags & STREAM_NETWORK_CONNECTION
3591                 ? make_winsock_output_stream ((SOCKET)hout, fdo)
3592 #endif
3593                 : make_ntpipe_output_stream (hout, fdo));
3594 #elif defined (HAVE_UNIX_PROCESSES)
3595   *outstream = (fdo >= 0
3596                 ? make_filedesc_output_stream (fdo, 0, -1, LSTR_BLOCKED_OK)
3597                 : Qnil);
3598
3599 #if defined(HAVE_UNIX_PROCESSES) && defined(HAVE_PTYS)
3600   /* FLAGS is process->pty_flag for UNIX_PROCESSES */
3601   if ((flags & STREAM_PTY_FLUSHING) && fdo >= 0)
3602     {
3603       Bufbyte eof_char = get_eof_char (fdo);
3604       int pty_max_bytes = get_pty_max_bytes (fdo);
3605       filedesc_stream_set_pty_flushing (XLSTREAM(*outstream), pty_max_bytes, eof_char);
3606     }
3607 #endif
3608 #endif
3609
3610   return (NILP (*instream)
3611           ? USID_ERROR
3612 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3613           : flags & STREAM_NETWORK_CONNECTION
3614           ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream)))
3615 #endif
3616           : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (*instream))));
3617 }
3618
3619 USID
3620 emacs_mswindows_delete_stream_pair (Lisp_Object instream,
3621                                     Lisp_Object outstream)
3622 {
3623   /* Oh nothing special here for Win32 at all */
3624 #if defined (HAVE_UNIX_PROCESSES)
3625   int in = (NILP(instream)
3626             ? -1
3627 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3628             : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
3629             ? get_winsock_stream_param (XLSTREAM (instream))
3630 #endif
3631             : get_ntpipe_input_stream_param (XLSTREAM (instream)));
3632   int out = (NILP(outstream) ? -1
3633              : filedesc_stream_fd (XLSTREAM (outstream)));
3634
3635   if (in >= 0)
3636     close (in);
3637   if (out != in && out >= 0)
3638     close (out);
3639 #endif
3640
3641   return (NILP (instream)
3642           ? USID_DONTHASH
3643 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3644           : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
3645           ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream)))
3646 #endif
3647           : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream))));
3648 }
3649
3650 static int
3651 emacs_mswindows_current_event_timestamp (struct console *c)
3652 {
3653   return GetTickCount ();
3654 }
3655
3656 #ifndef HAVE_X_WINDOWS
3657 /* This is called from GC when a process object is about to be freed.
3658    If we've still got pointers to it in this file, we're gonna lose hard.
3659  */
3660 void
3661 debug_process_finalization (Lisp_Process *p)
3662 {
3663 #if 0 /* #### */
3664   Lisp_Object instr, outstr;
3665
3666   get_process_streams (p, &instr, &outstr);
3667   /* if it still has fds, then it hasn't been killed yet. */
3668   assert (NILP(instr));
3669   assert (NILP(outstr));
3670
3671   /* #### More checks here */
3672 #endif
3673 }
3674 #endif
3675
3676 #ifdef DEBUG_XEMACS
3677
3678 struct mswin_message_debug
3679 {
3680   int mess;
3681   char *string;
3682 };
3683
3684 #define FROB(val) { val, #val, },
3685
3686 struct mswin_message_debug debug_mswin_messages[] =
3687 {
3688 FROB (WM_NULL)
3689 FROB (WM_CREATE)
3690 FROB (WM_DESTROY)
3691 FROB (WM_MOVE)
3692 FROB (WM_SIZE)
3693
3694 FROB (WM_ACTIVATE)
3695
3696 FROB (WM_SETFOCUS)
3697 FROB (WM_KILLFOCUS)
3698 FROB (WM_ENABLE)
3699 FROB (WM_SETREDRAW)
3700 FROB (WM_SETTEXT)
3701 FROB (WM_GETTEXT)
3702 FROB (WM_GETTEXTLENGTH)
3703 FROB (WM_PAINT)
3704 FROB (WM_CLOSE)
3705 FROB (WM_QUERYENDSESSION)
3706 FROB (WM_QUIT)
3707 FROB (WM_QUERYOPEN)
3708 FROB (WM_ERASEBKGND)
3709 FROB (WM_SYSCOLORCHANGE)
3710 FROB (WM_ENDSESSION)
3711 FROB (WM_SHOWWINDOW)
3712 FROB (WM_WININICHANGE)
3713 #if(WINVER >= 0x0400)
3714 FROB (WM_SETTINGCHANGE)
3715 #endif /* WINVER >= 0x0400 */
3716
3717 FROB (WM_DEVMODECHANGE)
3718 FROB (WM_ACTIVATEAPP)
3719 FROB (WM_FONTCHANGE)
3720 FROB (WM_TIMECHANGE)
3721 FROB (WM_CANCELMODE)
3722 FROB (WM_SETCURSOR)
3723 FROB (WM_MOUSEACTIVATE)
3724 FROB (WM_CHILDACTIVATE)
3725 FROB (WM_QUEUESYNC)
3726
3727 FROB (WM_GETMINMAXINFO)
3728
3729 FROB (WM_PAINTICON)
3730 FROB (WM_ICONERASEBKGND)
3731 FROB (WM_NEXTDLGCTL)
3732 FROB (WM_SPOOLERSTATUS)
3733 FROB (WM_DRAWITEM)
3734 FROB (WM_MEASUREITEM)
3735 FROB (WM_DELETEITEM)
3736 FROB (WM_VKEYTOITEM)
3737 FROB (WM_CHARTOITEM)
3738 FROB (WM_SETFONT)
3739 FROB (WM_GETFONT)
3740 FROB (WM_SETHOTKEY)
3741 FROB (WM_GETHOTKEY)
3742 FROB (WM_QUERYDRAGICON)
3743 FROB (WM_COMPAREITEM)
3744 #if(WINVER >= 0x0500)
3745 FROB (WM_GETOBJECT)
3746 #endif /* WINVER >= 0x0500 */
3747 FROB (WM_COMPACTING)
3748 FROB (WM_COMMNOTIFY)
3749 FROB (WM_WINDOWPOSCHANGING)
3750 FROB (WM_WINDOWPOSCHANGED)
3751
3752 FROB (WM_POWER)
3753
3754 FROB (WM_COPYDATA)
3755 FROB (WM_CANCELJOURNAL)
3756
3757 #if(WINVER >= 0x0400)
3758 FROB (WM_NOTIFY)
3759 FROB (WM_INPUTLANGCHANGEREQUEST)
3760 FROB (WM_INPUTLANGCHANGE)
3761 FROB (WM_TCARD)
3762 FROB (WM_HELP)
3763 FROB (WM_USERCHANGED)
3764 FROB (WM_NOTIFYFORMAT)
3765
3766 FROB (WM_CONTEXTMENU)
3767 FROB (WM_STYLECHANGING)
3768 FROB (WM_STYLECHANGED)
3769 FROB (WM_DISPLAYCHANGE)
3770 FROB (WM_GETICON)
3771 FROB (WM_SETICON)
3772 #endif /* WINVER >= 0x0400 */
3773
3774 FROB (WM_NCCREATE)
3775 FROB (WM_NCDESTROY)
3776 FROB (WM_NCCALCSIZE)
3777 FROB (WM_NCHITTEST)
3778 FROB (WM_NCPAINT)
3779 FROB (WM_NCACTIVATE)
3780 FROB (WM_GETDLGCODE)
3781 #ifdef WM_SYNCPAINT /* not in VC 5 */
3782 FROB (WM_SYNCPAINT)
3783 #endif /* WM_SYNCPAINT */
3784 FROB (WM_NCMOUSEMOVE)
3785 FROB (WM_NCLBUTTONDOWN)
3786 FROB (WM_NCLBUTTONUP)
3787 FROB (WM_NCLBUTTONDBLCLK)
3788 FROB (WM_NCRBUTTONDOWN)
3789 FROB (WM_NCRBUTTONUP)
3790 FROB (WM_NCRBUTTONDBLCLK)
3791 FROB (WM_NCMBUTTONDOWN)
3792 FROB (WM_NCMBUTTONUP)
3793 FROB (WM_NCMBUTTONDBLCLK)
3794
3795 /* FROB (WM_KEYFIRST) */
3796 FROB (WM_KEYDOWN)
3797 FROB (WM_KEYUP)
3798 FROB (WM_CHAR)
3799 FROB (WM_DEADCHAR)
3800 FROB (WM_SYSKEYDOWN)
3801 FROB (WM_SYSKEYUP)
3802 FROB (WM_SYSCHAR)
3803 FROB (WM_SYSDEADCHAR)
3804 FROB (WM_KEYLAST)
3805
3806 #if(WINVER >= 0x0400) && defined (WM_IME_STARTCOMPOSITION)
3807 FROB (WM_IME_STARTCOMPOSITION)
3808 FROB (WM_IME_ENDCOMPOSITION)
3809 FROB (WM_IME_COMPOSITION)
3810 FROB (WM_IME_KEYLAST)
3811 #endif /* WINVER >= 0x0400 && defined (WM_IME_STARTCOMPOSITION) */
3812
3813 FROB (WM_INITDIALOG)
3814 FROB (WM_COMMAND)
3815 FROB (WM_SYSCOMMAND)
3816 FROB (WM_TIMER)
3817 FROB (WM_HSCROLL)
3818 FROB (WM_VSCROLL)
3819 FROB (WM_INITMENU)
3820 FROB (WM_INITMENUPOPUP)
3821 FROB (WM_MENUSELECT)
3822 FROB (WM_MENUCHAR)
3823 FROB (WM_ENTERIDLE)
3824 #if(WINVER >= 0x0500)
3825 FROB (WM_MENURBUTTONUP)
3826 FROB (WM_MENUDRAG)
3827 FROB (WM_MENUGETOBJECT)
3828 FROB (WM_UNINITMENUPOPUP)
3829 FROB (WM_MENUCOMMAND)
3830 #endif /* WINVER >= 0x0500 */
3831
3832
3833 FROB (WM_CTLCOLORMSGBOX)
3834 FROB (WM_CTLCOLOREDIT)
3835 FROB (WM_CTLCOLORLISTBOX)
3836 FROB (WM_CTLCOLORBTN)
3837 FROB (WM_CTLCOLORDLG)
3838 FROB (WM_CTLCOLORSCROLLBAR)
3839 FROB (WM_CTLCOLORSTATIC)
3840
3841
3842 /* FROB (WM_MOUSEFIRST) */
3843 FROB (WM_MOUSEMOVE)
3844 FROB (WM_LBUTTONDOWN)
3845 FROB (WM_LBUTTONUP)
3846 FROB (WM_LBUTTONDBLCLK)
3847 FROB (WM_RBUTTONDOWN)
3848 FROB (WM_RBUTTONUP)
3849 FROB (WM_RBUTTONDBLCLK)
3850 FROB (WM_MBUTTONDOWN)
3851 FROB (WM_MBUTTONUP)
3852 FROB (WM_MBUTTONDBLCLK)
3853
3854 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
3855 FROB (WM_MOUSEWHEEL)
3856 FROB (WM_MOUSELAST)
3857 #else
3858 FROB (WM_MOUSELAST)
3859 #endif /* if (_WIN32_WINNT < 0x0400) */
3860
3861 FROB (WM_PARENTNOTIFY)
3862 FROB (WM_ENTERMENULOOP)
3863 FROB (WM_EXITMENULOOP)
3864
3865 #if(WINVER >= 0x0400)
3866 FROB (WM_NEXTMENU)
3867
3868 FROB (WM_SIZING)
3869 FROB (WM_CAPTURECHANGED)
3870 FROB (WM_MOVING)
3871 FROB (WM_POWERBROADCAST)
3872
3873 FROB (WM_DEVICECHANGE)
3874
3875 #endif /* WINVER >= 0x0400 */
3876
3877 FROB (WM_MDICREATE)
3878 FROB (WM_MDIDESTROY)
3879 FROB (WM_MDIACTIVATE)
3880 FROB (WM_MDIRESTORE)
3881 FROB (WM_MDINEXT)
3882 FROB (WM_MDIMAXIMIZE)
3883 FROB (WM_MDITILE)
3884 FROB (WM_MDICASCADE)
3885 FROB (WM_MDIICONARRANGE)
3886 FROB (WM_MDIGETACTIVE)
3887
3888
3889 FROB (WM_MDISETMENU)
3890 FROB (WM_ENTERSIZEMOVE)
3891 FROB (WM_EXITSIZEMOVE)
3892 FROB (WM_DROPFILES)
3893 FROB (WM_MDIREFRESHMENU)
3894
3895 #ifdef WM_IME_SETCONTEXT /* not in Cygwin? */
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 #endif /* WM_IME_SETCONTEXT */
3914
3915 #if(_WIN32_WINNT >= 0x0400)
3916 FROB (WM_MOUSEHOVER)
3917 FROB (WM_MOUSELEAVE)
3918 #endif /* _WIN32_WINNT >= 0x0400 */
3919
3920 FROB (WM_CUT)
3921 FROB (WM_COPY)
3922 FROB (WM_PASTE)
3923 FROB (WM_CLEAR)
3924 FROB (WM_UNDO)
3925 FROB (WM_RENDERFORMAT)
3926 FROB (WM_RENDERALLFORMATS)
3927 FROB (WM_DESTROYCLIPBOARD)
3928 FROB (WM_DRAWCLIPBOARD)
3929 FROB (WM_PAINTCLIPBOARD)
3930 FROB (WM_VSCROLLCLIPBOARD)
3931 FROB (WM_SIZECLIPBOARD)
3932 FROB (WM_ASKCBFORMATNAME)
3933 FROB (WM_CHANGECBCHAIN)
3934 FROB (WM_HSCROLLCLIPBOARD)
3935 FROB (WM_QUERYNEWPALETTE)
3936 FROB (WM_PALETTEISCHANGING)
3937 FROB (WM_PALETTECHANGED)
3938 FROB (WM_HOTKEY)
3939
3940 #if(WINVER >= 0x0400)
3941 FROB (WM_PRINT)
3942 FROB (WM_PRINTCLIENT)
3943
3944 FROB (WM_HANDHELDFIRST)
3945 FROB (WM_HANDHELDLAST)
3946
3947 FROB (WM_AFXFIRST)
3948 FROB (WM_AFXLAST)
3949 #endif /* WINVER >= 0x0400 */
3950
3951 FROB (WM_PENWINFIRST)
3952 FROB (WM_PENWINLAST)
3953 };
3954
3955 #undef FROB
3956
3957 static void
3958 debug_output_mswin_message (HWND hwnd, UINT message_, WPARAM wParam,
3959                             LPARAM lParam)
3960 {
3961   Lisp_Object frame = mswindows_find_frame (hwnd);
3962   int i;
3963   char *str = 0;
3964   /* struct mswin_message_debug *i_hate_cranking_out_code_like_this; */
3965
3966   for (i = 0; i < countof (debug_mswin_messages); i++)
3967     {
3968       if (debug_mswin_messages[i].mess == message_)
3969         {
3970           str = debug_mswin_messages[i].string;
3971           break;
3972         }
3973     }
3974
3975   if (str)
3976     stderr_out ("%s", str);
3977   else
3978     stderr_out ("%x", message_);
3979
3980   if (debug_mswindows_events > 1)
3981     {
3982       stderr_out (" wparam=%d lparam=%d hwnd=%x frame: ",
3983                   wParam, (int) lParam, (unsigned int) hwnd);
3984       debug_print (frame);
3985     }
3986   else
3987     stderr_out ("\n");
3988 }
3989
3990 #endif /* DEBUG_XEMACS */
3991
3992 /************************************************************************/
3993 /*                            initialization                            */
3994 /************************************************************************/
3995
3996 void
3997 reinit_vars_of_event_mswindows (void)
3998 {
3999   mswindows_in_modal_loop = 0;
4000   mswindows_pending_timers_count = 0;
4001
4002   mswindows_event_stream = xnew (struct event_stream);
4003
4004   mswindows_event_stream->event_pending_p       = emacs_mswindows_event_pending_p;
4005   mswindows_event_stream->force_event_pending = 0;
4006   mswindows_event_stream->next_event_cb         = emacs_mswindows_next_event;
4007   mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event;
4008   mswindows_event_stream->add_timeout_cb        = emacs_mswindows_add_timeout;
4009   mswindows_event_stream->remove_timeout_cb     = emacs_mswindows_remove_timeout;
4010   mswindows_event_stream->quit_p_cb             = emacs_mswindows_quit_p;
4011   mswindows_event_stream->select_console_cb     = emacs_mswindows_select_console;
4012   mswindows_event_stream->unselect_console_cb   = emacs_mswindows_unselect_console;
4013 #ifdef HAVE_MSG_SELECT
4014   mswindows_event_stream->select_process_cb     =
4015     (void (*)(Lisp_Process*))event_stream_unixoid_select_process;
4016   mswindows_event_stream->unselect_process_cb   =
4017     (void (*)(Lisp_Process*))event_stream_unixoid_unselect_process;
4018   mswindows_event_stream->create_stream_pair_cb = event_stream_unixoid_create_stream_pair;
4019   mswindows_event_stream->delete_stream_pair_cb = event_stream_unixoid_delete_stream_pair;
4020 #else
4021   mswindows_event_stream->select_process_cb     = emacs_mswindows_select_process;
4022   mswindows_event_stream->unselect_process_cb   = emacs_mswindows_unselect_process;
4023   mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair;
4024   mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair;
4025 #endif
4026   mswindows_event_stream->current_event_timestamp_cb =
4027     emacs_mswindows_current_event_timestamp;
4028 }
4029
4030 void
4031 vars_of_event_mswindows (void)
4032 {
4033   reinit_vars_of_event_mswindows ();
4034
4035   mswindows_u_dispatch_event_queue = Qnil;
4036   staticpro (&mswindows_u_dispatch_event_queue);
4037   mswindows_u_dispatch_event_queue_tail = Qnil;
4038   dump_add_root_object (&mswindows_u_dispatch_event_queue_tail);
4039
4040   mswindows_s_dispatch_event_queue = Qnil;
4041   staticpro (&mswindows_s_dispatch_event_queue);
4042   mswindows_s_dispatch_event_queue_tail = Qnil;
4043   dump_add_root_object (&mswindows_s_dispatch_event_queue_tail);
4044
4045   mswindows_error_caught_in_modal_loop = Qnil;
4046   staticpro (&mswindows_error_caught_in_modal_loop);
4047
4048
4049 #ifdef DEBUG_XEMACS
4050   DEFVAR_INT ("debug-mswindows-events", &debug_mswindows_events /*
4051 If non-zero, display debug information about Windows messages that XEmacs sees.
4052 Information is displayed in a console window.  Currently defined values are:
4053
4054 1 == non-verbose output (just the message name)
4055 2 == verbose output (all parameters)
4056 3 == even more verbose output (extra debugging info)
4057 */ );
4058   debug_mswindows_events = 0;
4059 #endif
4060
4061   DEFVAR_BOOL ("mswindows-alt-by-itself-activates-menu",
4062                &mswindows_alt_by_itself_activates_menu /*
4063 *Controls whether pressing and releasing the Alt key activates the menubar.
4064 This applies only if no intervening key was pressed.  See also
4065 `menu-accelerator-enabled', which is probably the behavior you actually want.
4066 Default is t.
4067 */ );
4068
4069   DEFVAR_BOOL ("mswindows-dynamic-frame-resize",
4070                &mswindows_dynamic_frame_resize /*
4071 *Controls redrawing frame contents during mouse-drag or keyboard resize
4072 operation. When non-nil, the frame is redrawn while being resized. When
4073 nil, frame is not redrawn, and exposed areas are filled with default
4074 MDI application background color. Note that this option only has effect
4075 if "Show window contents while dragging" is on in system Display/Plus!
4076 settings.
4077 Default is t on fast machines, nil on slow.
4078 */ );
4079
4080   DEFVAR_INT ("mswindows-mouse-button-tolerance",
4081               &mswindows_mouse_button_tolerance /*
4082 *Analogue of double click interval for faking middle mouse events.
4083 The value is the minimum time in milliseconds that must elapse between
4084 left/right button down events before they are considered distinct events.
4085 If both mouse buttons are depressed within this interval, a middle mouse
4086 button down event is generated instead.
4087 If negative or zero, currently set system default is used instead.
4088 */ );
4089
4090   DEFVAR_INT ("mswindows-num-mouse-buttons", &mswindows_num_mouse_buttons /*
4091 Number of physical mouse buttons.
4092 */ );
4093
4094   DEFVAR_INT ("mswindows-mouse-button-max-skew-x",
4095               &mswindows_mouse_button_max_skew_x /*
4096 *Maximum horizontal distance in pixels between points in which left and
4097 right button clicks occurred for them to be translated into single
4098 middle button event. Clicks must occur in time not longer than defined
4099 by the variable `mswindows-mouse-button-tolerance'.
4100 If negative or zero, currently set system default is used instead.
4101 */ );
4102
4103   DEFVAR_INT ("mswindows-mouse-button-max-skew-y",
4104               &mswindows_mouse_button_max_skew_y /*
4105 *Maximum vertical distance in pixels between points in which left and
4106 right button clicks occurred for them to be translated into single
4107 middle button event. Clicks must occur in time not longer than defined
4108 by the variable `mswindows-mouse-button-tolerance'.
4109 If negative or zero, currently set system default is used instead.
4110 */ );
4111
4112   mswindows_mouse_button_max_skew_x = 0;
4113   mswindows_mouse_button_max_skew_y = 0;
4114   mswindows_mouse_button_tolerance = 0;
4115   mswindows_alt_by_itself_activates_menu = 1;
4116 }
4117
4118 void
4119 syms_of_event_mswindows (void)
4120 {
4121 }
4122
4123 void
4124 lstream_type_create_mswindows_selectable (void)
4125 {
4126   init_slurp_stream ();
4127   init_shove_stream ();
4128 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
4129   init_winsock_stream ();
4130 #endif
4131 }
4132
4133 void
4134 init_event_mswindows_late (void)
4135 {
4136 #ifdef HAVE_MSG_SELECT
4137   windows_fd = open("/dev/windows", O_RDONLY | O_NONBLOCK, 0);
4138   assert (windows_fd>=0);
4139   FD_SET (windows_fd, &input_wait_mask);
4140   FD_ZERO(&zero_mask);
4141 #endif
4142
4143   event_stream = mswindows_event_stream;
4144
4145   mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE);
4146   mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);
4147 }