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