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