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