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__) && !defined (CYGWIN_VERSION_DLL_MAJOR)
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
94 mswindows_get_toolbar_button_text (struct frame* f, int command_id);
96 mswindows_handle_toolbar_wm_command (struct frame* f, HWND ctrl, WORD id);
98 mswindows_handle_gui_wm_command (struct frame* f, HWND ctrl, WORD id);
100 static Lisp_Object mswindows_find_frame (HWND hwnd);
101 static Lisp_Object mswindows_find_console (HWND hwnd);
102 static Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
104 static int mswindows_modifier_state (BYTE* keymap, int has_AltGr);
105 static void mswindows_set_chord_timer (HWND hwnd);
106 static int mswindows_button2_near_enough (POINTS p1, POINTS p2);
107 static int mswindows_current_layout_has_AltGr (void);
109 static struct event_stream *mswindows_event_stream;
111 #ifdef HAVE_MSG_SELECT
112 extern SELECT_TYPE input_wait_mask, non_fake_input_wait_mask;
113 extern SELECT_TYPE process_only_mask, tty_only_mask;
114 SELECT_TYPE zero_mask;
115 extern int signal_event_pipe_initialized;
120 * Two separate queues, for efficiency, one (_u_) for user events, and
121 * another (_s_) for non-user ones. We always return events out of the
122 * first one until it is empty and only then proceed with the second
125 static Lisp_Object mswindows_u_dispatch_event_queue, mswindows_u_dispatch_event_queue_tail;
126 static Lisp_Object mswindows_s_dispatch_event_queue, mswindows_s_dispatch_event_queue_tail;
128 /* The number of things we can wait on */
129 #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1)
131 #ifndef HAVE_MSG_SELECT
132 /* List of mswindows waitable handles. */
133 static HANDLE mswindows_waitable_handles[MAX_WAITABLE];
135 /* Number of wait handles */
136 static int mswindows_waitable_count=0;
137 #endif /* HAVE_MSG_SELECT */
138 /* Brush for painting widgets */
139 static HBRUSH widget_brush = 0;
140 static LONG last_widget_brushed = 0;
142 /* Count of quit chars currently in the queue */
143 /* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc()
144 Decremented in mswindows_dequeue_dispatch_event() */
145 int mswindows_quit_chars_count = 0;
147 /* These are Lisp integers; see DEFVARS in this file for description. */
148 int mswindows_dynamic_frame_resize;
149 int mswindows_meta_activates_menu;
150 int mswindows_num_mouse_buttons;
151 int mswindows_mouse_button_max_skew_x;
152 int mswindows_mouse_button_max_skew_y;
153 int mswindows_mouse_button_tolerance;
155 /* This is the event signaled by the event pump.
156 See mswindows_pump_outstanding_events for comments */
157 static Lisp_Object mswindows_error_caught_in_modal_loop;
158 static int mswindows_in_modal_loop;
160 /* Count of wound timers */
161 static int mswindows_pending_timers_count;
163 /************************************************************************/
164 /* Pipe instream - reads process output */
165 /************************************************************************/
167 #define PIPE_READ_DELAY 20
169 #define HANDLE_TO_USID(h) ((USID)(h))
171 #define NTPIPE_SLURP_STREAM_DATA(stream) \
172 LSTREAM_TYPE_DATA (stream, ntpipe_slurp)
174 /* This structure is allocated by the main thread, and is deallocated
175 in the thread upon exit. There are situations when a thread
176 remains blocked for a long time, much longer than the lstream
177 exists. For example, "start notepad" command is issued from the
178 shell, then the shell is closed by C-c C-d. Although the shell
179 process exits, its output pipe will not get closed until the
180 notepad process exits also, because it inherits the pipe form the
181 shell. In this case, we abandon the thread, and let it live until
182 all such processes exit. While struct ntpipe_slurp_stream is
183 deallocated in this case, ntpipe_slurp_stream_shared_data are not. */
185 struct ntpipe_slurp_stream_shared_data
187 HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */
188 /* This is a manual-reset object. */
189 HANDLE hev_caller; /* Caller blocks on this, and we signal it */
190 /* This is a manual-reset object. */
191 HANDLE hev_unsleep; /* Pipe read delay is canceled if this is set */
192 /* This is a manual-reset object. */
193 HANDLE hpipe; /* Pipe read end handle. */
194 LONG die_p; /* Thread must exit ASAP if non-zero */
195 BOOL eof_p : 1; /* Set when thread saw EOF */
196 BOOL error_p : 1; /* Read error other than EOF/broken pipe */
197 BOOL inuse_p : 1; /* this structure is in use */
198 LONG lock_count; /* Client count of this struct, 0=safe to free */
199 BYTE onebyte; /* One byte buffer read by thread */
202 #define MAX_SLURP_STREAMS 32
203 struct ntpipe_slurp_stream_shared_data
204 shared_data_block[MAX_SLURP_STREAMS]={{0}};
206 struct ntpipe_slurp_stream
208 LPARAM user_data; /* Any user data stored in the stream object */
209 struct ntpipe_slurp_stream_shared_data* thread_data;
212 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-input", lstream_ntpipe_slurp,
213 sizeof (struct ntpipe_slurp_stream));
215 /* This function is thread-safe, and is called from either thread
216 context. It serializes freeing shared data structure */
218 slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s)
220 if (InterlockedDecrement (&s->lock_count) == 0)
223 CloseHandle (s->hev_thread);
224 CloseHandle (s->hev_caller);
225 CloseHandle (s->hev_unsleep);
230 static struct ntpipe_slurp_stream_shared_data*
231 slurper_allocate_shared_data()
234 for (i=0; i<MAX_SLURP_STREAMS; i++)
236 if (!shared_data_block[i].inuse_p)
238 shared_data_block[i].inuse_p=1;
239 return &shared_data_block[i];
242 return (struct ntpipe_slurp_stream_shared_data*)0;
246 slurp_thread (LPVOID vparam)
248 struct ntpipe_slurp_stream_shared_data *s =
249 (struct ntpipe_slurp_stream_shared_data*)vparam;
253 /* Read one byte from the pipe */
255 if (!ReadFile (s->hpipe, &s->onebyte, 1, &actually_read, NULL))
257 DWORD err = GetLastError ();
258 if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA)
263 else if (actually_read == 0)
266 /* We must terminate on an error or eof */
267 if (s->eof_p || s->error_p)
268 InterlockedIncrement (&s->die_p);
270 /* Before we notify caller, we unsignal our event. */
271 ResetEvent (s->hev_thread);
273 /* Now we got something to notify caller, either a byte or an
274 error/eof indication. Before we do, allow internal pipe
275 buffer to accumulate little bit more data.
276 Reader function pulses this event before waiting for
277 a character, to avoid pipe delay, and to get the byte
280 WaitForSingleObject (s->hev_unsleep, PIPE_READ_DELAY);
282 /* Either make event loop generate a process event, or
284 SetEvent (s->hev_caller);
286 /* Cleanup and exit if we're shot off */
290 /* Block until the client finishes with retrieving the rest of
292 WaitForSingleObject (s->hev_thread, INFINITE);
295 slurper_free_shared_data_maybe (s);
301 make_ntpipe_input_stream (HANDLE hpipe, LPARAM param)
304 Lstream *lstr = Lstream_new (lstream_ntpipe_slurp, "r");
305 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA (lstr);
306 DWORD thread_id_unused;
309 /* We deal only with pipes, for we're using PeekNamedPipe api */
310 assert (GetFileType (hpipe) == FILE_TYPE_PIPE);
312 s->thread_data = slurper_allocate_shared_data();
314 /* Create reader thread. This could fail, so do not create events
315 until thread is created */
316 hthread = CreateThread (NULL, 0, slurp_thread, (LPVOID)s->thread_data,
317 CREATE_SUSPENDED, &thread_id_unused);
320 Lstream_delete (lstr);
321 s->thread_data->inuse_p=0;
325 /* Shared data are initially owned by both main and slurper
327 s->thread_data->lock_count = 2;
328 s->thread_data->die_p = 0;
329 s->thread_data->eof_p = FALSE;
330 s->thread_data->error_p = FALSE;
331 s->thread_data->hpipe = hpipe;
332 s->user_data = param;
334 /* hev_thread is a manual-reset event, initially signaled */
335 s->thread_data->hev_thread = CreateEvent (NULL, TRUE, TRUE, NULL);
336 /* hev_caller is a manual-reset event, initially nonsignaled */
337 s->thread_data->hev_caller = CreateEvent (NULL, TRUE, FALSE, NULL);
338 /* hev_unsleep is a manual-reset event, initially nonsignaled */
339 s->thread_data->hev_unsleep = CreateEvent (NULL, TRUE, FALSE, NULL);
342 ResumeThread (hthread);
343 CloseHandle (hthread);
345 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
346 XSETLSTREAM (obj, lstr);
351 get_ntpipe_input_stream_param (Lstream *stream)
353 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream);
358 get_ntpipe_input_stream_waitable (Lstream *stream)
360 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream);
361 return s->thread_data->hev_caller;
365 ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size)
367 /* This function must be called from the main thread only */
368 struct ntpipe_slurp_stream_shared_data* s =
369 NTPIPE_SLURP_STREAM_DATA(stream)->thread_data;
374 /* Disallow pipe read delay for the thread: we need a character
376 SetEvent (s->hev_unsleep);
378 /* Check if we have a character ready. Give it a short delay,
379 for the thread to awake from pipe delay, just ion case*/
380 wait_result = WaitForSingleObject (s->hev_caller, 2);
382 /* Revert to the normal sleep behavior. */
383 ResetEvent (s->hev_unsleep);
385 /* If there's no byte buffered yet, give up */
386 if (wait_result == WAIT_TIMEOUT)
393 /* Reset caller unlock event now, as we've handled the pending
394 process output event */
395 ResetEvent (s->hev_caller);
397 /* It is now safe to do anything with contents of S, except for
398 changing s->die_p, which still should be interlocked */
402 if (s->error_p || s->die_p)
405 /* Ok, there were no error neither eof - we've got a byte from the
407 *(data++) = s->onebyte;
411 DWORD bytes_read = 0;
414 DWORD bytes_available;
416 /* If the api call fails, return at least one byte already
417 read. ReadFile in thread will return error */
418 if (PeekNamedPipe (s->hpipe, NULL, 0, NULL, &bytes_available, NULL))
421 /* Fetch available bytes. The same consideration applies,
422 so do not check for errors. ReadFile in the thread will
423 fail if the next call fails. */
425 ReadFile (s->hpipe, data, min (bytes_available, size),
429 /* Now we can unblock thread, so it attempts to read more */
430 SetEvent (s->hev_thread);
431 return bytes_read + 1;
438 ntpipe_slurp_closer (Lstream *stream)
440 /* This function must be called from the main thread only */
441 struct ntpipe_slurp_stream_shared_data* s =
442 NTPIPE_SLURP_STREAM_DATA(stream)->thread_data;
444 /* Force thread to stop */
445 InterlockedIncrement (&s->die_p);
447 /* Set events which could possibly block slurper. Let it finish soon
449 SetEvent (s->hev_unsleep);
450 SetEvent (s->hev_thread);
452 /* Unlock and maybe free shared data */
453 slurper_free_shared_data_maybe (s);
459 init_slurp_stream (void)
461 LSTREAM_HAS_METHOD (ntpipe_slurp, reader);
462 LSTREAM_HAS_METHOD (ntpipe_slurp, closer);
465 /************************************************************************/
466 /* Pipe outstream - writes process input */
467 /************************************************************************/
469 #define NTPIPE_SHOVE_STREAM_DATA(stream) \
470 LSTREAM_TYPE_DATA (stream, ntpipe_shove)
472 #define MAX_SHOVE_BUFFER_SIZE 128
474 struct ntpipe_shove_stream
476 LPARAM user_data; /* Any user data stored in the stream object */
477 HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */
478 /* This is an auto-reset object. */
479 HANDLE hpipe; /* Pipe write end handle. */
480 HANDLE hthread; /* Reader thread handle. */
481 char buffer[MAX_SHOVE_BUFFER_SIZE]; /* Buffer being written */
482 DWORD size; /* Number of bytes to write */
483 LONG die_p; /* Thread must exit ASAP if non-zero */
484 LONG idle_p; /* Non-zero if thread is waiting for job */
485 BOOL error_p : 1; /* Read error other than EOF/broken pipe */
486 BOOL blocking_p : 1;/* Last write attempt would cause blocking */
489 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-output", lstream_ntpipe_shove,
490 sizeof (struct ntpipe_shove_stream));
492 #ifndef HAVE_MSG_SELECT
494 shove_thread (LPVOID vparam)
496 struct ntpipe_shove_stream *s = (struct ntpipe_shove_stream*) vparam;
502 /* Block on event and wait for a job */
503 InterlockedIncrement (&s->idle_p);
504 WaitForSingleObject (s->hev_thread, INFINITE);
509 /* Write passed buffer */
510 if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL)
511 || bytes_written != s->size)
514 InterlockedIncrement (&s->die_p);
525 make_ntpipe_output_stream (HANDLE hpipe, LPARAM param)
528 Lstream *lstr = Lstream_new (lstream_ntpipe_shove, "w");
529 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA (lstr);
530 DWORD thread_id_unused;
535 s->user_data = param;
537 /* Create reader thread. This could fail, so do not
538 create the event until thread is created */
539 s->hthread = CreateThread (NULL, 0, shove_thread, (LPVOID)s,
540 CREATE_SUSPENDED, &thread_id_unused);
541 if (s->hthread == NULL)
543 Lstream_delete (lstr);
547 /* hev_thread is an auto-reset event, initially nonsignaled */
548 s->hev_thread = CreateEvent (NULL, FALSE, FALSE, NULL);
551 ResumeThread (s->hthread);
553 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
554 XSETLSTREAM (obj, lstr);
559 get_ntpipe_output_stream_param (Lstream *stream)
561 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
567 ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size)
569 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
574 s->blocking_p = !s->idle_p;
578 if (size>MAX_SHOVE_BUFFER_SIZE)
581 memcpy (s->buffer, data, size);
585 InterlockedDecrement (&s->idle_p);
586 SetEvent (s->hev_thread);
591 ntpipe_shove_was_blocked_p (Lstream *stream)
593 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
594 return s->blocking_p;
598 ntpipe_shove_closer (Lstream *stream)
600 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream);
602 /* Force thread stop */
603 InterlockedIncrement (&s->die_p);
605 /* Close pipe handle, possibly breaking it */
606 CloseHandle (s->hpipe);
608 /* Thread will end upon unblocking */
609 SetEvent (s->hev_thread);
611 /* Wait while thread terminates */
612 WaitForSingleObject (s->hthread, INFINITE);
613 CloseHandle (s->hthread);
615 /* Destroy the event */
616 CloseHandle (s->hev_thread);
622 init_shove_stream (void)
624 LSTREAM_HAS_METHOD (ntpipe_shove, writer);
625 LSTREAM_HAS_METHOD (ntpipe_shove, was_blocked_p);
626 LSTREAM_HAS_METHOD (ntpipe_shove, closer);
629 /************************************************************************/
630 /* Winsock I/O stream */
631 /************************************************************************/
632 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
634 #define WINSOCK_READ_BUFFER_SIZE 1024
636 struct winsock_stream
638 LPARAM user_data; /* Any user data stored in the stream object */
639 SOCKET s; /* Socket handle (which is a Win32 handle) */
640 OVERLAPPED ov; /* Overlapped I/O structure */
641 void* buffer; /* Buffer. Allocated for input stream only */
642 unsigned int bufsize; /* Number of bytes last read */
643 unsigned int bufpos; /* Position in buffer for next fetch */
644 unsigned int error_p :1; /* I/O Error seen */
645 unsigned int eof_p :1; /* EOF Error seen */
646 unsigned int pending_p :1; /* There is a pending I/O operation */
647 unsigned int blocking_p :1; /* Last write attempt would block */
650 #define WINSOCK_STREAM_DATA(stream) LSTREAM_TYPE_DATA (stream, winsock)
652 DEFINE_LSTREAM_IMPLEMENTATION ("winsock", lstream_winsock,
653 sizeof (struct winsock_stream));
656 winsock_initiate_read (struct winsock_stream *str)
658 ResetEvent (str->ov.hEvent);
661 if (!ReadFile ((HANDLE)str->s, str->buffer, WINSOCK_READ_BUFFER_SIZE,
662 &str->bufsize, &str->ov))
664 if (GetLastError () == ERROR_IO_PENDING)
666 else if (GetLastError () == ERROR_HANDLE_EOF)
671 else if (str->bufsize == 0)
676 winsock_reader (Lstream *stream, unsigned char *data, size_t size)
678 struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
680 /* If the current operation is not yet complete, there's nothing to
684 if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT)
691 if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &str->bufsize, TRUE))
693 if (GetLastError() == ERROR_HANDLE_EOF)
698 if (str->bufsize == 0)
709 /* Return as much of buffer as we have */
710 size = min (size, (size_t) (str->bufsize - str->bufpos));
711 memcpy (data, (void*)((BYTE*)str->buffer + str->bufpos), size);
714 /* Read more if buffer is exhausted */
715 if (str->bufsize == str->bufpos)
716 winsock_initiate_read (str);
722 winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size)
724 struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
728 if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT)
736 if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &dw_unused, TRUE))
751 ResetEvent (str->ov.hEvent);
753 /* Docs indicate that 4th parameter to WriteFile can be NULL since this is
754 * an overlapped operation. This fails on Win95 with winsock 1.x so we
755 * supply a spare address which is ignored by Win95 anyway. Sheesh. */
756 if (WriteFile ((HANDLE)str->s, data, size, (LPDWORD)&str->buffer, &str->ov)
757 || GetLastError() == ERROR_IO_PENDING)
763 return str->error_p ? -1 : size;
767 winsock_closer (Lstream *lstr)
769 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
771 if (lstr->flags & LSTREAM_FL_READ)
772 shutdown (str->s, 0);
774 shutdown (str->s, 1);
776 CloseHandle ((HANDLE)str->s);
778 WaitForSingleObject (str->ov.hEvent, INFINITE);
780 if (lstr->flags & LSTREAM_FL_READ)
783 CloseHandle (str->ov.hEvent);
788 winsock_was_blocked_p (Lstream *stream)
790 struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
791 return str->blocking_p;
795 make_winsock_stream_1 (SOCKET s, LPARAM param, CONST char *mode)
798 Lstream *lstr = Lstream_new (lstream_winsock, mode);
799 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
806 str->user_data = param;
809 str->ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
811 if (lstr->flags & LSTREAM_FL_READ)
813 str->buffer = xmalloc (WINSOCK_READ_BUFFER_SIZE);
814 winsock_initiate_read (str);
817 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
818 XSETLSTREAM (obj, lstr);
823 make_winsock_input_stream (SOCKET s, LPARAM param)
825 return make_winsock_stream_1 (s, param, "r");
829 make_winsock_output_stream (SOCKET s, LPARAM param)
831 return make_winsock_stream_1 (s, param, "w");
835 get_winsock_stream_waitable (Lstream *lstr)
837 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
838 return str->ov.hEvent;
842 get_winsock_stream_param (Lstream *lstr)
844 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
845 return str->user_data;
849 init_winsock_stream (void)
851 LSTREAM_HAS_METHOD (winsock, reader);
852 LSTREAM_HAS_METHOD (winsock, writer);
853 LSTREAM_HAS_METHOD (winsock, closer);
854 LSTREAM_HAS_METHOD (winsock, was_blocked_p);
856 #endif /* defined (HAVE_SOCKETS) */
858 /************************************************************************/
859 /* Dispatch queue management */
860 /************************************************************************/
863 mswindows_user_event_p (struct Lisp_Event* sevt)
865 return (sevt->event_type == key_press_event
866 || sevt->event_type == button_press_event
867 || sevt->event_type == button_release_event
868 || sevt->event_type == misc_user_event);
872 * Add an emacs event to the proper dispatch queue
875 mswindows_enqueue_dispatch_event (Lisp_Object event)
877 int user_p = mswindows_user_event_p (XEVENT(event));
878 enqueue_event (event,
879 user_p ? &mswindows_u_dispatch_event_queue :
880 &mswindows_s_dispatch_event_queue,
881 user_p ? &mswindows_u_dispatch_event_queue_tail :
882 &mswindows_s_dispatch_event_queue_tail);
884 /* Avoid blocking on WaitMessage */
885 PostMessage (NULL, XM_BUMPQUEUE, 0, 0);
889 * Add a misc-user event to the dispatch queue.
891 * Stuff it into our own dispatch queue, so we have something
892 * to return from next_event callback.
895 mswindows_enqueue_misc_user_event (Lisp_Object channel, Lisp_Object function,
898 Lisp_Object event = Fmake_event (Qnil, Qnil);
899 struct Lisp_Event* e = XEVENT (event);
901 e->event_type = misc_user_event;
902 e->channel = channel;
903 e->event.misc.function = function;
904 e->event.misc.object = object;
906 mswindows_enqueue_dispatch_event (event);
910 mswindows_enqueue_magic_event (HWND hwnd, UINT message)
912 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
913 struct Lisp_Event* event = XEVENT (emacs_event);
915 event->channel = hwnd ? mswindows_find_frame (hwnd) : Qnil;
916 event->timestamp = GetMessageTime();
917 event->event_type = magic_event;
918 EVENT_MSWINDOWS_MAGIC_TYPE (event) = message;
920 mswindows_enqueue_dispatch_event (emacs_event);
924 mswindows_enqueue_process_event (struct Lisp_Process* p)
926 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
927 struct Lisp_Event* event = XEVENT (emacs_event);
929 XSETPROCESS (process, p);
931 event->event_type = process_event;
932 event->timestamp = GetTickCount ();
933 event->event.process.process = process;
935 mswindows_enqueue_dispatch_event (emacs_event);
939 mswindows_enqueue_mouse_button_event (HWND hwnd, UINT message, POINTS where, DWORD when)
942 /* We always use last message time, because mouse button
943 events may get delayed, and XEmacs double click
944 recognition will fail */
946 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
947 struct Lisp_Event* event = XEVENT(emacs_event);
949 event->channel = mswindows_find_frame(hwnd);
950 event->timestamp = when;
951 event->event.button.button =
952 (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 :
953 ((message==WM_RBUTTONDOWN || message==WM_RBUTTONUP) ? 3 : 2);
954 event->event.button.x = where.x;
955 event->event.button.y = where.y;
956 event->event.button.modifiers = mswindows_modifier_state (NULL, 0);
958 if (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN ||
959 message==WM_RBUTTONDOWN)
961 event->event_type = button_press_event;
963 /* we need this to make sure the main window regains the focus
964 from control subwindows */
965 if (GetFocus() != hwnd)
968 mswindows_enqueue_magic_event (hwnd, WM_SETFOCUS);
973 event->event_type = button_release_event;
977 mswindows_enqueue_dispatch_event (emacs_event);
981 mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods)
983 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
984 struct Lisp_Event* event = XEVENT(emacs_event);
986 event->channel = mswindows_find_console(hwnd);
987 event->timestamp = GetMessageTime();
988 event->event_type = key_press_event;
989 event->event.key.keysym = keysym;
990 event->event.key.modifiers = mods;
991 mswindows_enqueue_dispatch_event (emacs_event);
995 * Remove and return the first emacs event on the dispatch queue.
996 * Give a preference to user events over non-user ones.
999 mswindows_dequeue_dispatch_event ()
1002 struct Lisp_Event* sevt;
1004 assert (!NILP(mswindows_u_dispatch_event_queue) ||
1005 !NILP(mswindows_s_dispatch_event_queue));
1007 event = dequeue_event (
1008 NILP(mswindows_u_dispatch_event_queue) ?
1009 &mswindows_s_dispatch_event_queue :
1010 &mswindows_u_dispatch_event_queue,
1011 NILP(mswindows_u_dispatch_event_queue) ?
1012 &mswindows_s_dispatch_event_queue_tail :
1013 &mswindows_u_dispatch_event_queue_tail);
1015 sevt = XEVENT(event);
1016 if (sevt->event_type == key_press_event
1017 && (sevt->event.key.modifiers & FAKE_MOD_QUIT))
1019 sevt->event.key.modifiers &= ~FAKE_MOD_QUIT;
1020 --mswindows_quit_chars_count;
1027 * Remove and return the first emacs event on the dispatch queue that matches
1028 * the supplied event.
1029 * Timeout event matches if interval_id is equal to that of the given event.
1030 * Keypress event matches if logical AND between modifiers bitmask of the
1031 * event in the queue and that of the given event is non-zero.
1032 * For all other event types, this function aborts.
1036 mswindows_cancel_dispatch_event (struct Lisp_Event *match)
1039 Lisp_Object previous_event = Qnil;
1040 int user_p = mswindows_user_event_p (match);
1041 Lisp_Object* head = user_p ? &mswindows_u_dispatch_event_queue :
1042 &mswindows_s_dispatch_event_queue;
1043 Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail :
1044 &mswindows_s_dispatch_event_queue_tail;
1046 assert (match->event_type == timeout_event
1047 || match->event_type == key_press_event);
1049 EVENT_CHAIN_LOOP (event, *head)
1051 struct Lisp_Event *e = XEVENT (event);
1052 if ((e->event_type == match->event_type) &&
1053 ((e->event_type == timeout_event) ?
1054 (e->event.timeout.interval_id == match->event.timeout.interval_id) :
1055 /* Must be key_press_event */
1056 ((e->event.key.modifiers & match->event.key.modifiers) != 0)))
1058 if (NILP (previous_event))
1059 dequeue_event (head, tail);
1062 XSET_EVENT_NEXT (previous_event, XEVENT_NEXT (event));
1063 if (EQ (*tail, event))
1064 *tail = previous_event;
1069 previous_event = event;
1074 #ifndef HAVE_MSG_SELECT
1075 /************************************************************************/
1076 /* Waitable handles manipulation */
1077 /************************************************************************/
1079 find_waitable_handle (HANDLE h)
1082 for (i = 0; i < mswindows_waitable_count; ++i)
1083 if (mswindows_waitable_handles[i] == h)
1090 add_waitable_handle (HANDLE h)
1092 assert (find_waitable_handle (h) < 0);
1093 if (mswindows_waitable_count == MAX_WAITABLE)
1096 mswindows_waitable_handles [mswindows_waitable_count++] = h;
1101 remove_waitable_handle (HANDLE h)
1103 int ix = find_waitable_handle (h);
1107 mswindows_waitable_handles [ix] =
1108 mswindows_waitable_handles [--mswindows_waitable_count];
1110 #endif /* HAVE_MSG_SELECT */
1113 /************************************************************************/
1115 /************************************************************************/
1118 mswindows_modal_loop_error_handler (Lisp_Object cons_sig_data,
1119 Lisp_Object u_n_u_s_e_d)
1121 mswindows_error_caught_in_modal_loop = cons_sig_data;
1126 mswindows_protect_modal_loop (Lisp_Object (*bfun) (Lisp_Object barg),
1131 ++mswindows_in_modal_loop;
1132 tmp = condition_case_1 (Qt,
1134 mswindows_modal_loop_error_handler, Qnil);
1135 --mswindows_in_modal_loop;
1141 mswindows_unmodalize_signal_maybe (void)
1143 if (!NILP (mswindows_error_caught_in_modal_loop))
1145 /* Got an error while messages were pumped while
1146 in window procedure - have to resignal */
1147 Lisp_Object sym = XCAR (mswindows_error_caught_in_modal_loop);
1148 Lisp_Object data = XCDR (mswindows_error_caught_in_modal_loop);
1149 mswindows_error_caught_in_modal_loop = Qnil;
1150 Fsignal (sym, data);
1155 * This is an unsafe part of event pump, guarded by
1156 * condition_case. See mswindows_pump_outstanding_events
1159 mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d)
1161 /* This function can call lisp */
1162 Lisp_Object event = Fmake_event (Qnil, Qnil);
1163 struct gcpro gcpro1;
1164 int do_redisplay = 0;
1167 while (detect_input_pending ())
1169 Fnext_event (event, Qnil);
1170 Fdispatch_event (event);
1177 Fdeallocate_event (event);
1180 /* Qt becomes return value of mswindows_pump_outstanding_events
1186 * This function pumps emacs events, while available, by using
1187 * next_message/dispatch_message loop. Errors are trapped around
1188 * the loop so the function always returns.
1190 * Windows message queue is not looked into during the call,
1191 * neither are waitable handles checked. The function pumps
1192 * thus only dispatch events already queued, as well as those
1193 * resulted in dispatching thereof. This is done by setting
1194 * module local variable mswindows_in_modal_loop to nonzero.
1196 * Return value is Qt if no errors was trapped, or Qunbound if
1197 * there was an error.
1199 * In case of error, a cons representing the error, in the
1200 * form (SIGNAL . DATA), is stored in the module local variable
1201 * mswindows_error_caught_in_modal_loop. This error is signaled
1202 * again when DispatchMessage returns. Thus, Windows internal
1203 * modal loops are protected against throws, which are proven
1204 * to corrupt internal Windows structures.
1206 * In case of success, mswindows_error_caught_in_modal_loop is
1209 * If the value of mswindows_error_caught_in_modal_loop is not
1210 * nil already upon entry, the function just returns non-nil.
1211 * This situation means that a new event has been queued while
1212 * in cancel mode. The event will be dequeued on the next regular
1213 * call of next-event; the pump is off since error is caught.
1214 * The caller must *unconditionally* cancel modal loop if the
1215 * value returned by this function is nil. Otherwise, everything
1216 * will become frozen until the modal loop exits under normal
1217 * condition (scrollbar drag is released, menu closed etc.)
1220 mswindows_pump_outstanding_events (void)
1222 /* This function can call lisp */
1224 Lisp_Object result = Qt;
1225 struct gcpro gcpro1;
1228 if (NILP(mswindows_error_caught_in_modal_loop))
1229 result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil);
1235 mswindows_drain_windows_queue ()
1238 while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
1240 /* we have to translate messages that are not sent to the main
1241 window. this is so that key presses work ok in things like
1242 edit fields. however, we *musn't* translate message for the
1243 main window as this is handled in the wnd proc. */
1244 if ( GetWindowLong (msg.hwnd, GWL_STYLE) & WS_CHILD )
1246 TranslateMessage (&msg);
1248 DispatchMessage (&msg);
1249 mswindows_unmodalize_signal_maybe ();
1254 * This is a special flavor of the mswindows_need_event function,
1255 * used while in event pump. Actually, there is only kind of events
1256 * allowed while in event pump: a timer. An attempt to fetch any
1257 * other event leads to a deadlock, as there's no source of user input
1258 * ('cause event pump mirrors windows modal loop, which is a sole
1259 * owner of thread message queue).
1261 * To detect this, we use a counter of active timers, and allow
1262 * fetching WM_TIMER messages. Instead of trying to fetch a WM_TIMER
1263 * which will never come when there are no pending timers, which leads
1264 * to deadlock, we simply signal an error.
1267 mswindows_need_event_in_modal_loop (int badly_p)
1271 /* Check if already have one */
1272 if (!NILP (mswindows_u_dispatch_event_queue)
1273 || !NILP (mswindows_s_dispatch_event_queue))
1276 /* No event is ok */
1280 /* We do not check the _u_ queue, because timers go to _s_ */
1281 while (NILP (mswindows_s_dispatch_event_queue))
1283 /* We'll deadlock if go waiting */
1284 if (mswindows_pending_timers_count == 0)
1285 error ("Deadlock due to an attempt to call next-event in a wrong context");
1287 /* Fetch and dispatch any pending timers */
1288 GetMessage (&msg, NULL, WM_TIMER, WM_TIMER);
1289 DispatchMessage (&msg);
1294 * This drains the event queue and fills up two internal queues until
1295 * an event of a type specified by USER_P is retrieved.
1298 * Used by emacs_mswindows_event_pending_p and emacs_mswindows_next_event
1301 mswindows_need_event (int badly_p)
1305 if (mswindows_in_modal_loop)
1307 mswindows_need_event_in_modal_loop (badly_p);
1311 /* Have to drain Windows message queue first, otherwise, we may miss
1312 quit char when called from quit_p */
1313 mswindows_drain_windows_queue ();
1315 while (NILP (mswindows_u_dispatch_event_queue)
1316 && NILP (mswindows_s_dispatch_event_queue))
1318 #ifdef HAVE_MSG_SELECT
1320 SELECT_TYPE temp_mask = input_wait_mask;
1321 EMACS_TIME sometime;
1322 EMACS_SELECT_TIME select_time_to_block, *pointer_to_this;
1325 pointer_to_this = 0;
1328 EMACS_SET_SECS_USECS (sometime, 0, 0);
1329 EMACS_TIME_TO_SELECT_TIME (sometime, select_time_to_block);
1330 pointer_to_this = &select_time_to_block;
1333 active = select (MAXDESC, &temp_mask, 0, 0, pointer_to_this);
1338 return; /* timeout */
1340 else if (active > 0)
1342 if (FD_ISSET (windows_fd, &temp_mask))
1344 mswindows_drain_windows_queue ();
1347 /* Look for a TTY event */
1348 for (i = 0; i < MAXDESC-1; i++)
1350 /* To avoid race conditions (among other things, an infinite
1351 loop when called from Fdiscard_input()), we must return
1352 user events ahead of process events. */
1353 if (FD_ISSET (i, &temp_mask) && FD_ISSET (i, &tty_only_mask))
1355 struct console *c = tty_find_console_from_fd (i);
1356 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1357 struct Lisp_Event* event = XEVENT (emacs_event);
1360 if (read_event_from_tty_or_stream_desc (event, c, i))
1362 mswindows_enqueue_dispatch_event (emacs_event);
1368 /* Look for a process event */
1369 for (i = 0; i < MAXDESC-1; i++)
1371 if (FD_ISSET (i, &temp_mask))
1373 if (FD_ISSET (i, &process_only_mask))
1375 struct Lisp_Process *p =
1376 get_process_from_usid (FD_TO_USID(i));
1378 mswindows_enqueue_process_event (p);
1382 /* We might get here when a fake event came
1383 through a signal. Return a dummy event, so
1384 that a cycle of the command loop will
1386 drain_signal_event_pipe ();
1387 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
1392 else if (active==-1)
1396 /* something bad happened */
1405 /* Now try getting a message or process event */
1406 active = MsgWaitForMultipleObjects (mswindows_waitable_count,
1407 mswindows_waitable_handles,
1408 FALSE, badly_p ? INFINITE : 0,
1411 /* This will assert if handle being waited for becomes abandoned.
1412 Not the case currently tho */
1413 assert ((!badly_p && active == WAIT_TIMEOUT) ||
1414 (active >= WAIT_OBJECT_0 &&
1415 active <= WAIT_OBJECT_0 + mswindows_waitable_count));
1417 if (active == WAIT_TIMEOUT)
1419 /* No luck trying - just return what we've already got */
1422 else if (active == WAIT_OBJECT_0 + mswindows_waitable_count)
1424 /* Got your message, thanks */
1425 mswindows_drain_windows_queue ();
1429 int ix = active - WAIT_OBJECT_0;
1430 /* First, try to find which process' output has signaled */
1431 struct Lisp_Process *p =
1432 get_process_from_usid (HANDLE_TO_USID (mswindows_waitable_handles[ix]));
1435 /* Found a signaled process input handle */
1436 mswindows_enqueue_process_event (p);
1440 /* None. This means that the process handle itself has signaled.
1441 Remove the handle from the wait vector, and make status_notify
1442 note the exited process */
1443 mswindows_waitable_handles [ix] =
1444 mswindows_waitable_handles [--mswindows_waitable_count];
1445 kick_status_notify ();
1446 /* Have to return something: there may be no accompanying
1448 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
1455 /************************************************************************/
1456 /* Event generators */
1457 /************************************************************************/
1460 * Callback procedure for synchronous timer messages
1462 static void CALLBACK
1463 mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime)
1465 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1466 struct Lisp_Event *event = XEVENT (emacs_event);
1468 if (KillTimer (NULL, id_timer))
1469 --mswindows_pending_timers_count;
1471 event->channel = Qnil;
1472 event->timestamp = dwtime;
1473 event->event_type = timeout_event;
1474 event->event.timeout.interval_id = id_timer;
1475 event->event.timeout.function = Qnil;
1476 event->event.timeout.object = Qnil;
1478 mswindows_enqueue_dispatch_event (emacs_event);
1482 * Callback procedure for dde messages
1484 * We execute a dde Open("file") by simulating a file drop, so dde support
1485 * depends on dnd support.
1487 #ifdef HAVE_DRAGNDROP
1489 mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
1490 HSZ hszTopic, HSZ hszItem, HDDEDATA hdata,
1491 DWORD dwData1, DWORD dwData2)
1496 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
1497 return (HDDEDATA)TRUE;
1498 return (HDDEDATA)FALSE;
1500 case XTYP_WILDCONNECT:
1502 /* We only support one {service,topic} pair */
1503 HSZPAIR pairs[2] = {
1504 { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } };
1506 if (!(hszItem || DdeCmpStringHandles (hszItem, mswindows_dde_service)) &&
1507 !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)));
1508 return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs,
1509 sizeof (pairs), 0L, 0, uFmt, 0));
1511 return (HDDEDATA)NULL;
1514 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
1516 DWORD len = DdeGetData (hdata, NULL, 0, 0);
1517 char *cmd = alloca (len+1);
1520 struct gcpro gcpro1, gcpro2;
1521 Lisp_Object l_dndlist = Qnil;
1522 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
1523 Lisp_Object frmcons, devcons, concons;
1524 struct Lisp_Event *event = XEVENT (emacs_event);
1526 DdeGetData (hdata, cmd, len, 0);
1528 DdeFreeDataHandle (hdata);
1530 /* Check syntax & that it's an [Open("foo")] command, which we
1531 * treat like a file drop */
1532 /* #### Ought to be generalised and accept some other commands */
1535 if (strnicmp (cmd, MSWINDOWS_DDE_ITEM_OPEN,
1536 strlen (MSWINDOWS_DDE_ITEM_OPEN)))
1537 return DDE_FNOTPROCESSED;
1538 cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN);
1541 if (*cmd!='(' || *(cmd+1)!='\"')
1542 return DDE_FNOTPROCESSED;
1544 while (*end && *end!='\"')
1547 return DDE_FNOTPROCESSED;
1550 return DDE_FNOTPROCESSED;
1554 return DDE_FNOTPROCESSED;
1557 filename = alloca (cygwin32_win32_to_posix_path_list_buf_size (cmd) + 5);
1558 strcpy (filename, "file:");
1559 cygwin32_win32_to_posix_path_list (cmd, filename+5);
1561 dostounix_filename (cmd);
1562 filename = alloca (strlen (cmd)+6);
1563 strcpy (filename, "file:");
1564 strcat (filename, cmd);
1566 GCPRO2 (emacs_event, l_dndlist);
1567 l_dndlist = make_string (filename, strlen (filename));
1569 /* Find a mswindows frame */
1570 event->channel = Qnil;
1571 FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
1573 Lisp_Object frame = XCAR (frmcons);
1574 if (FRAME_TYPE_P (XFRAME (frame), mswindows))
1575 event->channel = frame;
1577 assert (!NILP (event->channel));
1579 event->timestamp = GetTickCount();
1580 event->event_type = misc_user_event;
1581 event->event.misc.button = 1;
1582 event->event.misc.modifiers = 0;
1583 event->event.misc.x = -1;
1584 event->event.misc.y = -1;
1585 event->event.misc.function = Qdragdrop_drop_dispatch;
1586 event->event.misc.object = Fcons (Qdragdrop_URL,
1587 Fcons (l_dndlist, Qnil));
1588 mswindows_enqueue_dispatch_event (emacs_event);
1590 return (HDDEDATA) DDE_FACK;
1592 DdeFreeDataHandle (hdata);
1593 return (HDDEDATA) DDE_FNOTPROCESSED;
1596 return (HDDEDATA) NULL;
1602 * The windows procedure for the window class XEMACS_CLASS
1605 mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1607 /* Note: Remember to initialize emacs_event and event before use.
1608 This code calls code that can GC. You must GCPRO before calling such code. */
1609 Lisp_Object emacs_event = Qnil;
1610 Lisp_Object fobj = Qnil;
1612 struct Lisp_Event *event;
1613 struct frame *frame;
1614 struct mswindows_frame* msframe;
1618 case WM_DESTROYCLIPBOARD:
1619 /* We own the clipboard and someone else wants it. Delete our
1620 cached copy of the clipboard contents so we'll ask for it from
1621 Windows again when someone does a paste. */
1622 handle_selection_clear(QCLIPBOARD);
1626 /* Erase background only during non-dynamic sizing */
1627 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1628 if (msframe->sizing && !mswindows_dynamic_frame_resize)
1633 fobj = mswindows_find_frame (hwnd);
1634 mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt));
1639 /* See Win95 comment under WM_KEYDOWN */
1643 if (wParam == VK_CONTROL)
1645 GetKeyboardState (keymap);
1646 keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80;
1647 SetKeyboardState (keymap);
1649 else if (wParam == VK_MENU)
1651 GetKeyboardState (keymap);
1652 keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80;
1653 SetKeyboardState (keymap);
1660 /* In some locales the right-hand Alt key is labelled AltGr. This key
1661 * should produce alternative charcaters when combined with another key.
1662 * eg on a German keyboard pressing AltGr+q should produce '@'.
1663 * AltGr generates exactly the same keystrokes as LCtrl+RAlt. But if
1664 * TranslateMessage() is called with *any* combination of Ctrl+Alt down,
1665 * it translates as if AltGr were down.
1666 * We get round this by removing all modifiers from the keymap before
1667 * calling TranslateMessage() unless AltGr is *really* down. */
1670 int has_AltGr = mswindows_current_layout_has_AltGr ();
1672 int extendedp = lParam & 0x1000000;
1675 GetKeyboardState (keymap);
1676 mods = mswindows_modifier_state (keymap, has_AltGr);
1678 /* Handle non-printables */
1679 if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods,
1681 mswindows_enqueue_keypress_event (hwnd, keysym, mods);
1682 else /* Normal keys & modifiers */
1684 int quit_ch = CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd)));
1685 BYTE keymap_orig[256];
1686 POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) };
1690 msg.message = message;
1691 msg.wParam = wParam;
1692 msg.lParam = lParam;
1693 msg.time = GetMessageTime();
1696 /* GetKeyboardState() does not work as documented on Win95. We have
1697 * to loosely track Left and Right modifiers on behalf of the OS,
1698 * without screwing up Windows NT which tracks them properly. */
1699 if (wParam == VK_CONTROL)
1700 keymap [extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80;
1701 else if (wParam == VK_MENU)
1702 keymap [extendedp ? VK_RMENU : VK_LMENU] |= 0x80;
1704 memcpy (keymap_orig, keymap, 256);
1706 /* Remove shift modifier from an ascii character */
1709 /* Clear control and alt modifiers unless AltGr is pressed */
1710 keymap [VK_RCONTROL] = 0;
1711 keymap [VK_LMENU] = 0;
1712 if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80) || !(keymap [VK_RMENU] & 0x80))
1714 keymap [VK_LCONTROL] = 0;
1715 keymap [VK_CONTROL] = 0;
1716 keymap [VK_RMENU] = 0;
1717 keymap [VK_MENU] = 0;
1719 SetKeyboardState (keymap);
1721 /* Maybe generate some WM_[SYS]CHARs in the queue */
1722 TranslateMessage (&msg);
1724 while (PeekMessage (&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)
1725 || PeekMessage (&msg, hwnd, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE))
1728 WPARAM ch = msg.wParam;
1730 /* If a quit char with no modifiers other than control and
1731 shift, then mark it with a fake modifier, which is removed
1732 upon dequeueing the event */
1733 /* #### This might also not withstand localization, if
1734 quit character is not a latin-1 symbol */
1735 if (((quit_ch < ' ' && (mods & MOD_CONTROL) && quit_ch + 'a' - 1 == ch)
1736 || (quit_ch >= ' ' && !(mods & MOD_CONTROL) && quit_ch == ch))
1737 && ((mods & ~(MOD_CONTROL | MOD_SHIFT)) == 0))
1739 mods1 |= FAKE_MOD_QUIT;
1740 ++mswindows_quit_chars_count;
1743 mswindows_enqueue_keypress_event (hwnd, make_char(ch), mods1);
1745 SetKeyboardState (keymap_orig);
1748 /* F10 causes menu activation by default. We do not want this */
1749 if (wParam != VK_F10 && (mswindows_meta_activates_menu || wParam != VK_MENU))
1753 case WM_MBUTTONDOWN:
1755 /* Real middle mouse button has nothing to do with emulated one:
1756 if one wants to exercise fingers playing chords on the mouse,
1757 he is allowed to do that! */
1758 mswindows_enqueue_mouse_button_event (hwnd, message,
1759 MAKEPOINTS (lParam), GetMessageTime());
1763 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1764 msframe->last_click_time = GetMessageTime();
1766 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1767 msframe->button2_need_lbutton = 0;
1768 if (msframe->ignore_next_lbutton_up)
1770 msframe->ignore_next_lbutton_up = 0;
1772 else if (msframe->button2_is_down)
1774 msframe->button2_is_down = 0;
1775 msframe->ignore_next_rbutton_up = 1;
1776 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
1777 MAKEPOINTS (lParam), GetMessageTime());
1781 if (msframe->button2_need_rbutton)
1783 msframe->button2_need_rbutton = 0;
1784 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
1785 MAKEPOINTS (lParam), GetMessageTime());
1787 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
1788 MAKEPOINTS (lParam), GetMessageTime());
1793 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1794 msframe->last_click_time = GetMessageTime();
1796 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1797 msframe->button2_need_rbutton = 0;
1798 if (msframe->ignore_next_rbutton_up)
1800 msframe->ignore_next_rbutton_up = 0;
1802 else if (msframe->button2_is_down)
1804 msframe->button2_is_down = 0;
1805 msframe->ignore_next_lbutton_up = 1;
1806 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
1807 MAKEPOINTS (lParam), GetMessageTime());
1811 if (msframe->button2_need_lbutton)
1813 msframe->button2_need_lbutton = 0;
1814 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
1815 MAKEPOINTS (lParam), GetMessageTime());
1817 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
1818 MAKEPOINTS (lParam), GetMessageTime());
1822 case WM_LBUTTONDOWN:
1823 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1825 if (msframe->button2_need_lbutton)
1827 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1828 msframe->button2_need_lbutton = 0;
1829 msframe->button2_need_rbutton = 0;
1830 if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
1832 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
1833 MAKEPOINTS (lParam), GetMessageTime());
1834 msframe->button2_is_down = 1;
1838 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
1839 msframe->last_click_point, msframe->last_click_time);
1840 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
1841 MAKEPOINTS (lParam), GetMessageTime());
1846 mswindows_set_chord_timer (hwnd);
1847 msframe->button2_need_rbutton = 1;
1848 msframe->last_click_point = MAKEPOINTS (lParam);
1850 msframe->last_click_time = GetMessageTime();
1853 case WM_RBUTTONDOWN:
1854 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1856 if (msframe->button2_need_rbutton)
1858 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1859 msframe->button2_need_lbutton = 0;
1860 msframe->button2_need_rbutton = 0;
1861 if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
1863 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
1864 MAKEPOINTS (lParam), GetMessageTime());
1865 msframe->button2_is_down = 1;
1869 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
1870 msframe->last_click_point, msframe->last_click_time);
1871 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
1872 MAKEPOINTS (lParam), GetMessageTime());
1877 mswindows_set_chord_timer (hwnd);
1878 msframe->button2_need_lbutton = 1;
1879 msframe->last_click_point = MAKEPOINTS (lParam);
1881 msframe->last_click_time = GetMessageTime();
1885 if (wParam == BUTTON_2_TIMER_ID)
1887 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1888 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1890 if (msframe->button2_need_lbutton)
1892 msframe->button2_need_lbutton = 0;
1893 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
1894 msframe->last_click_point, msframe->last_click_time);
1896 else if (msframe->button2_need_rbutton)
1898 msframe->button2_need_rbutton = 0;
1899 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
1900 msframe->last_click_point, msframe->last_click_time);
1904 assert ("Spurious timer fired" == 0);
1908 /* Optimization: don't report mouse movement while size is changing */
1909 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1910 if (!msframe->sizing)
1912 /* When waiting for the second mouse button to finish
1913 button2 emulation, and have moved too far, just pretend
1914 as if timer has expired. This improves drag-select feedback */
1915 if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton)
1916 && !mswindows_button2_near_enough (msframe->last_click_point,
1917 MAKEPOINTS (lParam)))
1919 KillTimer (hwnd, BUTTON_2_TIMER_ID);
1920 SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0);
1923 emacs_event = Fmake_event (Qnil, Qnil);
1924 event = XEVENT(emacs_event);
1926 event->channel = mswindows_find_frame(hwnd);
1927 event->timestamp = GetMessageTime();
1928 event->event_type = pointer_motion_event;
1929 event->event.motion.x = MAKEPOINTS(lParam).x;
1930 event->event.motion.y = MAKEPOINTS(lParam).y;
1931 event->event.motion.modifiers = mswindows_modifier_state (NULL, 0);
1933 mswindows_enqueue_dispatch_event (emacs_event);
1939 /* Queue a `cancel-mode-internal' misc user event, so mouse
1940 selection would be canceled if any */
1941 mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd),
1942 Qcancel_mode_internal, Qnil);
1947 LPNMHDR nmhdr = (LPNMHDR)lParam;
1949 if (nmhdr->code == TTN_NEEDTEXT)
1951 #ifdef HAVE_TOOLBARS
1952 LPTOOLTIPTEXT tttext = (LPTOOLTIPTEXT)lParam;
1955 /* find out which toolbar */
1956 frame = XFRAME (mswindows_find_frame (hwnd));
1957 btext = mswindows_get_toolbar_button_text ( frame,
1960 tttext->lpszText = NULL;
1961 tttext->hinst = NULL;
1965 /* I think this is safe since the text will only go away
1966 when the toolbar does...*/
1967 GET_C_STRING_EXT_DATA_ALLOCA (btext, FORMAT_OS,
1972 /* handle tree view callbacks */
1973 else if (nmhdr->code == TVN_SELCHANGED)
1975 NM_TREEVIEW* ptree = (NM_TREEVIEW*)lParam;
1976 frame = XFRAME (mswindows_find_frame (hwnd));
1977 mswindows_handle_gui_wm_command (frame, 0, ptree->itemNew.lParam);
1979 /* handle tab control callbacks */
1980 else if (nmhdr->code == TCN_SELCHANGE)
1983 int index = SendMessage (nmhdr->hwndFrom, TCM_GETCURSEL, 0, 0);
1984 frame = XFRAME (mswindows_find_frame (hwnd));
1986 item.mask = TCIF_PARAM;
1987 SendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM)index,
1990 mswindows_handle_gui_wm_command (frame, 0, item.lParam);
1997 /* According to the docs we need to check GetUpdateRect() before
1998 actually doing a WM_PAINT */
1999 if (GetUpdateRect (hwnd, NULL, FALSE))
2001 PAINTSTRUCT paintStruct;
2002 int x, y, width, height;
2004 frame = XFRAME (mswindows_find_frame (hwnd));
2006 BeginPaint (hwnd, &paintStruct);
2007 x = paintStruct.rcPaint.left;
2008 y = paintStruct.rcPaint.top;
2009 width = paintStruct.rcPaint.right - paintStruct.rcPaint.left;
2010 height = paintStruct.rcPaint.bottom - paintStruct.rcPaint.top;
2011 /* Normally we want to ignore expose events when child
2012 windows are unmapped, however once we are in the guts of
2013 WM_PAINT we need to make sure that we don't register
2014 unmaps then because they will not actually occur. */
2015 if (!check_for_ignored_expose (frame, x, y, width, height))
2017 hold_ignored_expose_registration = 1;
2018 mswindows_redraw_exposed_area (frame, x, y, width, height);
2019 hold_ignored_expose_registration = 0;
2022 EndPaint (hwnd, &paintStruct);
2030 /* We only care about this message if our size has really changed */
2031 if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
2036 fobj = mswindows_find_frame (hwnd);
2037 frame = XFRAME (fobj);
2038 msframe = FRAME_MSWINDOWS_DATA (frame);
2040 /* We cannot handle frame map and unmap hooks right in
2041 this routine, because these may throw. We queue
2042 magic events to run these hooks instead - kkm */
2044 if (wParam==SIZE_MINIMIZED)
2047 FRAME_VISIBLE_P (frame) = 0;
2048 mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME);
2052 GetClientRect(hwnd, &rect);
2053 FRAME_PIXWIDTH(frame) = rect.right;
2054 FRAME_PIXHEIGHT(frame) = rect.bottom;
2056 pixel_to_real_char_size (frame, rect.right, rect.bottom,
2057 &FRAME_MSWINDOWS_CHARWIDTH (frame),
2058 &FRAME_MSWINDOWS_CHARHEIGHT (frame));
2060 pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows);
2061 change_frame_size (frame, rows, columns, 1);
2063 /* If we are inside frame creation, we have to apply geometric
2065 if (FRAME_MSWINDOWS_TARGET_RECT (frame))
2067 /* Yes, we have to size again */
2068 mswindows_size_frame_internal ( frame,
2069 FRAME_MSWINDOWS_TARGET_RECT
2071 /* Reset so we do not get here again. The SetWindowPos call in
2072 * mswindows_size_frame_internal can cause recursion here. */
2073 if (FRAME_MSWINDOWS_TARGET_RECT (frame))
2075 xfree (FRAME_MSWINDOWS_TARGET_RECT (frame));
2076 FRAME_MSWINDOWS_TARGET_RECT (frame) = 0;
2081 if (!msframe->sizing && !FRAME_VISIBLE_P (frame))
2082 mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME);
2083 FRAME_VISIBLE_P (frame) = 1;
2085 if (!msframe->sizing || mswindows_dynamic_frame_resize)
2092 /* Misc magic events which only require that the frame be identified */
2095 mswindows_enqueue_magic_event (hwnd, message);
2098 case WM_WINDOWPOSCHANGING:
2100 WINDOWPOS *wp = (LPWINDOWPOS) lParam;
2101 WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) };
2102 GetWindowPlacement(hwnd, &wpl);
2104 /* Only interested if size is changing and we're not being iconified */
2105 if (wpl.showCmd != SW_SHOWMINIMIZED
2106 && wpl.showCmd != SW_SHOWMAXIMIZED
2107 && !(wp->flags & SWP_NOSIZE))
2109 RECT ncsize = { 0, 0, 0, 0 };
2110 int pixwidth, pixheight;
2111 AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE),
2112 GetMenu(hwnd) != NULL,
2113 GetWindowLong (hwnd, GWL_EXSTYLE));
2115 round_size_to_real_char (XFRAME (mswindows_find_frame (hwnd)),
2116 wp->cx - (ncsize.right - ncsize.left),
2117 wp->cy - (ncsize.bottom - ncsize.top),
2118 &pixwidth, &pixheight);
2120 /* Convert client sizes to window sizes */
2121 pixwidth += (ncsize.right - ncsize.left);
2122 pixheight += (ncsize.bottom - ncsize.top);
2124 if (wpl.showCmd != SW_SHOWMAXIMIZED)
2126 /* Adjust so that the bottom or right doesn't move if it's
2127 * the top or left that's being changed */
2129 GetWindowRect (hwnd, &rect);
2131 if (rect.left != wp->x)
2132 wp->x += wp->cx - pixwidth;
2133 if (rect.top != wp->y)
2134 wp->y += wp->cy - pixheight;
2140 /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts
2141 window position if the user tries to track window too small */
2145 case WM_ENTERSIZEMOVE:
2146 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2147 msframe->sizing = 1;
2150 case WM_EXITSIZEMOVE:
2151 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
2152 msframe->sizing = 0;
2153 /* Queue noop event */
2154 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
2157 #ifdef HAVE_SCROLLBARS
2161 /* Direction of scroll is determined by scrollbar instance. */
2162 int code = (int) LOWORD(wParam);
2163 int pos = (short int) HIWORD(wParam);
2164 HWND hwndScrollBar = (HWND) lParam;
2165 struct gcpro gcpro1, gcpro2;
2167 mswindows_handle_scrollbar_event (hwndScrollBar, code, pos);
2168 GCPRO2 (emacs_event, fobj);
2169 if (UNBOUNDP(mswindows_pump_outstanding_events())) /* Can GC */
2171 /* Error during event pumping - cancel scroll */
2172 SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
2180 int keys = LOWORD (wParam); /* Modifier key flags */
2181 int delta = (short) HIWORD (wParam); /* Wheel rotation amount */
2182 struct gcpro gcpro1, gcpro2;
2184 if (mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd), keys, delta))
2186 GCPRO2 (emacs_event, fobj);
2187 mswindows_pump_outstanding_events (); /* Can GC */
2196 #ifdef HAVE_MENUBARS
2198 if (UNBOUNDP (mswindows_handle_wm_initmenu (
2200 XFRAME (mswindows_find_frame (hwnd)))))
2201 SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2204 case WM_INITMENUPOPUP:
2205 if (!HIWORD(lParam))
2207 if (UNBOUNDP (mswindows_handle_wm_initmenupopup (
2209 XFRAME (mswindows_find_frame (hwnd)))))
2210 SendMessage (hwnd, WM_CANCELMODE, 0, 0);
2214 #endif /* HAVE_MENUBARS */
2218 WORD id = LOWORD (wParam);
2219 WORD nid = HIWORD (wParam);
2220 HWND cid = (HWND)lParam;
2221 frame = XFRAME (mswindows_find_frame (hwnd));
2223 #ifdef HAVE_TOOLBARS
2224 if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id)))
2227 /* widgets in a buffer only eval a callback for suitable events.*/
2232 case CBN_EDITCHANGE:
2234 if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id)))
2237 /* menubars always must come last since the hashtables do not
2239 #ifdef HAVE_MENUBARS
2240 if (!NILP (mswindows_handle_wm_command (frame, id)))
2244 return DefWindowProc (hwnd, message, wParam, lParam);
2245 /* Bite me - a spurious command. This used to not be able to
2246 happen but with the introduction of widgets its now
2251 case WM_CTLCOLORBTN:
2252 case WM_CTLCOLORLISTBOX:
2253 case WM_CTLCOLOREDIT:
2254 case WM_CTLCOLORSTATIC:
2255 case WM_CTLCOLORSCROLLBAR:
2257 /* if we get an opportunity to paint a widget then do so if
2258 there is an appropriate face */
2259 HWND crtlwnd = (HWND)lParam;
2260 LONG ii = GetWindowLong (crtlwnd, GWL_USERDATA);
2263 Lisp_Object image_instance;
2264 VOID_TO_LISP (image_instance, ii);
2265 if (IMAGE_INSTANCEP (image_instance)
2267 IMAGE_INSTANCE_TYPE_P (image_instance, IMAGE_WIDGET)
2269 !NILP (XIMAGE_INSTANCE_WIDGET_FACE (image_instance)))
2271 /* set colors for the buttons */
2272 HDC hdc = (HDC)wParam;
2273 if (last_widget_brushed != ii)
2276 DeleteObject (widget_brush);
2277 widget_brush = CreateSolidBrush
2278 (COLOR_INSTANCE_MSWINDOWS_COLOR
2281 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2282 XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
2284 last_widget_brushed = ii;
2287 COLOR_INSTANCE_MSWINDOWS_COLOR
2290 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2291 XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
2292 SetBkMode (hdc, OPAQUE);
2295 COLOR_INSTANCE_MSWINDOWS_COLOR
2298 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance),
2299 XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance)))));
2300 return (LRESULT)widget_brush;
2306 #ifdef HAVE_DRAGNDROP
2307 case WM_DROPFILES: /* implementation ripped-off from event-Xt.c */
2309 UINT filecount, i, len;
2315 Lisp_Object l_dndlist = Qnil, l_item = Qnil;
2316 struct gcpro gcpro1, gcpro2, gcpro3;
2318 emacs_event = Fmake_event (Qnil, Qnil);
2319 event = XEVENT(emacs_event);
2321 GCPRO3 (emacs_event, l_dndlist, l_item);
2323 if (!DragQueryPoint ((HANDLE) wParam, &point))
2324 point.x = point.y = -1; /* outside client area */
2326 event->event_type = misc_user_event;
2327 event->channel = mswindows_find_frame(hwnd);
2328 event->timestamp = GetMessageTime();
2329 event->event.misc.button = 1; /* #### Should try harder */
2330 event->event.misc.modifiers = mswindows_modifier_state (NULL, 0);
2331 event->event.misc.x = point.x;
2332 event->event.misc.y = point.y;
2333 event->event.misc.function = Qdragdrop_drop_dispatch;
2335 filecount = DragQueryFile ((HANDLE) wParam, 0xffffffff, NULL, 0);
2336 for (i=0; i<filecount; i++)
2338 len = DragQueryFile ((HANDLE) wParam, i, NULL, 0);
2339 /* The URLs that we make here aren't correct according to section
2340 * 3.10 of rfc1738 because they're missing the //<host>/ part and
2341 * because they may contain reserved characters. But that's OK. */
2343 fname = (char *)xmalloc (len+1);
2344 DragQueryFile ((HANDLE) wParam, i, fname, len+1);
2345 filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5);
2346 strcpy (filename, "file:");
2347 cygwin32_win32_to_posix_path_list (fname, filename+5);
2350 filename = (char *)xmalloc (len+6);
2351 strcpy (filename, "file:");
2352 DragQueryFile ((HANDLE) wParam, i, filename+5, len+1);
2353 dostounix_filename (filename+5);
2355 l_item = make_string (filename, strlen (filename));
2356 l_dndlist = Fcons (l_item, l_dndlist);
2359 DragFinish ((HANDLE) wParam);
2361 event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist);
2362 mswindows_enqueue_dispatch_event (emacs_event);
2370 return DefWindowProc (hwnd, message, wParam, lParam);
2376 /************************************************************************/
2377 /* keyboard, mouse & other helpers for the windows procedure */
2378 /************************************************************************/
2380 mswindows_set_chord_timer (HWND hwnd)
2384 /* We get one third half system double click threshold */
2385 if (mswindows_mouse_button_tolerance <= 0)
2386 interval = GetDoubleClickTime () / 3;
2388 interval = mswindows_mouse_button_tolerance;
2390 SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0);
2394 mswindows_button2_near_enough (POINTS p1, POINTS p2)
2397 if (mswindows_mouse_button_max_skew_x <= 0)
2398 dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2;
2400 dx = mswindows_mouse_button_max_skew_x;
2402 if (mswindows_mouse_button_max_skew_y <= 0)
2403 dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2;
2405 dy = mswindows_mouse_button_max_skew_y;
2407 return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy;
2411 mswindows_current_layout_has_AltGr (void)
2413 /* This simple caching mechanism saves 10% of CPU
2414 time when a key typed at autorepeat rate of 30 cps! */
2415 static HKL last_hkl = 0;
2416 static int last_hkl_has_AltGr;
2418 HKL current_hkl = GetKeyboardLayout (0);
2419 if (current_hkl != last_hkl)
2422 last_hkl_has_AltGr = 0;
2423 /* In this loop, we query whether a character requires
2424 AltGr to be down to generate it. If at least such one
2425 found, this means that the layout does regard AltGr */
2426 for (c = ' '; c <= 0xFFU && c != 0 && !last_hkl_has_AltGr; ++c)
2427 if (HIBYTE (VkKeyScan (c)) == 6)
2428 last_hkl_has_AltGr = 1;
2429 last_hkl = current_hkl;
2431 return last_hkl_has_AltGr;
2435 /* Returns the state of the modifier keys in the format expected by the
2436 * Lisp_Event key_data, button_data and motion_data modifiers member */
2437 int mswindows_modifier_state (BYTE* keymap, int has_AltGr)
2443 keymap = (BYTE*) alloca(256);
2444 GetKeyboardState (keymap);
2445 has_AltGr = mswindows_current_layout_has_AltGr ();
2448 if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80))
2450 mods |= (keymap [VK_LMENU] & 0x80) ? MOD_META : 0;
2451 mods |= (keymap [VK_RCONTROL] & 0x80) ? MOD_CONTROL : 0;
2455 mods |= (keymap [VK_MENU] & 0x80) ? MOD_META : 0;
2456 mods |= (keymap [VK_CONTROL] & 0x80) ? MOD_CONTROL : 0;
2459 mods |= (keymap [VK_SHIFT] & 0x80) ? MOD_SHIFT : 0;
2465 * Translate a mswindows virtual key to a keysym.
2466 * Only returns non-Qnil for keys that don't generate WM_CHAR messages
2467 * or whose ASCII codes (like space) xemacs doesn't like.
2468 * Virtual key values are defined in winresrc.h
2470 Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods,
2473 if (extendedp) /* Keys not present on a 82 key keyboard */
2475 switch (mswindows_key)
2477 case VK_RETURN: return KEYSYM ("kp-enter");
2478 case VK_PRIOR: return KEYSYM ("prior");
2479 case VK_NEXT: return KEYSYM ("next");
2480 case VK_END: return KEYSYM ("end");
2481 case VK_HOME: return KEYSYM ("home");
2482 case VK_LEFT: return KEYSYM ("left");
2483 case VK_UP: return KEYSYM ("up");
2484 case VK_RIGHT: return KEYSYM ("right");
2485 case VK_DOWN: return KEYSYM ("down");
2486 case VK_INSERT: return KEYSYM ("insert");
2487 case VK_DELETE: return QKdelete;
2492 switch (mswindows_key)
2494 case VK_BACK: return QKbackspace;
2495 case VK_TAB: return QKtab;
2496 case '\n': return QKlinefeed;
2497 case VK_CLEAR: return KEYSYM ("clear");
2498 case VK_RETURN: return QKreturn;
2499 case VK_ESCAPE: return QKescape;
2500 case VK_SPACE: return QKspace;
2501 case VK_PRIOR: return KEYSYM ("kp-prior");
2502 case VK_NEXT: return KEYSYM ("kp-next");
2503 case VK_END: return KEYSYM ("kp-end");
2504 case VK_HOME: return KEYSYM ("kp-home");
2505 case VK_LEFT: return KEYSYM ("kp-left");
2506 case VK_UP: return KEYSYM ("kp-up");
2507 case VK_RIGHT: return KEYSYM ("kp-right");
2508 case VK_DOWN: return KEYSYM ("kp-down");
2509 case VK_SELECT: return KEYSYM ("select");
2510 case VK_PRINT: return KEYSYM ("print");
2511 case VK_EXECUTE: return KEYSYM ("execute");
2512 case VK_SNAPSHOT: return KEYSYM ("print");
2513 case VK_INSERT: return KEYSYM ("kp-insert");
2514 case VK_DELETE: return KEYSYM ("kp-delete");
2515 case VK_HELP: return KEYSYM ("help");
2516 #if 0 /* FSF Emacs allows these to return configurable syms/mods */
2517 case VK_LWIN return KEYSYM ("");
2518 case VK_RWIN return KEYSYM ("");
2520 case VK_APPS: return KEYSYM ("menu");
2521 case VK_NUMPAD0: return KEYSYM ("kp-0");
2522 case VK_NUMPAD1: return KEYSYM ("kp-1");
2523 case VK_NUMPAD2: return KEYSYM ("kp-2");
2524 case VK_NUMPAD3: return KEYSYM ("kp-3");
2525 case VK_NUMPAD4: return KEYSYM ("kp-4");
2526 case VK_NUMPAD5: return KEYSYM ("kp-5");
2527 case VK_NUMPAD6: return KEYSYM ("kp-6");
2528 case VK_NUMPAD7: return KEYSYM ("kp-7");
2529 case VK_NUMPAD8: return KEYSYM ("kp-8");
2530 case VK_NUMPAD9: return KEYSYM ("kp-9");
2531 case VK_MULTIPLY: return KEYSYM ("kp-multiply");
2532 case VK_ADD: return KEYSYM ("kp-add");
2533 case VK_SEPARATOR: return KEYSYM ("kp-separator");
2534 case VK_SUBTRACT: return KEYSYM ("kp-subtract");
2535 case VK_DECIMAL: return KEYSYM ("kp-decimal");
2536 case VK_DIVIDE: return KEYSYM ("kp-divide");
2537 case VK_F1: return KEYSYM ("f1");
2538 case VK_F2: return KEYSYM ("f2");
2539 case VK_F3: return KEYSYM ("f3");
2540 case VK_F4: return KEYSYM ("f4");
2541 case VK_F5: return KEYSYM ("f5");
2542 case VK_F6: return KEYSYM ("f6");
2543 case VK_F7: return KEYSYM ("f7");
2544 case VK_F8: return KEYSYM ("f8");
2545 case VK_F9: return KEYSYM ("f9");
2546 case VK_F10: return KEYSYM ("f10");
2547 case VK_F11: return KEYSYM ("f11");
2548 case VK_F12: return KEYSYM ("f12");
2549 case VK_F13: return KEYSYM ("f13");
2550 case VK_F14: return KEYSYM ("f14");
2551 case VK_F15: return KEYSYM ("f15");
2552 case VK_F16: return KEYSYM ("f16");
2553 case VK_F17: return KEYSYM ("f17");
2554 case VK_F18: return KEYSYM ("f18");
2555 case VK_F19: return KEYSYM ("f19");
2556 case VK_F20: return KEYSYM ("f20");
2557 case VK_F21: return KEYSYM ("f21");
2558 case VK_F22: return KEYSYM ("f22");
2559 case VK_F23: return KEYSYM ("f23");
2560 case VK_F24: return KEYSYM ("f24");
2567 * Find the console that matches the supplied mswindows window handle
2570 mswindows_find_console (HWND hwnd)
2572 /* We only support one console */
2573 return XCAR (Vconsole_list);
2577 * Find the frame that matches the supplied mswindows window handle
2580 mswindows_find_frame (HWND hwnd)
2582 LONG l = GetWindowLong (hwnd, XWL_FRAMEOBJ);
2586 /* We are in progress of frame creation. Return the frame
2587 being created, as it still not remembered in the window
2589 assert (!NILP (Vmswindows_frame_being_created));
2590 return Vmswindows_frame_being_created;
2592 VOID_TO_LISP (f, l);
2597 /************************************************************************/
2599 /************************************************************************/
2602 emacs_mswindows_add_timeout (EMACS_TIME thyme)
2605 EMACS_TIME current_time;
2606 EMACS_GET_TIME (current_time);
2607 EMACS_SUB_TIME (thyme, thyme, current_time);
2608 milliseconds = EMACS_SECS (thyme) * 1000 +
2609 (EMACS_USECS (thyme) + 500) / 1000;
2610 if (milliseconds < 1)
2612 ++mswindows_pending_timers_count;
2613 return SetTimer (NULL, 0, milliseconds,
2614 (TIMERPROC) mswindows_wm_timer_callback);
2618 emacs_mswindows_remove_timeout (int id)
2620 struct Lisp_Event match_against;
2621 Lisp_Object emacs_event;
2623 if (KillTimer (NULL, id))
2624 --mswindows_pending_timers_count;
2626 /* If there is a dispatch event generated by this
2627 timeout in the queue, we have to remove it too. */
2628 match_against.event_type = timeout_event;
2629 match_against.event.timeout.interval_id = id;
2630 emacs_event = mswindows_cancel_dispatch_event (&match_against);
2631 if (!NILP (emacs_event))
2632 Fdeallocate_event(emacs_event);
2635 /* If `user_p' is false, then return whether there are any win32, timeout,
2636 * or subprocess events pending (that is, whether
2637 * emacs_mswindows_next_event() would return immediately without blocking).
2639 * if `user_p' is true, then return whether there are any *user generated*
2640 * events available (that is, whether there are keyboard or mouse-click
2641 * events ready to be read). This also implies that
2642 * emacs_mswindows_next_event() would not block.
2645 emacs_mswindows_event_pending_p (int user_p)
2647 mswindows_need_event (0);
2648 return (!NILP (mswindows_u_dispatch_event_queue)
2649 || (!user_p && !NILP (mswindows_s_dispatch_event_queue)));
2653 * Return the next event
2656 emacs_mswindows_next_event (struct Lisp_Event *emacs_event)
2658 Lisp_Object event, event2;
2660 mswindows_need_event (1);
2662 event = mswindows_dequeue_dispatch_event ();
2663 XSETEVENT (event2, emacs_event);
2664 Fcopy_event (event, event2);
2665 Fdeallocate_event (event);
2669 * Handle a magic event off the dispatch queue.
2672 emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event)
2674 switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event))
2682 Lisp_Object frame = EVENT_CHANNEL (emacs_event);
2683 struct frame *f = XFRAME (frame);
2684 int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS);
2687 /* struct gcpro gcpro1; */
2689 /* Clear sticky modifiers here (if we had any) */
2691 conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil));
2692 /* GCPRO1 (conser); XXX Not necessary? */
2693 emacs_handle_focus_change_preliminary (conser);
2694 /* Under X the stuff up to here is done in the X event handler.
2696 emacs_handle_focus_change_final (conser);
2705 Lisp_Object frame = EVENT_CHANNEL (emacs_event);
2706 va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)
2708 Qmap_frame_hook : Qunmap_frame_hook,
2713 /* #### What about Enter & Leave */
2715 va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook :
2716 Qmouse_leave_frame_hook, 1, frame);
2724 #ifndef HAVE_MSG_SELECT
2726 get_process_input_waitable (struct Lisp_Process *process)
2728 Lisp_Object instr, outstr, p;
2729 XSETPROCESS (p, process);
2730 get_process_streams (process, &instr, &outstr);
2731 assert (!NILP (instr));
2732 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
2733 return (network_connection_p (p)
2734 ? get_winsock_stream_waitable (XLSTREAM (instr))
2735 : get_ntpipe_input_stream_waitable (XLSTREAM (instr)));
2737 return get_ntpipe_input_stream_waitable (XLSTREAM (instr));
2742 emacs_mswindows_select_process (struct Lisp_Process *process)
2744 HANDLE hev = get_process_input_waitable (process);
2746 if (!add_waitable_handle (hev))
2747 error ("Too many active processes");
2749 #ifdef HAVE_WIN32_PROCESSES
2752 XSETPROCESS (p, process);
2753 if (!network_connection_p (p))
2755 HANDLE hprocess = get_nt_process_handle (process);
2756 if (!add_waitable_handle (hprocess))
2758 remove_waitable_handle (hev);
2759 error ("Too many active processes");
2767 emacs_mswindows_unselect_process (struct Lisp_Process *process)
2769 /* Process handle is removed in the event loop as soon
2770 as it is signaled, so don't bother here about it */
2771 HANDLE hev = get_process_input_waitable (process);
2772 remove_waitable_handle (hev);
2774 #endif /* HAVE_MSG_SELECT */
2777 emacs_mswindows_select_console (struct console *con)
2779 #ifdef HAVE_MSG_SELECT
2780 if (CONSOLE_MSWINDOWS_P (con))
2781 return; /* mswindows consoles are automatically selected */
2783 event_stream_unixoid_select_console (con);
2788 emacs_mswindows_unselect_console (struct console *con)
2790 #ifdef HAVE_MSG_SELECT
2791 if (CONSOLE_MSWINDOWS_P (con))
2792 return; /* mswindows consoles are automatically selected */
2794 event_stream_unixoid_unselect_console (con);
2799 emacs_mswindows_quit_p (void)
2803 /* Quit cannot happen in modal loop: all program
2804 input is dedicated to Windows. */
2805 if (mswindows_in_modal_loop)
2808 /* Drain windows queue. This sets up number of quit characters in the queue
2809 * (and also processes wm focus change, move, resize, etc messages).
2810 * We don't want to process WM_PAINT messages because this function can be
2811 * called from almost anywhere and the windows' states may be changing. */
2812 while (PeekMessage (&msg, NULL, 0, WM_PAINT-1, PM_REMOVE) ||
2813 PeekMessage (&msg, NULL, WM_PAINT+1, WM_USER-1, PM_REMOVE))
2814 DispatchMessage (&msg);
2816 if (mswindows_quit_chars_count > 0)
2818 /* Yes there's a hidden one... Throw it away */
2819 struct Lisp_Event match_against;
2820 Lisp_Object emacs_event;
2822 match_against.event_type = key_press_event;
2823 match_against.event.key.modifiers = FAKE_MOD_QUIT;
2825 emacs_event = mswindows_cancel_dispatch_event (&match_against);
2826 assert (!NILP (emacs_event));
2828 Vquit_flag = (XEVENT(emacs_event)->event.key.modifiers & MOD_SHIFT
2831 Fdeallocate_event(emacs_event);
2832 --mswindows_quit_chars_count;
2837 emacs_mswindows_create_stream_pair (void* inhandle, void* outhandle,
2838 Lisp_Object* instream,
2839 Lisp_Object* outstream,
2842 /* Handles for streams */
2844 /* fds. These just stored along with the streams, and are closed in
2845 delete stream pair method, because we need to handle fake unices
2849 /* Decode inhandle and outhandle. Their meaning depends on
2850 the process implementation being used. */
2851 #if defined (HAVE_WIN32_PROCESSES)
2852 /* We're passed in Windows handles. That's what we like most... */
2853 hin = (HANDLE) inhandle;
2854 hout = (HANDLE) outhandle;
2856 #elif defined (HAVE_UNIX_PROCESSES)
2857 /* We are passed UNIX fds. This must be Cygwin.
2859 hin = inhandle >= 0 ? (HANDLE)get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE;
2860 hout = outhandle >= 0 ? (HANDLE)get_osfhandle ((int)outhandle) : INVALID_HANDLE_VALUE;
2864 #error "So, WHICH kind of processes do you want?"
2867 *instream = (hin == INVALID_HANDLE_VALUE
2869 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
2870 : flags & STREAM_NETWORK_CONNECTION
2871 ? make_winsock_input_stream ((SOCKET)hin, fdi)
2873 : make_ntpipe_input_stream (hin, fdi));
2875 #ifdef HAVE_WIN32_PROCESSES
2876 *outstream = (hout == INVALID_HANDLE_VALUE
2878 #if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
2879 : flags & STREAM_NETWORK_CONNECTION
2880 ? make_winsock_output_stream ((SOCKET)hout, fdo)
2882 : make_ntpipe_output_stream (hout, fdo));
2883 #elif defined (HAVE_UNIX_PROCESSES)
2884 *outstream = (fdo >= 0
2885 ? make_filedesc_output_stream (fdo, 0, -1, LSTR_BLOCKED_OK)
2888 #if defined(HAVE_UNIX_PROCESSES) && defined(HAVE_PTYS)
2889 /* FLAGS is process->pty_flag for UNIX_PROCESSES */
2890 if ((flags & STREAM_PTY_FLUSHING) && fdo >= 0)
2892 Bufbyte eof_char = get_eof_char (fdo);
2893 int pty_max_bytes = get_pty_max_bytes (fdo);
2894 filedesc_stream_set_pty_flushing (XLSTREAM(*outstream), pty_max_bytes, eof_char);
2899 return (NILP (*instream)
2901 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
2902 : flags & STREAM_NETWORK_CONNECTION
2903 ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream)))
2905 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (*instream))));
2909 emacs_mswindows_delete_stream_pair (Lisp_Object instream,
2910 Lisp_Object outstream)
2912 /* Oh nothing special here for Win32 at all */
2913 #if defined (HAVE_UNIX_PROCESSES)
2914 int in = (NILP(instream)
2916 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
2917 : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
2918 ? get_winsock_stream_param (XLSTREAM (instream))
2920 : get_ntpipe_input_stream_param (XLSTREAM (instream)));
2921 int out = (NILP(outstream) ? -1
2922 : filedesc_stream_fd (XLSTREAM (outstream)));
2926 if (out != in && out >= 0)
2930 return (NILP (instream)
2932 #if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
2933 : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
2934 ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream)))
2936 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream))));
2939 #ifndef HAVE_X_WINDOWS
2940 /* This is called from GC when a process object is about to be freed.
2941 If we've still got pointers to it in this file, we're gonna lose hard.
2944 debug_process_finalization (struct Lisp_Process *p)
2947 Lisp_Object instr, outstr;
2949 get_process_streams (p, &instr, &outstr);
2950 /* if it still has fds, then it hasn't been killed yet. */
2951 assert (NILP(instr));
2952 assert (NILP(outstr));
2954 /* #### More checks here */
2959 /************************************************************************/
2960 /* initialization */
2961 /************************************************************************/
2964 reinit_vars_of_event_mswindows (void)
2966 mswindows_in_modal_loop = 0;
2967 mswindows_pending_timers_count = 0;
2969 mswindows_event_stream = xnew (struct event_stream);
2971 mswindows_event_stream->event_pending_p = emacs_mswindows_event_pending_p;
2972 mswindows_event_stream->next_event_cb = emacs_mswindows_next_event;
2973 mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event;
2974 mswindows_event_stream->add_timeout_cb = emacs_mswindows_add_timeout;
2975 mswindows_event_stream->remove_timeout_cb = emacs_mswindows_remove_timeout;
2976 mswindows_event_stream->quit_p_cb = emacs_mswindows_quit_p;
2977 mswindows_event_stream->select_console_cb = emacs_mswindows_select_console;
2978 mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console;
2979 #ifdef HAVE_MSG_SELECT
2980 mswindows_event_stream->select_process_cb =
2981 (void (*)(struct Lisp_Process*))event_stream_unixoid_select_process;
2982 mswindows_event_stream->unselect_process_cb =
2983 (void (*)(struct Lisp_Process*))event_stream_unixoid_unselect_process;
2984 mswindows_event_stream->create_stream_pair_cb = event_stream_unixoid_create_stream_pair;
2985 mswindows_event_stream->delete_stream_pair_cb = event_stream_unixoid_delete_stream_pair;
2987 mswindows_event_stream->select_process_cb = emacs_mswindows_select_process;
2988 mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process;
2989 mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair;
2990 mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair;
2995 vars_of_event_mswindows (void)
2997 reinit_vars_of_event_mswindows ();
2999 mswindows_u_dispatch_event_queue = Qnil;
3000 staticpro (&mswindows_u_dispatch_event_queue);
3001 mswindows_u_dispatch_event_queue_tail = Qnil;
3002 pdump_wire (&mswindows_u_dispatch_event_queue_tail);
3004 mswindows_s_dispatch_event_queue = Qnil;
3005 staticpro (&mswindows_s_dispatch_event_queue);
3006 mswindows_s_dispatch_event_queue_tail = Qnil;
3007 pdump_wire (&mswindows_u_dispatch_event_queue_tail);
3009 mswindows_error_caught_in_modal_loop = Qnil;
3010 staticpro (&mswindows_error_caught_in_modal_loop);
3012 DEFVAR_BOOL ("mswindows-meta-activates-menu", &mswindows_meta_activates_menu /*
3013 *Controls whether pressing and releasing the Meta (Alt) key should
3014 activate the menubar.
3018 DEFVAR_BOOL ("mswindows-dynamic-frame-resize", &mswindows_dynamic_frame_resize /*
3019 *Controls redrawing frame contents during mouse-drag or keyboard resize
3020 operation. When non-nil, the frame is redrawn while being resized. When
3021 nil, frame is not redrawn, and exposed areas are filled with default
3022 MDI application background color. Note that this option only has effect
3023 if "Show window contents while dragging" is on in system Display/Plus!
3025 Default is t on fast machines, nil on slow.
3028 /* The description copied verbatim from nt-emacs. (C) Geoff Voelker */
3029 DEFVAR_INT ("mswindows-mouse-button-tolerance", &mswindows_mouse_button_tolerance /*
3030 *Analogue of double click interval for faking middle mouse events.
3031 The value is the minimum time in milliseconds that must elapse between
3032 left/right button down events before they are considered distinct events.
3033 If both mouse buttons are depressed within this interval, a middle mouse
3034 button down event is generated instead.
3035 If negative or zero, currently set system default is used instead.
3038 /* The description copied verbatim from nt-emacs. (C) Geoff Voelker */
3039 DEFVAR_INT ("mswindows-num-mouse-buttons", &mswindows_num_mouse_buttons /*
3040 Number of physical mouse buttons.
3043 DEFVAR_INT ("mswindows-mouse-button-max-skew-x", &mswindows_mouse_button_max_skew_x /*
3044 *Maximum horizontal distance in pixels between points in which left and
3045 right button clicks occurred for them to be translated into single
3046 middle button event. Clicks must occur in time not longer than defined
3047 by the variable `mswindows-mouse-button-tolerance'.
3048 If negative or zero, currently set system default is used instead.
3051 DEFVAR_INT ("mswindows-mouse-button-max-skew-y", &mswindows_mouse_button_max_skew_y /*
3052 *Maximum vertical distance in pixels between points in which left and
3053 right button clicks occurred for them to be translated into single
3054 middle button event. Clicks must occur in time not longer than defined
3055 by the variable `mswindows-mouse-button-tolerance'.
3056 If negative or zero, currently set system default is used instead.
3059 mswindows_mouse_button_max_skew_x = 0;
3060 mswindows_mouse_button_max_skew_y = 0;
3061 mswindows_mouse_button_tolerance = 0;
3062 mswindows_meta_activates_menu = 1;
3066 syms_of_event_mswindows (void)
3071 lstream_type_create_mswindows_selectable (void)
3073 init_slurp_stream ();
3074 init_shove_stream ();
3075 #if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
3076 init_winsock_stream ();
3081 init_event_mswindows_late (void)
3083 #ifdef HAVE_MSG_SELECT
3084 windows_fd = open("/dev/windows", O_RDONLY | O_NONBLOCK, 0);
3085 assert (windows_fd>=0);
3086 FD_SET (windows_fd, &input_wait_mask);
3087 FD_ZERO(&zero_mask);
3090 event_stream = mswindows_event_stream;
3092 mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE);
3093 mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);