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.
7 This file is part of XEmacs.
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
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
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. */
24 /* Synched up with: Not in FSF. */
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.
37 #include "console-msw.h"
39 #ifdef HAVE_SCROLLBARS
40 # include "scrollbar-msw.h"
44 # include "menubar-msw.h"
48 # include "dragdrop.h"
58 #include "redisplay.h"
64 #include "objects-msw.h"
66 #include "events-mod.h"
67 #ifdef HAVE_MSG_SELECT
69 #include "console-tty.h"
70 #elif defined(__CYGWIN32__)
71 typedef unsigned int SOCKET;
76 #if defined (__CYGWIN32__) && (CYGWIN_VERSION_DLL_MAJOR < 20)
77 typedef NMHDR *LPNMHDR;
81 #define ADJR_MENUFLAG TRUE
83 #define ADJR_MENUFLAG FALSE
86 /* Fake key modifier which is attached to a quit char event.
87 Removed upon dequeueing an event */
88 #define FAKE_MOD_QUIT 0x80
90 /* Timer ID used for button2 emulation */
91 #define BUTTON_2_TIMER_ID 1
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,
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);
102 static struct event_stream *mswindows_event_stream;
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;
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
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;
121 /* The number of things we can wait on */
122 #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1)
124 #ifndef HAVE_MSG_SELECT
125 /* List of mswindows waitable handles. */
126 static HANDLE mswindows_waitable_handles[MAX_WAITABLE];
128 /* Number of wait handles */
129 static int mswindows_waitable_count=0;
130 #endif /* HAVE_MSG_SELECT */
131 /* Brush for painting widgets */
132 static HBRUSH widget_brush = 0;
133 static LONG last_widget_brushed = 0;
135 /* Count of quit chars currently in the queue */
136 /* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc()
137 Decremented in mswindows_dequeue_dispatch_event() */
138 int mswindows_quit_chars_count = 0;
140 /* These are Lisp integers; see DEFVARS in this file for description. */
141 int mswindows_dynamic_frame_resize;
142 int mswindows_meta_activates_menu;
143 int mswindows_num_mouse_buttons;
144 int mswindows_mouse_button_max_skew_x;
145 int mswindows_mouse_button_max_skew_y;
146 int mswindows_mouse_button_tolerance;
148 /* This is the event signaled by the event pump.
149 See mswindows_pump_outstanding_events for comments */
150 static Lisp_Object mswindows_error_caught_in_modal_loop;
151 static int mswindows_in_modal_loop;
153 /* Count of wound timers */
154 static int mswindows_pending_timers_count;
156 /************************************************************************/
157 /* Pipe instream - reads process output */
158 /************************************************************************/
160 #define PIPE_READ_DELAY 20
162 #define HANDLE_TO_USID(h) ((USID)(h))
164 #define NTPIPE_SLURP_STREAM_DATA(stream) \
165 LSTREAM_TYPE_DATA (stream, ntpipe_slurp)
167 /* This structure is allocated by the main thread, and is deallocated
168 in the thread upon exit. There are situations when a thread
169 remains blocked for a long time, much longer than the lstream
170 exists. For example, "start notepad" command is issued from the
171 shell, then the shell is closed by C-c C-d. Although the shell
172 process exits, its output pipe will not get closed until the
173 notepad process exits also, because it inherits the pipe form the
174 shell. In this case, we abandon the thread, and let it live until
175 all such processes exit. While struct ntpipe_slurp_stream is
176 deallocated in this case, ntpipe_slurp_stream_shared_data are not. */
178 struct ntpipe_slurp_stream_shared_data
180 HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */
181 /* This is a manual-reset object. */
182 HANDLE hev_caller; /* Caller blocks on this, and we signal it */
183 /* This is a manual-reset object. */
184 HANDLE hev_unsleep; /* Pipe read delay is canceled if this is set */
185 /* This is a manual-reset object. */
186 HANDLE hpipe; /* Pipe read end handle. */
187 LONG die_p; /* Thread must exit ASAP if non-zero */
188 BOOL eof_p : 1; /* Set when thread saw EOF */
189 BOOL error_p : 1; /* Read error other than EOF/broken pipe */
190 BOOL inuse_p : 1; /* this structure is in use */
191 LONG lock_count; /* Client count of this struct, 0=safe to free */
192 BYTE onebyte; /* One byte buffer read by thread */
195 #define MAX_SLURP_STREAMS 32
196 struct ntpipe_slurp_stream_shared_data
197 shared_data_block[MAX_SLURP_STREAMS]={{0}};
199 struct ntpipe_slurp_stream
201 LPARAM user_data; /* Any user data stored in the stream object */
202 struct ntpipe_slurp_stream_shared_data* thread_data;
205 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-input", lstream_ntpipe_slurp,
206 sizeof (struct ntpipe_slurp_stream));
208 /* This function is thread-safe, and is called from either thread
209 context. It serializes freeing shared data structure */
211 slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s)
213 if (InterlockedDecrement (&s->lock_count) == 0)
216 CloseHandle (s->hev_thread);
217 CloseHandle (s->hev_caller);
218 CloseHandle (s->hev_unsleep);
223 static struct ntpipe_slurp_stream_shared_data*
224 slurper_allocate_shared_data()
227 for (i=0; i<MAX_SLURP_STREAMS; i++)
229 if (!shared_data_block[i].inuse_p)
231 shared_data_block[i].inuse_p=1;
232 return &shared_data_block[i];
235 return (struct ntpipe_slurp_stream_shared_data*)0;
239 slurp_thread (LPVOID vparam)
241 struct ntpipe_slurp_stream_shared_data *s =
242 (struct ntpipe_slurp_stream_shared_data*)vparam;
246 /* Read one byte from the pipe */
248 if (!ReadFile (s->hpipe, &s->onebyte, 1, &actually_read, NULL))
250 DWORD err = GetLastError ();
251 if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA)
256 else if (actually_read == 0)
259 /* We must terminate on an error or eof */
260 if (s->eof_p || s->error_p)
261 InterlockedIncrement (&s->die_p);
263 /* Before we notify caller, we unsignal our event. */
264 ResetEvent (s->hev_thread);
266 /* Now we got something to notify caller, either a byte or an
267 error/eof indication. Before we do, allow internal pipe
268 buffer to accumulate little bit more data.
269 Reader function pulses this event before waiting for
270 a character, to avoid pipe delay, and to get the byte
273 WaitForSingleObject (s->hev_unsleep, PIPE_READ_DELAY);
275 /* Either make event loop generate a process event, or
277 SetEvent (s->hev_caller);
279 /* Cleanup and exit if we're shot off */
283 /* Block until the client finishes with retrieving the rest of
285 WaitForSingleObject (s->hev_thread, INFINITE);
288 slurper_free_shared_data_maybe (s);
294 make_ntpipe_input_stream (HANDLE hpipe, LPARAM param)
297 Lstream *lstr = Lstream_new (lstream_ntpipe_slurp, "r");
298 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA (lstr);
299 DWORD thread_id_unused;
302 /* We deal only with pipes, for we're using PeekNamedPipe api */
303 assert (GetFileType (hpipe) == FILE_TYPE_PIPE);
305 s->thread_data = slurper_allocate_shared_data();
307 /* Create reader thread. This could fail, so do not create events
308 until thread is created */
309 hthread = CreateThread (NULL, 0, slurp_thread, (LPVOID)s->thread_data,
310 CREATE_SUSPENDED, &thread_id_unused);
313 Lstream_delete (lstr);
314 s->thread_data->inuse_p=0;
318 /* Shared data are initially owned by both main and slurper
320 s->thread_data->lock_count = 2;
321 s->thread_data->die_p = 0;
322 s->thread_data->eof_p = FALSE;
323 s->thread_data->error_p = FALSE;
324 s->thread_data->hpipe = hpipe;
325 s->user_data = param;
327 /* hev_thread is a manual-reset event, initially signaled */
328 s->thread_data->hev_thread = CreateEvent (NULL, TRUE, TRUE, NULL);
329 /* hev_caller is a manual-reset event, initially nonsignaled */
330 s->thread_data->hev_caller = CreateEvent (NULL, TRUE, FALSE, NULL);
331 /* hev_unsleep is a manual-reset event, initially nonsignaled */
332 s->thread_data->hev_unsleep = CreateEvent (NULL, TRUE, FALSE, NULL);
335 ResumeThread (hthread);
336 CloseHandle (hthread);
338 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
339 XSETLSTREAM (obj, lstr);
344 get_ntpipe_input_stream_param (Lstream *stream)
346 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream);
351 get_ntpipe_input_stream_waitable (Lstream *stream)
353 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream);
354 return s->thread_data->hev_caller;
358 ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size)
360 /* This function must be called from the main thread only */
361 struct ntpipe_slurp_stream_shared_data* s =
362 NTPIPE_SLURP_STREAM_DATA(stream)->thread_data;
367 /* Disallow pipe read delay for the thread: we need a character
369 SetEvent (s->hev_unsleep);
371 /* Check if we have a character ready. Give it a short delay,
372 for the thread to awake from pipe delay, just ion case*/
373 wait_result = WaitForSingleObject (s->hev_caller, 2);
375 /* Revert to the normal sleep behavior. */
376 ResetEvent (s->hev_unsleep);
378 /* If there's no byte buffered yet, give up */
379 if (wait_result == WAIT_TIMEOUT)
386 /* Reset caller unlock event now, as we've handled the pending
387 process output event */
388 ResetEvent (s->hev_caller);
390 /* It is now safe to do anything with contents of S, except for
391 changing s->die_p, which still should be interlocked */
395 if (s->error_p || s->die_p)
398 /* Ok, there were no error neither eof - we've got a byte from the
400 *(data++) = s->onebyte;
404 DWORD bytes_read = 0;
407 DWORD bytes_available;
409 /* If the api call fails, return at least one byte already
410 read. ReadFile in thread will return error */
411 if (PeekNamedPipe (s->hpipe, NULL, 0, NULL, &bytes_available, NULL))
414 /* Fetch available bytes. The same consideration applies,
415 so do not check for errors. ReadFile in the thread will
416 fail if the next call fails. */
418 ReadFile (s->hpipe, data, min (bytes_available, size),
422 /* Now we can unblock thread, so it attempts to read more */
423 SetEvent (s->hev_thread);
424 return bytes_read + 1;
431 ntpipe_slurp_closer (Lstream *stream)
433 /* This function must be called from the main thread only */
434 struct ntpipe_slurp_stream_shared_data* s =
435 NTPIPE_SLURP_STREAM_DATA(stream)->thread_data;
437 /* Force thread to stop */
438 InterlockedIncrement (&s->die_p);
440 /* Set events which could possibly block slurper. Let it finish soon
442 SetEvent (s->hev_unsleep);
443 SetEvent (s->hev_thread);
445 /* Unlock and maybe free shared data */
446 slurper_free_shared_data_maybe (s);
452 init_slurp_stream (void)
454 LSTREAM_HAS_METHOD (ntpipe_slurp, reader);
455 LSTREAM_HAS_METHOD (ntpipe_slurp, closer);
458 /************************************************************************/
459 /* Pipe outstream - writes process input */
460 /************************************************************************/
462 #define NTPIPE_SHOVE_STREAM_DATA(stream) \
463 LSTREAM_TYPE_DATA (stream, ntpipe_shove)
465 #define MAX_SHOVE_BUFFER_SIZE 128
467 struct ntpipe_shove_stream
469 LPARAM user_data; /* Any user data stored in the stream object */
470 HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */
471 /* This is an auto-reset object. */
472 HANDLE hpipe; /* Pipe write end handle. */
473 HANDLE hthread; /* Reader thread handle. */
474 char buffer[MAX_SHOVE_BUFFER_SIZE]; /* Buffer being written */
475 DWORD size; /* Number of bytes to write */
476 LONG die_p; /* Thread must exit ASAP if non-zero */
477 LONG idle_p; /* Non-zero if thread is waiting for job */
478 BOOL error_p : 1; /* Read error other than EOF/broken pipe */
479 BOOL blocking_p : 1;/* Last write attempt would cause blocking */
482 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-output", lstream_ntpipe_shove,
483 sizeof (struct ntpipe_shove_stream));
485 #ifndef HAVE_MSG_SELECT
487 shove_thread (LPVOID vparam)
489 struct ntpipe_shove_stream *s = (struct ntpipe_shove_stream*) vparam;
495 /* Block on event and wait for a job */
496 InterlockedIncrement (&s->idle_p);
497 WaitForSingleObject (s->hev_thread, INFINITE);
502 /* Write passed buffer */
503 if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL)
504 || bytes_written != s->size)
507 InterlockedIncrement (&s->die_p);
518 make_ntpipe_output_stream (HANDLE hpipe, LPARAM param)
521 Lstream *lstr = Lstream_new (lstream_ntpipe_shove, "w");
522 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA (lstr);
523 DWORD thread_id_unused;
528 s->user_data = param;
530 /* Create reader thread. This could fail, so do not
531 create the event until thread is created */
532 s->hthread = CreateThread (NULL, 0, shove_thread, (LPVOID)s,
533 CREATE_SUSPENDED, &thread_id_unused);
534 if (s->hthread == NULL)
536 Lstream_delete (lstr);
540 /* hev_thread is an auto-reset event, initially nonsignaled */
541 s->hev_thread = CreateEvent (NULL, FALSE, FALSE, NULL);
544 ResumeThread (s->hthread);
546 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
547 XSETLSTREAM (obj, lstr);
552 get_ntpipe_output_stream_param (Lstream *stream)
554 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
560 ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size)
562 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
567 s->blocking_p = !s->idle_p;
571 if (size>MAX_SHOVE_BUFFER_SIZE)
574 memcpy (s->buffer, data, size);
578 InterlockedDecrement (&s->idle_p);
579 SetEvent (s->hev_thread);
584 ntpipe_shove_was_blocked_p (Lstream *stream)
586 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
587 return s->blocking_p;
591 ntpipe_shove_closer (Lstream *stream)
593 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
595 /* Force thread stop */
596 InterlockedIncrement (&s->die_p);
598 /* Close pipe handle, possibly breaking it */
599 CloseHandle (s->hpipe);
601 /* Thread will end upon unblocking */
602 SetEvent (s->hev_thread);
604 /* Wait while thread terminates */
605 WaitForSingleObject (s->hthread, INFINITE);
606 CloseHandle (s->hthread);
608 /* Destroy the event */
609 CloseHandle (s->hev_thread);
615 init_shove_stream (void)
617 LSTREAM_HAS_METHOD (ntpipe_shove, writer);
618 LSTREAM_HAS_METHOD (ntpipe_shove, was_blocked_p);
619 LSTREAM_HAS_METHOD (ntpipe_shove, closer);
622 /************************************************************************/
623 /* Winsock I/O stream */
624 /************************************************************************/
625 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
627 #define WINSOCK_READ_BUFFER_SIZE 1024
629 struct winsock_stream
631 LPARAM user_data; /* Any user data stored in the stream object */
632 SOCKET s; /* Socket handle (which is a Win32 handle) */
633 OVERLAPPED ov; /* Overlapped I/O structure */
634 void* buffer; /* Buffer. Allocated for input stream only */
635 unsigned int bufsize; /* Number of bytes last read */
636 unsigned int bufpos; /* Position in buffer for next fetch */
637 unsigned int error_p :1; /* I/O Error seen */
638 unsigned int eof_p :1; /* EOF Error seen */
639 unsigned int pending_p :1; /* There is a pending I/O operation */
640 unsigned int blocking_p :1; /* Last write attempt would block */
643 #define WINSOCK_STREAM_DATA(stream) LSTREAM_TYPE_DATA (stream, winsock)
645 DEFINE_LSTREAM_IMPLEMENTATION ("winsock", lstream_winsock,
646 sizeof (struct winsock_stream));
649 winsock_initiate_read (struct winsock_stream *str)
651 ResetEvent (str->ov.hEvent);
654 if (!ReadFile ((HANDLE)str->s, str->buffer, WINSOCK_READ_BUFFER_SIZE,
655 &str->bufsize, &str->ov))
657 if (GetLastError () == ERROR_IO_PENDING)
659 else if (GetLastError () == ERROR_HANDLE_EOF)
664 else if (str->bufsize == 0)
669 winsock_reader (Lstream *stream, unsigned char *data, size_t size)
671 struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
673 /* If the current operation is not yet complete, there's nothing to
677 if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT)
684 if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &str->bufsize, TRUE))
686 if (GetLastError() == ERROR_HANDLE_EOF)
691 if (str->bufsize == 0)
702 /* Return as much of buffer as we have */
703 size = min (size, (size_t) (str->bufsize - str->bufpos));
704 memcpy (data, (void*)((BYTE*)str->buffer + str->bufpos), size);
707 /* Read more if buffer is exhausted */
708 if (str->bufsize == str->bufpos)
709 winsock_initiate_read (str);
715 winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size)
717 struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
721 if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT)
729 if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &dw_unused, TRUE))
744 ResetEvent (str->ov.hEvent);
746 /* Docs indicate that 4th parameter to WriteFile can be NULL since this is
747 * an overlapped operation. This fails on Win95 with winsock 1.x so we
748 * supply a spare address which is ignored by Win95 anyway. Sheesh. */
749 if (WriteFile ((HANDLE)str->s, data, size, (LPDWORD)&str->buffer, &str->ov)
750 || GetLastError() == ERROR_IO_PENDING)
756 return str->error_p ? -1 : size;
760 winsock_closer (Lstream *lstr)
762 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
764 if (lstr->flags & LSTREAM_FL_READ)
765 shutdown (str->s, 0);
767 shutdown (str->s, 1);
769 CloseHandle ((HANDLE)str->s);
771 WaitForSingleObject (str->ov.hEvent, INFINITE);
773 if (lstr->flags & LSTREAM_FL_READ)
776 CloseHandle (str->ov.hEvent);
781 winsock_was_blocked_p (Lstream *stream)
783 struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
784 return str->blocking_p;
788 make_winsock_stream_1 (SOCKET s, LPARAM param, CONST char *mode)
791 Lstream *lstr = Lstream_new (lstream_winsock, mode);
792 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
799 str->user_data = param;
802 str->ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
804 if (lstr->flags & LSTREAM_FL_READ)
806 str->buffer = xmalloc (WINSOCK_READ_BUFFER_SIZE);
807 winsock_initiate_read (str);
810 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
811 XSETLSTREAM (obj, lstr);
816 make_winsock_input_stream (SOCKET s, LPARAM param)
818 return make_winsock_stream_1 (s, param, "r");
822 make_winsock_output_stream (SOCKET s, LPARAM param)
824 return make_winsock_stream_1 (s, param, "w");
828 get_winsock_stream_waitable (Lstream *lstr)
830 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
831 return str->ov.hEvent;
835 get_winsock_stream_param (Lstream *lstr)
837 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
838 return str->user_data;
842 init_winsock_stream (void)
844 LSTREAM_HAS_METHOD (winsock, reader);
845 LSTREAM_HAS_METHOD (winsock, writer);
846 LSTREAM_HAS_METHOD (winsock, closer);
847 LSTREAM_HAS_METHOD (winsock, was_blocked_p);
849 #endif /* defined (HAVE_SOCKETS) */
851 /************************************************************************/
852 /* Dispatch queue management */
853 /************************************************************************/
856 mswindows_user_event_p (Lisp_Event* sevt)
858 return (sevt->event_type == key_press_event
859 || sevt->event_type == button_press_event
860 || sevt->event_type == button_release_event
861 || sevt->event_type == misc_user_event);
865 * Add an emacs event to the proper dispatch queue
868 mswindows_enqueue_dispatch_event (Lisp_Object event)
870 int user_p = mswindows_user_event_p (XEVENT(event));
871 enqueue_event (event,
872 user_p ? &mswindows_u_dispatch_event_queue :
873 &mswindows_s_dispatch_event_queue,
874 user_p ? &mswindows_u_dispatch_event_queue_tail :
875 &mswindows_s_dispatch_event_queue_tail);
877 /* Avoid blocking on WaitMessage */
878 PostMessage (NULL, XM_BUMPQUEUE, 0, 0);
882 * Add a misc-user event to the dispatch queue.
884 * Stuff it into our own dispatch queue, so we have something
885 * to return from next_event callback.
888 mswindows_enqueue_misc_user_event (Lisp_Object channel, Lisp_Object function,
891 Lisp_Object event = Fmake_event (Qnil, Qnil);
892 Lisp_Event* e = XEVENT (event);
894 e->event_type = misc_user_event;
895 e->channel = channel;
896 e->timestamp = GetTickCount ();
897 e->event.misc.function = function;
898 e->event.misc.object = object;
900 mswindows_enqueue_dispatch_event (event);
904 mswindows_enqueue_magic_event (HWND hwnd, UINT msg)
906 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
907 Lisp_Event* event = XEVENT (emacs_event);
909 event->channel = hwnd ? mswindows_find_frame (hwnd) : Qnil;
910 event->timestamp = GetMessageTime();
911 event->event_type = magic_event;
912 EVENT_MSWINDOWS_MAGIC_TYPE (event) = msg;
914 mswindows_enqueue_dispatch_event (emacs_event);
918 mswindows_enqueue_process_event (Lisp_Process* p)
920 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
921 Lisp_Event* event = XEVENT (emacs_event);
923 XSETPROCESS (process, p);
925 event->event_type = process_event;
926 event->timestamp = GetTickCount ();
927 event->event.process.process = process;
929 mswindows_enqueue_dispatch_event (emacs_event);
933 mswindows_enqueue_mouse_button_event (HWND hwnd, UINT msg, POINTS where, DWORD when)
936 /* We always use last message time, because mouse button
937 events may get delayed, and XEmacs double click
938 recognition will fail */
940 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
941 Lisp_Event* event = XEVENT(emacs_event);
943 event->channel = mswindows_find_frame(hwnd);
944 event->timestamp = when;
945 event->event.button.button =
946 (msg==WM_LBUTTONDOWN || msg==WM_LBUTTONUP) ? 1 :
947 ((msg==WM_RBUTTONDOWN || msg==WM_RBUTTONUP) ? 3 : 2);
948 event->event.button.x = where.x;
949 event->event.button.y = where.y;
950 event->event.button.modifiers = mswindows_modifier_state (NULL, 0);
952 if (msg==WM_LBUTTONDOWN || msg==WM_MBUTTONDOWN ||
955 event->event_type = button_press_event;
957 /* we need this to make sure the main window regains the focus
958 from control subwindows */
959 if (GetFocus() != hwnd)
962 mswindows_enqueue_magic_event (hwnd, WM_SETFOCUS);
967 event->event_type = button_release_event;
971 mswindows_enqueue_dispatch_event (emacs_event);
975 mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods)
977 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
978 Lisp_Event* event = XEVENT(emacs_event);
980 event->channel = mswindows_find_console(hwnd);
981 event->timestamp = GetMessageTime();
982 event->event_type = key_press_event;
983 event->event.key.keysym = keysym;
984 event->event.key.modifiers = mods;
985 mswindows_enqueue_dispatch_event (emacs_event);
989 * Remove and return the first emacs event on the dispatch queue.
990 * Give a preference to user events over non-user ones.
993 mswindows_dequeue_dispatch_event ()
998 assert (!NILP(mswindows_u_dispatch_event_queue) ||
999 !NILP(mswindows_s_dispatch_event_queue));
1001 event = dequeue_event (
1002 NILP(mswindows_u_dispatch_event_queue) ?
1003 &mswindows_s_dispatch_event_queue :
1004 &mswindows_u_dispatch_event_queue,
1005 NILP(mswindows_u_dispatch_event_queue) ?
1006 &mswindows_s_dispatch_event_queue_tail :
1007 &mswindows_u_dispatch_event_queue_tail);
1009 sevt = XEVENT(event);
1010 if (sevt->event_type == key_press_event
1011 && (sevt->event.key.modifiers & FAKE_MOD_QUIT))
1013 sevt->event.key.modifiers &= ~FAKE_MOD_QUIT;
1014 --mswindows_quit_chars_count;
1021 * Remove and return the first emacs event on the dispatch queue that matches
1022 * the supplied event.
1023 * Timeout event matches if interval_id is equal to that of the given event.
1024 * Keypress event matches if logical AND between modifiers bitmask of the
1025 * event in the queue and that of the given event is non-zero.
1026 * For all other event types, this function aborts.
1030 mswindows_cancel_dispatch_event (Lisp_Event *match)
1033 Lisp_Object previous_event = Qnil;
1034 int user_p = mswindows_user_event_p (match);
1035 Lisp_Object* head = user_p ? &mswindows_u_dispatch_event_queue :
1036 &mswindows_s_dispatch_event_queue;
1037 Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail :
1038 &mswindows_s_dispatch_event_queue_tail;
1040 assert (match->event_type == timeout_event
1041 || match->event_type == key_press_event);
1043 EVENT_CHAIN_LOOP (event, *head)
1045 Lisp_Event *e = XEVENT (event);
1046 if ((e->event_type == match->event_type) &&
1047 ((e->event_type == timeout_event) ?
1048 (e->event.timeout.interval_id == match->event.timeout.interval_id) :
1049 /* Must be key_press_event */
1050 ((e->event.key.modifiers & match->event.key.modifiers) != 0)))
1052 if (NILP (previous_event))
1053 dequeue_event (head, tail);
1056 XSET_EVENT_NEXT (previous_event, XEVENT_NEXT (event));
1057 if (EQ (*tail, event))
1058 *tail = previous_event;
1063 previous_event = event;
1068 #ifndef HAVE_MSG_SELECT
1069 /************************************************************************/
1070 /* Waitable handles manipulation */
1071 /************************************************************************/
1073 find_waitable_handle (HANDLE h)
1076 for (i = 0; i < mswindows_waitable_count; ++i)
1077 if (mswindows_waitable_handles[i] == h)
1084 add_waitable_handle (HANDLE h)
1086 assert (find_waitable_handle (h) < 0);
1087 if (mswindows_waitable_count == MAX_WAITABLE)
1090 mswindows_waitable_handles [mswindows_waitable_count++] = h;
1095 remove_waitable_handle (HANDLE h)
1097 int ix = find_waitable_handle (h);
1101 mswindows_waitable_handles [ix] =
1102 mswindows_waitable_handles [--mswindows_waitable_count];
1104 #endif /* HAVE_MSG_SELECT */
1107 /************************************************************************/
1109 /************************************************************************/
1112 mswindows_modal_loop_error_handler (Lisp_Object cons_sig_data,
1113 Lisp_Object u_n_u_s_e_d)
1115 mswindows_error_caught_in_modal_loop = cons_sig_data;
1120 mswindows_protect_modal_loop (Lisp_Object (*bfun) (Lisp_Object barg),
1125 ++mswindows_in_modal_loop;
1126 tmp = condition_case_1 (Qt,
1128 mswindows_modal_loop_error_handler, Qnil);
1129 --mswindows_in_modal_loop;
1135 mswindows_unmodalize_signal_maybe (void)
1137 if (!NILP (mswindows_error_caught_in_modal_loop))
1139 /* Got an error while messages were pumped while
1140 in window procedure - have to resignal */
1141 Lisp_Object sym = XCAR (mswindows_error_caught_in_modal_loop);
1142 Lisp_Object data = XCDR (mswindows_error_caught_in_modal_loop);
1143 mswindows_error_caught_in_modal_loop = Qnil;
1144 Fsignal (sym, data);
1149 * This is an unsafe part of event pump, guarded by
1150 * condition_case. See mswindows_pump_outstanding_events
1153 mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d)
1155 /* This function can call lisp */
1156 Lisp_Object event = Fmake_event (Qnil, Qnil);
1157 struct gcpro gcpro1;
1158 int do_redisplay = 0;
1161 while (detect_input_pending ())
1163 Fnext_event (event, Qnil);
1164 Fdispatch_event (event);
1171 Fdeallocate_event (event);
1174 /* Qt becomes return value of mswindows_pump_outstanding_events
1180 * This function pumps emacs events, while available, by using
1181 * next_message/dispatch_message loop. Errors are trapped around
1182 * the loop so the function always returns.
1184 * Windows message queue is not looked into during the call,
1185 * neither are waitable handles checked. The function pumps
1186 * thus only dispatch events already queued, as well as those
1187 * resulted in dispatching thereof. This is done by setting
1188 * module local variable mswindows_in_modal_loop to nonzero.
1190 * Return value is Qt if no errors was trapped, or Qunbound if
1191 * there was an error.
1193 * In case of error, a cons representing the error, in the
1194 * form (SIGNAL . DATA), is stored in the module local variable
1195 * mswindows_error_caught_in_modal_loop. This error is signaled
1196 * again when DispatchMessage returns. Thus, Windows internal
1197 * modal loops are protected against throws, which are proven
1198 * to corrupt internal Windows structures.
1200 * In case of success, mswindows_error_caught_in_modal_loop is
1203 * If the value of mswindows_error_caught_in_modal_loop is not
1204 * nil already upon entry, the function just returns non-nil.
1205 * This situation means that a new event has been queued while
1206 * in cancel mode. The event will be dequeued on the next regular
1207 * call of next-event; the pump is off since error is caught.
1208 * The caller must *unconditionally* cancel modal loop if the
1209 * value returned by this function is nil. Otherwise, everything
1210 * will become frozen until the modal loop exits under normal
1211 * condition (scrollbar drag is released, menu closed etc.)
1214 mswindows_pump_outstanding_events (void)
1216 /* This function can call lisp */
1218 Lisp_Object result = Qt;
1219 struct gcpro gcpro1;
1222 if (NILP(mswindows_error_caught_in_modal_loop))
1223 result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil);
1229 * KEYBOARD_ONLY_P is set to non-zero when we are called from
1230 * QUITP, and are interesting in keyboard messages only.
1233 mswindows_drain_windows_queue (int keyboard_only_till_quit_char_p)
1237 /* Minimize the hassle of misordered events by not fetching
1238 past quit char if called from QUITP; */
1239 while (!(keyboard_only_till_quit_char_p &&
1240 mswindows_quit_chars_count > 0) &&
1241 PeekMessage (&msg, NULL,
1242 keyboard_only_till_quit_char_p ? WM_KEYFIRST : 0,
1243 keyboard_only_till_quit_char_p ? WM_KEYLAST : 0,
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)
1252 TranslateMessage (&msg);
1254 DispatchMessage (&msg);
1255 mswindows_unmodalize_signal_maybe ();
1260 * This is a special flavor of the mswindows_need_event function,
1261 * used while in event pump. Actually, there is only kind of events
1262 * allowed while in event pump: a timer. An attempt to fetch any
1263 * other event leads to a deadlock, as there's no source of user input
1264 * ('cause event pump mirrors windows modal loop, which is a sole
1265 * owner of thread message queue).
1267 * To detect this, we use a counter of active timers, and allow
1268 * fetching WM_TIMER messages. Instead of trying to fetch a WM_TIMER
1269 * which will never come when there are no pending timers, which leads
1270 * to deadlock, we simply signal an error.
1273 mswindows_need_event_in_modal_loop (int badly_p)
1277 /* Check if already have one */
1278 if (!NILP (mswindows_u_dispatch_event_queue)
1279 || !NILP (mswindows_s_dispatch_event_queue))
1282 /* No event is ok */
1286 /* We do not check the _u_ queue, because timers go to _s_ */
1287 while (NILP (mswindows_s_dispatch_event_queue))
1289 /* We'll deadlock if go waiting */
1290 if (mswindows_pending_timers_count == 0)
1291 error ("Deadlock due to an attempt to call next-event in a wrong context");
1293 /* Fetch and dispatch any pending timers */
1294 GetMessage (&msg, NULL, WM_TIMER, WM_TIMER);
1295 DispatchMessage (&msg);
1300 * This drains the event queue and fills up two internal queues until
1301 * an event of a type specified by USER_P is retrieved.
1304 * Used by emacs_mswindows_event_pending_p and emacs_mswindows_next_event
1307 mswindows_need_event (int badly_p)
1311 if (mswindows_in_modal_loop)
1313 mswindows_need_event_in_modal_loop (badly_p);
1318 /* Have to drain Windows message queue first, otherwise, we may miss
1319 quit char when called from quit_p */
1320 /* #### This is, ehm, not quite true -- this function is not
1321 called from quit_p. --kkm */
1322 mswindows_drain_windows_queue ();
1325 while (NILP (mswindows_u_dispatch_event_queue)
1326 && NILP (mswindows_s_dispatch_event_queue))
1328 #ifdef HAVE_MSG_SELECT
1330 SELECT_TYPE temp_mask = input_wait_mask;
1331 EMACS_TIME sometime;
1332 EMACS_SELECT_TIME select_time_to_block, *pointer_to_this;
1335 pointer_to_this = 0;
1338 EMACS_SET_SECS_USECS (sometime, 0, 0);
1339 EMACS_TIME_TO_SELECT_TIME (sometime, select_time_to_block);
1340 pointer_to_this = &select_time_to_block;
1343 active = select (MAXDESC, &temp_mask, 0, 0, pointer_to_this);
1348 return; /* timeout */
1350 else if (active > 0)
1352 if (FD_ISSET (windows_fd, &temp_mask))
1354 mswindows_drain_windows_queue (0);
1357 /* Look for a TTY event */
1358 for (i = 0; i < MAXDESC-1; i++)
1360 /* To avoid race conditions (among other things, an infinite
1361 loop when called from Fdiscard_input()), we must return
1362 user events ahead of process events. */
1363 if (FD_ISSET (i, &temp_mask) && FD_ISSET (i, &tty_only_mask))
1365 struct console *c = tty_find_console_from_fd (i);
1366 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1367 Lisp_Event* event = XEVENT (emacs_event);
1370 if (read_event_from_tty_or_stream_desc (event, c, i))
1372 mswindows_enqueue_dispatch_event (emacs_event);
1378 /* Look for a process event */
1379 for (i = 0; i < MAXDESC-1; i++)
1381 if (FD_ISSET (i, &temp_mask))
1383 if (FD_ISSET (i, &process_only_mask))
1386 get_process_from_usid (FD_TO_USID(i));
1388 mswindows_enqueue_process_event (p);
1392 /* We might get here when a fake event came
1393 through a signal. Return a dummy event, so
1394 that a cycle of the command loop will
1396 drain_signal_event_pipe ();
1397 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
1402 else if (active==-1)
1406 /* something bad happened */
1415 /* Now try getting a message or process event */
1416 active = MsgWaitForMultipleObjects (mswindows_waitable_count,
1417 mswindows_waitable_handles,
1418 FALSE, badly_p ? INFINITE : 0,
1421 /* This will assert if handle being waited for becomes abandoned.
1422 Not the case currently tho */
1423 assert ((!badly_p && active == WAIT_TIMEOUT) ||
1424 (active >= WAIT_OBJECT_0 &&
1425 active <= WAIT_OBJECT_0 + mswindows_waitable_count));
1427 if (active == WAIT_TIMEOUT)
1429 /* No luck trying - just return what we've already got */
1432 else if (active == WAIT_OBJECT_0 + mswindows_waitable_count)
1434 /* Got your message, thanks */
1435 mswindows_drain_windows_queue (0);
1439 int ix = active - WAIT_OBJECT_0;
1440 /* First, try to find which process' output has signaled */
1442 get_process_from_usid (HANDLE_TO_USID (mswindows_waitable_handles[ix]));
1445 /* Found a signaled process input handle */
1446 mswindows_enqueue_process_event (p);
1450 /* None. This means that the process handle itself has signaled.
1451 Remove the handle from the wait vector, and make status_notify
1452 note the exited process */
1453 mswindows_waitable_handles [ix] =
1454 mswindows_waitable_handles [--mswindows_waitable_count];
1455 kick_status_notify ();
1456 /* Have to return something: there may be no accompanying
1458 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
1465 /************************************************************************/
1466 /* Event generators */
1467 /************************************************************************/
1470 * Callback procedure for synchronous timer messages
1472 static void CALLBACK
1473 mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime)
1475 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1476 Lisp_Event *event = XEVENT (emacs_event);
1478 if (KillTimer (NULL, id_timer))
1479 --mswindows_pending_timers_count;
1481 event->channel = Qnil;
1482 event->timestamp = dwtime;
1483 event->event_type = timeout_event;
1484 event->event.timeout.interval_id = id_timer;
1485 event->event.timeout.function = Qnil;
1486 event->event.timeout.object = Qnil;
1488 mswindows_enqueue_dispatch_event (emacs_event);
1492 * Callback procedure for dde messages
1494 * We execute a dde Open("file") by simulating a file drop, so dde support
1495 * depends on dnd support.
1497 #ifdef HAVE_DRAGNDROP
1499 mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
1500 HSZ hszTopic, HSZ hszItem, HDDEDATA hdata,
1501 DWORD dwData1, DWORD dwData2)
1506 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
1507 return (HDDEDATA)TRUE;
1508 return (HDDEDATA)FALSE;
1510 case XTYP_WILDCONNECT:
1512 /* We only support one {service,topic} pair */
1513 HSZPAIR pairs[2] = {
1514 { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } };
1516 if (!(hszItem || DdeCmpStringHandles (hszItem, mswindows_dde_service)) &&
1517 !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)));
1518 return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs,
1519 sizeof (pairs), 0L, 0, uFmt, 0));
1521 return (HDDEDATA)NULL;
1524 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
1526 DWORD len = DdeGetData (hdata, NULL, 0, 0);
1527 char *cmd = alloca (len+1);
1530 struct gcpro gcpro1, gcpro2;
1531 Lisp_Object l_dndlist = Qnil;
1532 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1533 Lisp_Object frmcons, devcons, concons;
1534 Lisp_Event *event = XEVENT (emacs_event);
1536 DdeGetData (hdata, cmd, len, 0);
1538 DdeFreeDataHandle (hdata);
1540 /* Check syntax & that it's an [Open("foo")] command, which we
1541 * treat like a file drop */
1542 /* #### Ought to be generalised and accept some other commands */
1545 if (strnicmp (cmd, MSWINDOWS_DDE_ITEM_OPEN,
1546 strlen (MSWINDOWS_DDE_ITEM_OPEN)))
1547 return DDE_FNOTPROCESSED;
1548 cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN);
1551 if (*cmd!='(' || *(cmd+1)!='\"')
1552 return DDE_FNOTPROCESSED;
1554 while (*end && *end!='\"')
1557 return DDE_FNOTPROCESSED;
1560 return DDE_FNOTPROCESSED;
1564 return DDE_FNOTPROCESSED;
1567 filename = alloca (cygwin32_win32_to_posix_path_list_buf_size (cmd) + 5);
1568 strcpy (filename, "file:");
1569 cygwin32_win32_to_posix_path_list (cmd, filename+5);
1571 dostounix_filename (cmd);
1572 filename = alloca (strlen (cmd)+6);
1573 strcpy (filename, "file:");
1574 strcat (filename, cmd);
1576 GCPRO2 (emacs_event, l_dndlist);
1577 l_dndlist = make_string (filename, strlen (filename));
1579 /* Find a mswindows frame */
1580 event->channel = Qnil;
1581 FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
1583 Lisp_Object frame = XCAR (frmcons);
1584 if (FRAME_TYPE_P (XFRAME (frame), mswindows))
1585 event->channel = frame;
1587 assert (!NILP (event->channel));
1589 event->timestamp = GetTickCount();
1590 event->event_type = misc_user_event;
1591 event->event.misc.button = 1;
1592 event->event.misc.modifiers = 0;
1593 event->event.misc.x = -1;
1594 event->event.misc.y = -1;
1595 event->event.misc.function = Qdragdrop_drop_dispatch;
1596 event->event.misc.object = Fcons (Qdragdrop_URL,
1597 Fcons (l_dndlist, Qnil));
1598 mswindows_enqueue_dispatch_event (emacs_event);
1600 return (HDDEDATA) DDE_FACK;
1602 DdeFreeDataHandle (hdata);
1603 return (HDDEDATA) DDE_FNOTPROCESSED;
1606 return (HDDEDATA) NULL;
1612 * Returns 1 if a key is a real modifier or special key, which
1613 * is better handled by DefWindowProc
1616 key_needs_default_processing_p (UINT vkey)
1618 if (mswindows_meta_activates_menu && vkey == VK_MENU)
1625 * The windows procedure for the window class XEMACS_CLASS
1628 mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1630 /* Note: Remember to initialize emacs_event and event before use.
1631 This code calls code that can GC. You must GCPRO before calling such code. */
1632 Lisp_Object emacs_event = Qnil;
1633 Lisp_Object fobj = Qnil;
1636 struct frame *frame;
1637 struct mswindows_frame* msframe;
1641 case WM_DESTROYCLIPBOARD:
1642 /* We own the clipboard and someone else wants it. Delete our
1643 cached copy of the clipboard contents so we'll ask for it from
1644 Windows again when someone does a paste. */
1645 handle_selection_clear(QCLIPBOARD);
1649 /* Erase background only during non-dynamic sizing */
1650 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1651 if (msframe->sizing && !mswindows_dynamic_frame_resize)
1656 fobj = mswindows_find_frame (hwnd);
1657 mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt));
1662 /* See Win95 comment under WM_KEYDOWN */
1666 if (wParam == VK_CONTROL)
1668 GetKeyboardState (keymap);
1669 keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80;
1670 SetKeyboardState (keymap);
1672 else if (wParam == VK_MENU)
1674 GetKeyboardState (keymap);
1675 keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80;
1676 SetKeyboardState (keymap);
1679 if (key_needs_default_processing_p (wParam))
1686 /* In some locales the right-hand Alt key is labelled AltGr. This key
1687 * should produce alternative charcaters when combined with another key.
1688 * eg on a German keyboard pressing AltGr+q should produce '@'.
1689 * AltGr generates exactly the same keystrokes as LCtrl+RAlt. But if
1690 * TranslateMessage() is called with *any* combination of Ctrl+Alt down,
1691 * it translates as if AltGr were down.
1692 * We get round this by removing all modifiers from the keymap before
1693 * calling TranslateMessage() unless AltGr is *really* down. */
1696 int has_AltGr = mswindows_current_layout_has_AltGr ();
1698 int extendedp = lParam & 0x1000000;
1701 GetKeyboardState (keymap);
1702 mods = mswindows_modifier_state (keymap, has_AltGr);
1704 /* Handle non-printables */
1705 if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods,
1707 mswindows_enqueue_keypress_event (hwnd, keysym, mods);
1708 else /* Normal keys & modifiers */
1710 int quit_ch = CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd)));
1711 BYTE keymap_orig[256];
1712 POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) };
1716 msg.message = message;
1717 msg.wParam = wParam;
1718 msg.lParam = lParam;
1719 msg.time = GetMessageTime();
1722 /* GetKeyboardState() does not work as documented on Win95. We have
1723 * to loosely track Left and Right modifiers on behalf of the OS,
1724 * without screwing up Windows NT which tracks them properly. */
1725 if (wParam == VK_CONTROL)
1726 keymap [extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
1727 else if (wParam == VK_MENU)
1728 keymap [extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
1730 memcpy (keymap_orig, keymap, 256);
1732 /* Remove shift modifier from an ascii character */
1735 /* Clear control and alt modifiers unless AltGr is pressed */
1736 keymap [VK_RCONTROL] = 0;
1737 keymap [VK_LMENU] = 0;
1738 if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80)
1739 || !(keymap [VK_RMENU] & 0x80))
1741 keymap [VK_LCONTROL] = 0;
1742 keymap [VK_CONTROL] = 0;
1743 keymap [VK_RMENU] = 0;
1744 keymap [VK_MENU] = 0;
1746 SetKeyboardState (keymap);
1748 /* Maybe generate some WM_[SYS]CHARs in the queue */
1749 TranslateMessage (&msg);
1751 while (PeekMessage (&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)
1752 || PeekMessage (&msg, hwnd, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE))
1755 WPARAM ch = msg.wParam;
1757 /* If a quit char with no modifiers other than control and
1758 shift, then mark it with a fake modifier, which is removed
1759 upon dequeueing the event */
1760 /* #### This might also not withstand localization, if
1761 quit character is not a latin-1 symbol */
1762 if (((quit_ch < ' ' && (mods & MOD_CONTROL) && quit_ch + 'a' - 1 == ch)
1763 || (quit_ch >= ' ' && !(mods & MOD_CONTROL) && quit_ch == ch))
1764 && ((mods & ~(MOD_CONTROL | MOD_SHIFT)) == 0))
1766 mods1 |= FAKE_MOD_QUIT;
1767 ++mswindows_quit_chars_count;
1770 mswindows_enqueue_keypress_event (hwnd, make_char(ch), mods1);
1772 SetKeyboardState (keymap_orig);
1775 if (key_needs_default_processing_p (wParam))
1780 case WM_MBUTTONDOWN:
1782 /* Real middle mouse button has nothing to do with emulated one:
1783 if one wants to exercise fingers playing chords on the mouse,
1784 he is allowed to do that! */
1785 mswindows_enqueue_mouse_button_event (hwnd, message,
1786 MAKEPOINTS (lParam), GetMessageTime());
1790 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1791 msframe->last_click_time = GetMessageTime();
1793 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1794 msframe->button2_need_lbutton = 0;
1795 if (msframe->ignore_next_lbutton_up)
1797 msframe->ignore_next_lbutton_up = 0;
1799 else if (msframe->button2_is_down)
1801 msframe->button2_is_down = 0;
1802 msframe->ignore_next_rbutton_up = 1;
1803 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
1804 MAKEPOINTS (lParam), GetMessageTime());
1808 if (msframe->button2_need_rbutton)
1810 msframe->button2_need_rbutton = 0;
1811 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
1812 MAKEPOINTS (lParam), GetMessageTime());
1814 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
1815 MAKEPOINTS (lParam), GetMessageTime());
1820 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1821 msframe->last_click_time = GetMessageTime();
1823 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1824 msframe->button2_need_rbutton = 0;
1825 if (msframe->ignore_next_rbutton_up)
1827 msframe->ignore_next_rbutton_up = 0;
1829 else if (msframe->button2_is_down)
1831 msframe->button2_is_down = 0;
1832 msframe->ignore_next_lbutton_up = 1;
1833 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
1834 MAKEPOINTS (lParam), GetMessageTime());
1838 if (msframe->button2_need_lbutton)
1840 msframe->button2_need_lbutton = 0;
1841 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
1842 MAKEPOINTS (lParam), GetMessageTime());
1844 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
1845 MAKEPOINTS (lParam), GetMessageTime());
1849 case WM_LBUTTONDOWN:
1850 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1852 if (msframe->button2_need_lbutton)
1854 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1855 msframe->button2_need_lbutton = 0;
1856 msframe->button2_need_rbutton = 0;
1857 if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
1859 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
1860 MAKEPOINTS (lParam), GetMessageTime());
1861 msframe->button2_is_down = 1;
1865 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
1866 msframe->last_click_point, msframe->last_click_time);
1867 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
1868 MAKEPOINTS (lParam), GetMessageTime());
1873 mswindows_set_chord_timer (hwnd);
1874 msframe->button2_need_rbutton = 1;
1875 msframe->last_click_point = MAKEPOINTS (lParam);
1877 msframe->last_click_time = GetMessageTime();
1880 case WM_RBUTTONDOWN:
1881 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1883 if (msframe->button2_need_rbutton)
1885 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1886 msframe->button2_need_lbutton = 0;
1887 msframe->button2_need_rbutton = 0;
1888 if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
1890 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
1891 MAKEPOINTS (lParam), GetMessageTime());
1892 msframe->button2_is_down = 1;
1896 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
1897 msframe->last_click_point, msframe->last_click_time);
1898 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
1899 MAKEPOINTS (lParam), GetMessageTime());
1904 mswindows_set_chord_timer (hwnd);
1905 msframe->button2_need_lbutton = 1;
1906 msframe->last_click_point = MAKEPOINTS (lParam);
1908 msframe->last_click_time = GetMessageTime();
1912 if (wParam == BUTTON_2_TIMER_ID)
1914 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1915 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1917 if (msframe->button2_need_lbutton)
1919 msframe->button2_need_lbutton = 0;
1920 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
1921 msframe->last_click_point, msframe->last_click_time);
1923 else if (msframe->button2_need_rbutton)
1925 msframe->button2_need_rbutton = 0;
1926 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
1927 msframe->last_click_point, msframe->last_click_time);
1931 assert ("Spurious timer fired" == 0);
1935 /* Optimization: don't report mouse movement while size is changing */
1936 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1937 if (!msframe->sizing)
1939 /* When waiting for the second mouse button to finish
1940 button2 emulation, and have moved too far, just pretend
1941 as if timer has expired. This improves drag-select feedback */
1942 if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton)
1943 && !mswindows_button2_near_enough (msframe->last_click_point,
1944 MAKEPOINTS (lParam)))
1946 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1947 SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0);
1950 emacs_event = Fmake_event (Qnil, Qnil);
1951 event = XEVENT(emacs_event);
1953 event->channel = mswindows_find_frame(hwnd);
1954 event->timestamp = GetMessageTime();
1955 event->event_type = pointer_motion_event;
1956 event->event.motion.x = MAKEPOINTS(lParam).x;
1957 event->event.motion.y = MAKEPOINTS(lParam).y;
1958 event->event.motion.modifiers = mswindows_modifier_state (NULL, 0);
1960 mswindows_enqueue_dispatch_event (emacs_event);
1966 /* Queue a `cancel-mode-internal' misc user event, so mouse
1967 selection would be canceled if any */
1968 mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd),
1969 Qcancel_mode_internal, Qnil);
1974 LPNMHDR nmhdr = (LPNMHDR)lParam;
1976 if (nmhdr->code == TTN_NEEDTEXT)
1978 #ifdef HAVE_TOOLBARS
1979 LPTOOLTIPTEXT tttext = (LPTOOLTIPTEXT)lParam;
1982 /* find out which toolbar */
1983 frame = XFRAME (mswindows_find_frame (hwnd));
1984 btext = mswindows_get_toolbar_button_text ( frame,
1987 tttext->lpszText = NULL;
1988 tttext->hinst = NULL;
1992 /* I think this is safe since the text will only go away
1993 when the toolbar does...*/
1994 TO_EXTERNAL_FORMAT (LISP_STRING, btext,
1995 C_STRING_ALLOCA, tttext->lpszText,
2000 /* handle tree view callbacks */
2001 else if (nmhdr->code == TVN_SELCHANGED)
2003 NM_TREEVIEW* ptree = (NM_TREEVIEW*)lParam;
2004 frame = XFRAME (mswindows_find_frame (hwnd));
2005 mswindows_handle_gui_wm_command (frame, 0, ptree->itemNew.lParam);
2007 /* handle tab control callbacks */
2008 else if (nmhdr->code == TCN_SELCHANGE)
2011 int idx = SendMessage (nmhdr->hwndFrom, TCM_GETCURSEL, 0, 0);
2012 frame = XFRAME (mswindows_find_frame (hwnd));
2014 item.mask = TCIF_PARAM;
2015 SendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM)idx,
2018 mswindows_handle_gui_wm_command (frame, 0, item.lParam);
2025 /* According to the docs we need to check GetUpdateRect() before
2026 actually doing a WM_PAINT */
2027 if (GetUpdateRect (hwnd, NULL, FALSE))
2029 PAINTSTRUCT paintStruct;
2030 int x, y, width, height;
2032 frame = XFRAME (mswindows_find_frame (hwnd));
2034 BeginPaint (hwnd, &paintStruct);
2035 x = paintStruct.rcPaint.left;
2036 y = paintStruct.rcPaint.top;
2037 width = paintStruct.rcPaint.right - paintStruct.rcPaint.left;
2038 height = paintStruct.rcPaint.bottom - paintStruct.rcPaint.top;
2039 /* Normally we want to ignore expose events when child
2040 windows are unmapped, however once we are in the guts of
2041 WM_PAINT we need to make sure that we don't register
2042 unmaps then because they will not actually occur. */
2043 if (!check_for_ignored_expose (frame, x, y, width, height))
2045 hold_ignored_expose_registration = 1;
2046 mswindows_redraw_exposed_area (frame, x, y, width, height);
2047 hold_ignored_expose_registration = 0;
2050 EndPaint (hwnd, &paintStruct);
2058 /* We only care about this message if our size has really changed */
2059 if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
2064 fobj = mswindows_find_frame (hwnd);
2065 frame = XFRAME (fobj);
2066 msframe = FRAME_MSWINDOWS_DATA (frame);
2068 /* We cannot handle frame map and unmap hooks right in
2069 this routine, because these may throw. We queue
2070 magic events to run these hooks instead - kkm */
2072 if (wParam==SIZE_MINIMIZED)
2075 FRAME_VISIBLE_P (frame) = 0;
2076 mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME);
2080 GetClientRect(hwnd, &rect);
2081 FRAME_PIXWIDTH(frame) = rect.right;
2082 FRAME_PIXHEIGHT(frame) = rect.bottom;
2084 pixel_to_real_char_size (frame, rect.right, rect.bottom,
2085 &FRAME_MSWINDOWS_CHARWIDTH (frame),
2086 &FRAME_MSWINDOWS_CHARHEIGHT (frame));
2088 pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows);
2089 change_frame_size (frame, rows, columns, 1);
2091 /* If we are inside frame creation, we have to apply geometric
2093 if (FRAME_MSWINDOWS_TARGET_RECT (frame))
2095 /* Yes, we have to size again */
2096 mswindows_size_frame_internal ( frame,
2097 FRAME_MSWINDOWS_TARGET_RECT
2099 /* Reset so we do not get here again. The SetWindowPos call in
2100 * mswindows_size_frame_internal can cause recursion here. */
2101 if (FRAME_MSWINDOWS_TARGET_RECT (frame))
2103 xfree (FRAME_MSWINDOWS_TARGET_RECT (frame));
2104 FRAME_MSWINDOWS_TARGET_RECT (frame) = 0;
2109 if (!msframe->sizing && !FRAME_VISIBLE_P (frame))
2110 mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME);
2111 FRAME_VISIBLE_P (frame) = 1;
2113 if (!msframe->sizing || mswindows_dynamic_frame_resize)
2120 /* Misc magic events which only require that the frame be identified */
2123 mswindows_enqueue_magic_event (hwnd, message);
2126 case WM_WINDOWPOSCHANGING:
2128 WINDOWPOS *wp = (LPWINDOWPOS) lParam;
2129 WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) };
2130 GetWindowPlacement(hwnd, &wpl);
2132 /* Only interested if size is changing and we're not being iconified */
2133 if (wpl.showCmd != SW_SHOWMINIMIZED
2134 && wpl.showCmd != SW_SHOWMAXIMIZED
2135 && !(wp->flags & SWP_NOSIZE))
2137 RECT ncsize = { 0, 0, 0, 0 };
2138 int pixwidth, pixheight;
2139 AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE),
2140 GetMenu(hwnd) != NULL,
2141 GetWindowLong (hwnd, GWL_EXSTYLE));
2143 round_size_to_real_char (XFRAME (mswindows_find_frame (hwnd)),
2144 wp->cx - (ncsize.right - ncsize.left),
2145 wp->cy - (ncsize.bottom - ncsize.top),
2146 &pixwidth, &pixheight);
2148 /* Convert client sizes to window sizes */
2149 pixwidth += (ncsize.right - ncsize.left);
2150 pixheight += (ncsize.bottom - ncsize.top);
2152 if (wpl.showCmd != SW_SHOWMAXIMIZED)
2154 /* Adjust so that the bottom or right doesn't move if it's
2155 * the top or left that's being changed */
2157 GetWindowRect (hwnd, &rect);
2159 if (rect.left != wp->x)
2160 wp->x += wp->cx - pixwidth;
2161 if (rect.top != wp->y)
2162 wp->y += wp->cy - pixheight;
2168 /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts
2169 window position if the user tries to track window too small */
2173 case WM_ENTERSIZEMOVE:
2174 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2175 msframe->sizing = 1;
2178 case WM_EXITSIZEMOVE:
2179 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2180 msframe->sizing = 0;
2181 /* Queue noop event */
2182 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
2185 #ifdef HAVE_SCROLLBARS
2189 /* Direction of scroll is determined by scrollbar instance. */
2190 int code = (int) LOWORD(wParam);
2191 int pos = (short int) HIWORD(wParam);
2192 HWND hwndScrollBar = (HWND) lParam;
2193 struct gcpro gcpro1, gcpro2;
2195 mswindows_handle_scrollbar_event (hwndScrollBar, code, pos);
2196 GCPRO2 (emacs_event, fobj);
2197 if (UNBOUNDP(mswindows_pump_outstanding_events())) /* Can GC */
2199 /* Error during event pumping - cancel scroll */
2200 SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
2208 int keys = LOWORD (wParam); /* Modifier key flags */
2209 int delta = (short) HIWORD (wParam); /* Wheel rotation amount */
2210 struct gcpro gcpro1, gcpro2;
2212 if (mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd), keys, delta))
2214 GCPRO2 (emacs_event, fobj);
2215 mswindows_pump_outstanding_events (); /* Can GC */
2224 #ifdef HAVE_MENUBARS
2226 if (UNBOUNDP (mswindows_handle_wm_initmenu (
2228 XFRAME (mswindows_find_frame (hwnd)))))
2229 SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2232 case WM_INITMENUPOPUP:
2233 if (!HIWORD(lParam))
2235 if (UNBOUNDP (mswindows_handle_wm_initmenupopup (
2237 XFRAME (mswindows_find_frame (hwnd)))))
2238 SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2242 #endif /* HAVE_MENUBARS */
2246 WORD id = LOWORD (wParam);
2247 WORD nid = HIWORD (wParam);
2248 HWND cid = (HWND)lParam;
2249 frame = XFRAME (mswindows_find_frame (hwnd));
2251 #ifdef HAVE_TOOLBARS
2252 if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id)))
2255 /* widgets in a buffer only eval a callback for suitable events.*/
2260 case CBN_EDITCHANGE:
2262 if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id)))
2265 /* menubars always must come last since the hashtables do not
2267 #ifdef HAVE_MENUBARS
2268 if (!NILP (mswindows_handle_wm_command (frame, id)))
2272 return DefWindowProc (hwnd, message, wParam, lParam);
2273 /* Bite me - a spurious command. This used to not be able to
2274 happen but with the introduction of widgets its now
2279 case WM_CTLCOLORBTN:
2280 case WM_CTLCOLORLISTBOX:
2281 case WM_CTLCOLOREDIT:
2282 case WM_CTLCOLORSTATIC:
2283 case WM_CTLCOLORSCROLLBAR:
2285 /* if we get an opportunity to paint a widget then do so if
2286 there is an appropriate face */
2287 HWND crtlwnd = (HWND)lParam;
2288 LONG ii = GetWindowLong (crtlwnd, GWL_USERDATA);
2291 Lisp_Object image_instance;
2292 VOID_TO_LISP (image_instance, ii);
2293 if (IMAGE_INSTANCEP (image_instance)
2295 IMAGE_INSTANCE_TYPE_P (image_instance, IMAGE_WIDGET))
2297 /* set colors for the buttons */
2298 HDC hdc = (HDC)wParam;
2299 if (last_widget_brushed != ii)
2302 DeleteObject (widget_brush);
2303 widget_brush = CreateSolidBrush
2304 (COLOR_INSTANCE_MSWINDOWS_COLOR
2307 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2308 XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
2310 last_widget_brushed = ii;
2313 COLOR_INSTANCE_MSWINDOWS_COLOR
2316 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2317 XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
2318 SetBkMode (hdc, OPAQUE);
2321 COLOR_INSTANCE_MSWINDOWS_COLOR
2324 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2325 XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
2326 return (LRESULT)widget_brush;
2332 #ifdef HAVE_DRAGNDROP
2333 case WM_DROPFILES: /* implementation ripped-off from event-Xt.c */
2335 UINT filecount, i, len;
2341 Lisp_Object l_dndlist = Qnil, l_item = Qnil;
2342 struct gcpro gcpro1, gcpro2, gcpro3;
2344 emacs_event = Fmake_event (Qnil, Qnil);
2345 event = XEVENT(emacs_event);
2347 GCPRO3 (emacs_event, l_dndlist, l_item);
2349 if (!DragQueryPoint ((HANDLE) wParam, &point))
2350 point.x = point.y = -1; /* outside client area */
2352 event->event_type = misc_user_event;
2353 event->channel = mswindows_find_frame(hwnd);
2354 event->timestamp = GetMessageTime();
2355 event->event.misc.button = 1; /* #### Should try harder */
2356 event->event.misc.modifiers = mswindows_modifier_state (NULL, 0);
2357 event->event.misc.x = point.x;
2358 event->event.misc.y = point.y;
2359 event->event.misc.function = Qdragdrop_drop_dispatch;
2361 filecount = DragQueryFile ((HANDLE) wParam, 0xffffffff, NULL, 0);
2362 for (i=0; i<filecount; i++)
2364 len = DragQueryFile ((HANDLE) wParam, i, NULL, 0);
2365 /* The URLs that we make here aren't correct according to section
2366 * 3.10 of rfc1738 because they're missing the //<host>/ part and
2367 * because they may contain reserved characters. But that's OK. */
2369 fname = (char *)xmalloc (len+1);
2370 DragQueryFile ((HANDLE) wParam, i, fname, len+1);
2371 filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5);
2372 strcpy (filename, "file:");
2373 cygwin32_win32_to_posix_path_list (fname, filename+5);
2376 filename = (char *)xmalloc (len+6);
2377 strcpy (filename, "file:");
2378 DragQueryFile ((HANDLE) wParam, i, filename+5, len+1);
2379 dostounix_filename (filename+5);
2381 l_item = make_string (filename, strlen (filename));
2382 l_dndlist = Fcons (l_item, l_dndlist);
2385 DragFinish ((HANDLE) wParam);
2387 event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist);
2388 mswindows_enqueue_dispatch_event (emacs_event);
2396 return DefWindowProc (hwnd, message, wParam, lParam);
2402 /************************************************************************/
2403 /* keyboard, mouse & other helpers for the windows procedure */
2404 /************************************************************************/
2406 mswindows_set_chord_timer (HWND hwnd)
2410 /* We get one third half system double click threshold */
2411 if (mswindows_mouse_button_tolerance <= 0)
2412 interval = GetDoubleClickTime () / 3;
2414 interval = mswindows_mouse_button_tolerance;
2416 SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0);
2420 mswindows_button2_near_enough (POINTS p1, POINTS p2)
2423 if (mswindows_mouse_button_max_skew_x <= 0)
2424 dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2;
2426 dx = mswindows_mouse_button_max_skew_x;
2428 if (mswindows_mouse_button_max_skew_y <= 0)
2429 dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2;
2431 dy = mswindows_mouse_button_max_skew_y;
2433 return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy;
2437 mswindows_current_layout_has_AltGr (void)
2439 /* This simple caching mechanism saves 10% of CPU
2440 time when a key typed at autorepeat rate of 30 cps! */
2441 static HKL last_hkl = 0;
2442 static int last_hkl_has_AltGr;
2444 HKL current_hkl = GetKeyboardLayout (0);
2445 if (current_hkl != last_hkl)
2448 last_hkl_has_AltGr = 0;
2449 /* In this loop, we query whether a character requires
2450 AltGr to be down to generate it. If at least such one
2451 found, this means that the layout does regard AltGr */
2452 for (c = ' '; c <= 0xFFU && c != 0 && !last_hkl_has_AltGr; ++c)
2453 if (HIBYTE (VkKeyScan (c)) == 6)
2454 last_hkl_has_AltGr = 1;
2455 last_hkl = current_hkl;
2457 return last_hkl_has_AltGr;
2461 /* Returns the state of the modifier keys in the format expected by the
2462 * Lisp_Event key_data, button_data and motion_data modifiers member */
2463 int mswindows_modifier_state (BYTE* keymap, int has_AltGr)
2469 keymap = (BYTE*) alloca(256);
2470 GetKeyboardState (keymap);
2471 has_AltGr = mswindows_current_layout_has_AltGr ();
2474 if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80))
2476 mods |= (keymap [VK_LMENU] & 0x80) ? MOD_META : 0;
2477 mods |= (keymap [VK_RCONTROL] & 0x80) ? MOD_CONTROL : 0;
2481 mods |= (keymap [VK_MENU] & 0x80) ? MOD_META : 0;
2482 mods |= (keymap [VK_CONTROL] & 0x80) ? MOD_CONTROL : 0;
2485 mods |= (keymap [VK_SHIFT] & 0x80) ? MOD_SHIFT : 0;
2491 * Translate a mswindows virtual key to a keysym.
2492 * Only returns non-Qnil for keys that don't generate WM_CHAR messages
2493 * or whose ASCII codes (like space) xemacs doesn't like.
2494 * Virtual key values are defined in winresrc.h
2496 Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
2499 if (extendedp) /* Keys not present on a 82 key keyboard */
2501 switch (mswindows_key)
2503 case VK_RETURN: return KEYSYM ("kp-enter");
2504 case VK_PRIOR: return KEYSYM ("prior");
2505 case VK_NEXT: return KEYSYM ("next");
2506 case VK_END: return KEYSYM ("end");
2507 case VK_HOME: return KEYSYM ("home");
2508 case VK_LEFT: return KEYSYM ("left");
2509 case VK_UP: return KEYSYM ("up");
2510 case VK_RIGHT: return KEYSYM ("right");
2511 case VK_DOWN: return KEYSYM ("down");
2512 case VK_INSERT: return KEYSYM ("insert");
2513 case VK_DELETE: return QKdelete;
2518 switch (mswindows_key)
2520 case VK_BACK: return QKbackspace;
2521 case VK_TAB: return QKtab;
2522 case '\n': return QKlinefeed;
2523 case VK_CLEAR: return KEYSYM ("clear");
2524 case VK_RETURN: return QKreturn;
2525 case VK_ESCAPE: return QKescape;
2526 case VK_SPACE: return QKspace;
2527 case VK_PRIOR: return KEYSYM ("kp-prior");
2528 case VK_NEXT: return KEYSYM ("kp-next");
2529 case VK_END: return KEYSYM ("kp-end");
2530 case VK_HOME: return KEYSYM ("kp-home");
2531 case VK_LEFT: return KEYSYM ("kp-left");
2532 case VK_UP: return KEYSYM ("kp-up");
2533 case VK_RIGHT: return KEYSYM ("kp-right");
2534 case VK_DOWN: return KEYSYM ("kp-down");
2535 case VK_SELECT: return KEYSYM ("select");
2536 case VK_PRINT: return KEYSYM ("print");
2537 case VK_EXECUTE: return KEYSYM ("execute");
2538 case VK_SNAPSHOT: return KEYSYM ("print");
2539 case VK_INSERT: return KEYSYM ("kp-insert");
2540 case VK_DELETE: return KEYSYM ("kp-delete");
2541 case VK_HELP: return KEYSYM ("help");
2542 #if 0 /* FSF Emacs allows these to return configurable syms/mods */
2543 case VK_LWIN return KEYSYM ("");
2544 case VK_RWIN return KEYSYM ("");
2546 case VK_APPS: return KEYSYM ("menu");
2547 case VK_NUMPAD0: return KEYSYM ("kp-0");
2548 case VK_NUMPAD1: return KEYSYM ("kp-1");
2549 case VK_NUMPAD2: return KEYSYM ("kp-2");
2550 case VK_NUMPAD3: return KEYSYM ("kp-3");
2551 case VK_NUMPAD4: return KEYSYM ("kp-4");
2552 case VK_NUMPAD5: return KEYSYM ("kp-5");
2553 case VK_NUMPAD6: return KEYSYM ("kp-6");
2554 case VK_NUMPAD7: return KEYSYM ("kp-7");
2555 case VK_NUMPAD8: return KEYSYM ("kp-8");
2556 case VK_NUMPAD9: return KEYSYM ("kp-9");
2557 case VK_MULTIPLY: return KEYSYM ("kp-multiply");
2558 case VK_ADD: return KEYSYM ("kp-add");
2559 case VK_SEPARATOR: return KEYSYM ("kp-separator");
2560 case VK_SUBTRACT: return KEYSYM ("kp-subtract");
2561 case VK_DECIMAL: return KEYSYM ("kp-decimal");
2562 case VK_DIVIDE: return KEYSYM ("kp-divide");
2563 case VK_F1: return KEYSYM ("f1");
2564 case VK_F2: return KEYSYM ("f2");
2565 case VK_F3: return KEYSYM ("f3");
2566 case VK_F4: return KEYSYM ("f4");
2567 case VK_F5: return KEYSYM ("f5");
2568 case VK_F6: return KEYSYM ("f6");
2569 case VK_F7: return KEYSYM ("f7");
2570 case VK_F8: return KEYSYM ("f8");
2571 case VK_F9: return KEYSYM ("f9");
2572 case VK_F10: return KEYSYM ("f10");
2573 case VK_F11: return KEYSYM ("f11");
2574 case VK_F12: return KEYSYM ("f12");
2575 case VK_F13: return KEYSYM ("f13");
2576 case VK_F14: return KEYSYM ("f14");
2577 case VK_F15: return KEYSYM ("f15");
2578 case VK_F16: return KEYSYM ("f16");
2579 case VK_F17: return KEYSYM ("f17");
2580 case VK_F18: return KEYSYM ("f18");
2581 case VK_F19: return KEYSYM ("f19");
2582 case VK_F20: return KEYSYM ("f20");
2583 case VK_F21: return KEYSYM ("f21");
2584 case VK_F22: return KEYSYM ("f22");
2585 case VK_F23: return KEYSYM ("f23");
2586 case VK_F24: return KEYSYM ("f24");
2593 * Find the console that matches the supplied mswindows window handle
2596 mswindows_find_console (HWND hwnd)
2598 /* We only support one console */
2599 return XCAR (Vconsole_list);
2603 * Find the frame that matches the supplied mswindows window handle
2606 mswindows_find_frame (HWND hwnd)
2608 LONG l = GetWindowLong (hwnd, XWL_FRAMEOBJ);
2612 /* We are in progress of frame creation. Return the frame
2613 being created, as it still not remembered in the window
2615 assert (!NILP (Vmswindows_frame_being_created));
2616 return Vmswindows_frame_being_created;
2618 VOID_TO_LISP (f, l);
2623 /************************************************************************/
2625 /************************************************************************/
2628 emacs_mswindows_add_timeout (EMACS_TIME thyme)
2631 EMACS_TIME current_time;
2632 EMACS_GET_TIME (current_time);
2633 EMACS_SUB_TIME (thyme, thyme, current_time);
2634 milliseconds = EMACS_SECS (thyme) * 1000 +
2635 (EMACS_USECS (thyme) + 500) / 1000;
2636 if (milliseconds < 1)
2638 ++mswindows_pending_timers_count;
2639 return SetTimer (NULL, 0, milliseconds,
2640 (TIMERPROC) mswindows_wm_timer_callback);
2644 emacs_mswindows_remove_timeout (int id)
2646 Lisp_Event match_against;
2647 Lisp_Object emacs_event;
2649 if (KillTimer (NULL, id))
2650 --mswindows_pending_timers_count;
2652 /* If there is a dispatch event generated by this
2653 timeout in the queue, we have to remove it too. */
2654 match_against.event_type = timeout_event;
2655 match_against.event.timeout.interval_id = id;
2656 emacs_event = mswindows_cancel_dispatch_event (&match_against);
2657 if (!NILP (emacs_event))
2658 Fdeallocate_event(emacs_event);
2661 /* If `user_p' is false, then return whether there are any win32, timeout,
2662 * or subprocess events pending (that is, whether
2663 * emacs_mswindows_next_event() would return immediately without blocking).
2665 * if `user_p' is true, then return whether there are any *user generated*
2666 * events available (that is, whether there are keyboard or mouse-click
2667 * events ready to be read). This also implies that
2668 * emacs_mswindows_next_event() would not block.
2671 emacs_mswindows_event_pending_p (int user_p)
2673 mswindows_need_event (0);
2674 return (!NILP (mswindows_u_dispatch_event_queue)
2675 || (!user_p && !NILP (mswindows_s_dispatch_event_queue)));
2679 * Return the next event
2682 emacs_mswindows_next_event (Lisp_Event *emacs_event)
2684 Lisp_Object event, event2;
2686 mswindows_need_event (1);
2688 event = mswindows_dequeue_dispatch_event ();
2689 XSETEVENT (event2, emacs_event);
2690 Fcopy_event (event, event2);
2691 Fdeallocate_event (event);
2695 * Handle a magic event off the dispatch queue.
2698 emacs_mswindows_handle_magic_event (Lisp_Event *emacs_event)
2700 switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event))
2708 Lisp_Object frame = EVENT_CHANNEL (emacs_event);
2709 struct frame *f = XFRAME (frame);
2710 int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS);
2713 /* struct gcpro gcpro1; */
2715 /* Clear sticky modifiers here (if we had any) */
2717 conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil));
2718 /* GCPRO1 (conser); XXX Not necessary? */
2719 emacs_handle_focus_change_preliminary (conser);
2720 /* Under X the stuff up to here is done in the X event handler.
2722 emacs_handle_focus_change_final (conser);
2731 Lisp_Object frame = EVENT_CHANNEL (emacs_event);
2732 va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)
2734 Qmap_frame_hook : Qunmap_frame_hook,
2739 /* #### What about Enter & Leave */
2741 va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook :
2742 Qmouse_leave_frame_hook, 1, frame);
2750 #ifndef HAVE_MSG_SELECT
2752 get_process_input_waitable (Lisp_Process *process)
2754 Lisp_Object instr, outstr, p;
2755 XSETPROCESS (p, process);
2756 get_process_streams (process, &instr, &outstr);
2757 assert (!NILP (instr));
2758 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
2759 return (network_connection_p (p)
2760 ? get_winsock_stream_waitable (XLSTREAM (instr))
2761 : get_ntpipe_input_stream_waitable (XLSTREAM (instr)));
2763 return get_ntpipe_input_stream_waitable (XLSTREAM (instr));
2768 emacs_mswindows_select_process (Lisp_Process *process)
2770 HANDLE hev = get_process_input_waitable (process);
2772 if (!add_waitable_handle (hev))
2773 error ("Too many active processes");
2775 #ifdef HAVE_WIN32_PROCESSES
2778 XSETPROCESS (p, process);
2779 if (!network_connection_p (p))
2781 HANDLE hprocess = get_nt_process_handle (process);
2782 if (!add_waitable_handle (hprocess))
2784 remove_waitable_handle (hev);
2785 error ("Too many active processes");
2793 emacs_mswindows_unselect_process (Lisp_Process *process)
2795 /* Process handle is removed in the event loop as soon
2796 as it is signaled, so don't bother here about it */
2797 HANDLE hev = get_process_input_waitable (process);
2798 remove_waitable_handle (hev);
2800 #endif /* HAVE_MSG_SELECT */
2803 emacs_mswindows_select_console (struct console *con)
2805 #ifdef HAVE_MSG_SELECT
2806 if (CONSOLE_MSWINDOWS_P (con))
2807 return; /* mswindows consoles are automatically selected */
2809 event_stream_unixoid_select_console (con);
2814 emacs_mswindows_unselect_console (struct console *con)
2816 #ifdef HAVE_MSG_SELECT
2817 if (CONSOLE_MSWINDOWS_P (con))
2818 return; /* mswindows consoles are automatically selected */
2820 event_stream_unixoid_unselect_console (con);
2825 emacs_mswindows_quit_p (void)
2827 /* Quit cannot happen in modal loop: all program
2828 input is dedicated to Windows. */
2829 if (mswindows_in_modal_loop)
2832 /* Drain windows queue. This sets up number of quit characters in
2834 mswindows_drain_windows_queue (1);
2836 if (mswindows_quit_chars_count > 0)
2838 /* Yes there's a hidden one... Throw it away */
2839 Lisp_Event match_against;
2840 Lisp_Object emacs_event;
2843 match_against.event_type = key_press_event;
2844 match_against.event.key.modifiers = FAKE_MOD_QUIT;
2846 while (mswindows_quit_chars_count-- > 0)
2848 emacs_event = mswindows_cancel_dispatch_event (&match_against);
2849 assert (!NILP (emacs_event));
2851 if (XEVENT(emacs_event)->event.key.modifiers & MOD_SHIFT)
2854 Fdeallocate_event(emacs_event);
2857 Vquit_flag = critical_p ? Qcritical : Qt;
2862 emacs_mswindows_create_stream_pair (void* inhandle, void* outhandle,
2863 Lisp_Object* instream,
2864 Lisp_Object* outstream,
2867 /* Handles for streams */
2869 /* fds. These just stored along with the streams, and are closed in
2870 delete stream pair method, because we need to handle fake unices
2874 /* Decode inhandle and outhandle. Their meaning depends on
2875 the process implementation being used. */
2876 #if defined (HAVE_WIN32_PROCESSES)
2877 /* We're passed in Windows handles. That's what we like most... */
2878 hin = (HANDLE) inhandle;
2879 hout = (HANDLE) outhandle;
2881 #elif defined (HAVE_UNIX_PROCESSES)
2882 /* We are passed UNIX fds. This must be Cygwin.
2884 hin = inhandle >= 0 ? (HANDLE)get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE;
2885 hout = outhandle >= 0 ? (HANDLE)get_osfhandle ((int)outhandle) : INVALID_HANDLE_VALUE;
2889 #error "So, WHICH kind of processes do you want?"
2892 *instream = (hin == INVALID_HANDLE_VALUE
2894 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
2895 : flags & STREAM_NETWORK_CONNECTION
2896 ? make_winsock_input_stream ((SOCKET)hin, fdi)
2898 : make_ntpipe_input_stream (hin, fdi));
2900 #ifdef HAVE_WIN32_PROCESSES
2901 *outstream = (hout == INVALID_HANDLE_VALUE
2903 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
2904 : flags & STREAM_NETWORK_CONNECTION
2905 ? make_winsock_output_stream ((SOCKET)hout, fdo)
2907 : make_ntpipe_output_stream (hout, fdo));
2908 #elif defined (HAVE_UNIX_PROCESSES)
2909 *outstream = (fdo >= 0
2910 ? make_filedesc_output_stream (fdo, 0, -1, LSTR_BLOCKED_OK)
2913 #if defined(HAVE_UNIX_PROCESSES) && defined(HAVE_PTYS)
2914 /* FLAGS is process->pty_flag for UNIX_PROCESSES */
2915 if ((flags & STREAM_PTY_FLUSHING) && fdo >= 0)
2917 Bufbyte eof_char = get_eof_char (fdo);
2918 int pty_max_bytes = get_pty_max_bytes (fdo);
2919 filedesc_stream_set_pty_flushing (XLSTREAM(*outstream), pty_max_bytes, eof_char);
2924 return (NILP (*instream)
2926 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
2927 : flags & STREAM_NETWORK_CONNECTION
2928 ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream)))
2930 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (*instream))));
2934 emacs_mswindows_delete_stream_pair (Lisp_Object instream,
2935 Lisp_Object outstream)
2937 /* Oh nothing special here for Win32 at all */
2938 #if defined (HAVE_UNIX_PROCESSES)
2939 int in = (NILP(instream)
2941 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
2942 : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
2943 ? get_winsock_stream_param (XLSTREAM (instream))
2945 : get_ntpipe_input_stream_param (XLSTREAM (instream)));
2946 int out = (NILP(outstream) ? -1
2947 : filedesc_stream_fd (XLSTREAM (outstream)));
2951 if (out != in && out >= 0)
2955 return (NILP (instream)
2957 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
2958 : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
2959 ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream)))
2961 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream))));
2964 #ifndef HAVE_X_WINDOWS
2965 /* This is called from GC when a process object is about to be freed.
2966 If we've still got pointers to it in this file, we're gonna lose hard.
2969 debug_process_finalization (Lisp_Process *p)
2972 Lisp_Object instr, outstr;
2974 get_process_streams (p, &instr, &outstr);
2975 /* if it still has fds, then it hasn't been killed yet. */
2976 assert (NILP(instr));
2977 assert (NILP(outstr));
2979 /* #### More checks here */
2984 /************************************************************************/
2985 /* initialization */
2986 /************************************************************************/
2989 reinit_vars_of_event_mswindows (void)
2991 mswindows_in_modal_loop = 0;
2992 mswindows_pending_timers_count = 0;
2994 mswindows_event_stream = xnew (struct event_stream);
2996 mswindows_event_stream->event_pending_p = emacs_mswindows_event_pending_p;
2997 mswindows_event_stream->next_event_cb = emacs_mswindows_next_event;
2998 mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event;
2999 mswindows_event_stream->add_timeout_cb = emacs_mswindows_add_timeout;
3000 mswindows_event_stream->remove_timeout_cb = emacs_mswindows_remove_timeout;
3001 mswindows_event_stream->quit_p_cb = emacs_mswindows_quit_p;
3002 mswindows_event_stream->select_console_cb = emacs_mswindows_select_console;
3003 mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console;
3004 #ifdef HAVE_MSG_SELECT
3005 mswindows_event_stream->select_process_cb =
3006 (void (*)(Lisp_Process*))event_stream_unixoid_select_process;
3007 mswindows_event_stream->unselect_process_cb =
3008 (void (*)(Lisp_Process*))event_stream_unixoid_unselect_process;
3009 mswindows_event_stream->create_stream_pair_cb = event_stream_unixoid_create_stream_pair;
3010 mswindows_event_stream->delete_stream_pair_cb = event_stream_unixoid_delete_stream_pair;
3012 mswindows_event_stream->select_process_cb = emacs_mswindows_select_process;
3013 mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process;
3014 mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair;
3015 mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair;
3020 vars_of_event_mswindows (void)
3022 reinit_vars_of_event_mswindows ();
3024 mswindows_u_dispatch_event_queue = Qnil;
3025 staticpro (&mswindows_u_dispatch_event_queue);
3026 mswindows_u_dispatch_event_queue_tail = Qnil;
3027 pdump_wire (&mswindows_u_dispatch_event_queue_tail);
3029 mswindows_s_dispatch_event_queue = Qnil;
3030 staticpro (&mswindows_s_dispatch_event_queue);
3031 mswindows_s_dispatch_event_queue_tail = Qnil;
3032 pdump_wire (&mswindows_s_dispatch_event_queue_tail);
3034 mswindows_error_caught_in_modal_loop = Qnil;
3035 staticpro (&mswindows_error_caught_in_modal_loop);
3037 DEFVAR_BOOL ("mswindows-meta-activates-menu", &mswindows_meta_activates_menu /*
3038 *Controls whether pressing and releasing the Meta (Alt) key should
3039 activate the menubar.
3043 DEFVAR_BOOL ("mswindows-dynamic-frame-resize", &mswindows_dynamic_frame_resize /*
3044 *Controls redrawing frame contents during mouse-drag or keyboard resize
3045 operation. When non-nil, the frame is redrawn while being resized. When
3046 nil, frame is not redrawn, and exposed areas are filled with default
3047 MDI application background color. Note that this option only has effect
3048 if "Show window contents while dragging" is on in system Display/Plus!
3050 Default is t on fast machines, nil on slow.
3053 /* The description copied verbatim from nt-emacs. (C) Geoff Voelker */
3054 DEFVAR_INT ("mswindows-mouse-button-tolerance", &mswindows_mouse_button_tolerance /*
3055 *Analogue of double click interval for faking middle mouse events.
3056 The value is the minimum time in milliseconds that must elapse between
3057 left/right button down events before they are considered distinct events.
3058 If both mouse buttons are depressed within this interval, a middle mouse
3059 button down event is generated instead.
3060 If negative or zero, currently set system default is used instead.
3063 /* The description copied verbatim from nt-emacs. (C) Geoff Voelker */
3064 DEFVAR_INT ("mswindows-num-mouse-buttons", &mswindows_num_mouse_buttons /*
3065 Number of physical mouse buttons.
3068 DEFVAR_INT ("mswindows-mouse-button-max-skew-x", &mswindows_mouse_button_max_skew_x /*
3069 *Maximum horizontal distance in pixels between points in which left and
3070 right button clicks occurred for them to be translated into single
3071 middle button event. Clicks must occur in time not longer than defined
3072 by the variable `mswindows-mouse-button-tolerance'.
3073 If negative or zero, currently set system default is used instead.
3076 DEFVAR_INT ("mswindows-mouse-button-max-skew-y", &mswindows_mouse_button_max_skew_y /*
3077 *Maximum vertical distance in pixels between points in which left and
3078 right button clicks occurred for them to be translated into single
3079 middle button event. Clicks must occur in time not longer than defined
3080 by the variable `mswindows-mouse-button-tolerance'.
3081 If negative or zero, currently set system default is used instead.
3084 mswindows_mouse_button_max_skew_x = 0;
3085 mswindows_mouse_button_max_skew_y = 0;
3086 mswindows_mouse_button_tolerance = 0;
3087 mswindows_meta_activates_menu = 1;
3091 syms_of_event_mswindows (void)
3096 lstream_type_create_mswindows_selectable (void)
3098 init_slurp_stream ();
3099 init_shove_stream ();
3100 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3101 init_winsock_stream ();
3106 init_event_mswindows_late (void)
3108 #ifdef HAVE_MSG_SELECT
3109 windows_fd = open("/dev/windows", O_RDONLY | O_NONBLOCK, 0);
3110 assert (windows_fd>=0);
3111 FD_SET (windows_fd, &input_wait_mask);
3112 FD_ZERO(&zero_mask);
3115 event_stream = mswindows_event_stream;
3117 mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE);
3118 mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);