X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=src%2Fevent-msw.c;h=fec746e5395db4879842b43741723124c8f8822c;hb=3062d425fac0473eb5aa2efc0bb002f6ce0cb028;hp=09054fd94eb8add05f4f382b333496ea91da472e;hpb=6883ee56ec887c2c48abe5b06b5e66aa74031910;p=chise%2Fxemacs-chise.git.1 diff --git a/src/event-msw.c b/src/event-msw.c index 09054fd..fec746e 100644 --- a/src/event-msw.c +++ b/src/event-msw.c @@ -1,7 +1,7 @@ -/* The mswindows event_stream interface. +/* The mswindows event_stream interface. Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. Copyright (C) 1995 Sun Microsystems, Inc. - Copyright (C) 1996 Ben Wing. + Copyright (C) 1996, 2000 Ben Wing. Copyright (C) 1997 Jonathan Harris. This file is part of XEmacs. @@ -28,6 +28,7 @@ Boston, MA 02111-1307, USA. */ Ultimately based on FSF. Rewritten by Ben Wing. Rewritten for mswindows by Jonathan Harris, November 1997 for 21.0. + Subprocess and modal loop support by Kirill M. Katsnelson. */ #include @@ -40,6 +41,7 @@ Boston, MA 02111-1307, USA. */ #endif #ifdef HAVE_MENUBARS +# include "menubar.h" # include "menubar-msw.h" #endif @@ -50,23 +52,33 @@ Boston, MA 02111-1307, USA. */ #include "device.h" #include "events.h" #include "frame.h" +#include "buffer.h" +#include "faces.h" #include "lstream.h" #include "process.h" #include "redisplay.h" +#include "select.h" +#include "window.h" #include "sysproc.h" #include "syswait.h" #include "systime.h" #include "sysdep.h" +#include "objects-msw.h" #include "events-mod.h" #ifdef HAVE_MSG_SELECT #include "sysfile.h" -#elif defined(__CYGWIN32__) +#include "console-tty.h" +#elif defined(CYGWIN) typedef unsigned int SOCKET; #endif #include #include +#if !(defined(CYGWIN) || defined(MINGW)) +# include /* For IShellLink */ +#endif + #ifdef HAVE_MENUBARS #define ADJR_MENUFLAG TRUE #else @@ -80,24 +92,24 @@ typedef unsigned int SOCKET; /* Timer ID used for button2 emulation */ #define BUTTON_2_TIMER_ID 1 -extern Lisp_Object -mswindows_get_toolbar_button_text (struct frame* f, int command_id); -extern Lisp_Object -mswindows_handle_toolbar_wm_command (struct frame* f, HWND ctrl, WORD id); - static Lisp_Object mswindows_find_frame (HWND hwnd); static Lisp_Object mswindows_find_console (HWND hwnd); -static Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods); -static int mswindows_modifier_state (BYTE* keymap, int has_AltGr); +static Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods, + int extendedp); +static int mswindows_modifier_state (BYTE* keymap, DWORD fwKeys, + int has_AltGr); static void mswindows_set_chord_timer (HWND hwnd); static int mswindows_button2_near_enough (POINTS p1, POINTS p2); static int mswindows_current_layout_has_AltGr (void); +static int mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam, + int downp, int keyp); static struct event_stream *mswindows_event_stream; #ifdef HAVE_MSG_SELECT extern SELECT_TYPE input_wait_mask, non_fake_input_wait_mask; extern SELECT_TYPE process_only_mask, tty_only_mask; +SELECT_TYPE zero_mask; extern int signal_event_pipe_initialized; int windows_fd; #endif @@ -114,11 +126,17 @@ static Lisp_Object mswindows_s_dispatch_event_queue, mswindows_s_dispatch_event_ /* The number of things we can wait on */ #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1) +#ifndef HAVE_MSG_SELECT /* List of mswindows waitable handles. */ static HANDLE mswindows_waitable_handles[MAX_WAITABLE]; /* Number of wait handles */ static int mswindows_waitable_count=0; +#endif /* HAVE_MSG_SELECT */ + +/* Brush for painting widgets */ +static HBRUSH widget_brush = 0; +static LONG last_widget_brushed = 0; /* Count of quit chars currently in the queue */ /* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc() @@ -127,10 +145,15 @@ int mswindows_quit_chars_count = 0; /* These are Lisp integers; see DEFVARS in this file for description. */ int mswindows_dynamic_frame_resize; -int mswindows_num_mouse_buttons; -int mswindows_mouse_button_max_skew_x; -int mswindows_mouse_button_max_skew_y; -int mswindows_mouse_button_tolerance; +int mswindows_alt_by_itself_activates_menu; +Fixnum mswindows_num_mouse_buttons; +Fixnum mswindows_mouse_button_max_skew_x; +Fixnum mswindows_mouse_button_max_skew_y; +Fixnum mswindows_mouse_button_tolerance; + +#ifdef DEBUG_XEMACS +Fixnum debug_mswindows_events; +#endif /* This is the event signaled by the event pump. See mswindows_pump_outstanding_events for comments */ @@ -139,6 +162,8 @@ static int mswindows_in_modal_loop; /* Count of wound timers */ static int mswindows_pending_timers_count; + +static DWORD mswindows_last_mouse_button_state; /************************************************************************/ /* Pipe instream - reads process output */ @@ -154,7 +179,7 @@ static int mswindows_pending_timers_count; /* This structure is allocated by the main thread, and is deallocated in the thread upon exit. There are situations when a thread remains blocked for a long time, much longer than the lstream - exists. For exmaple, "start notepad" command is issued from the + exists. For example, "start notepad" command is issued from the shell, then the shell is closed by C-c C-d. Although the shell process exits, its output pipe will not get closed until the notepad process exits also, because it inherits the pipe form the @@ -165,11 +190,11 @@ static int mswindows_pending_timers_count; struct ntpipe_slurp_stream_shared_data { HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */ - /* This is a manual-reset object. */ + /* This is a manual-reset object. */ HANDLE hev_caller; /* Caller blocks on this, and we signal it */ - /* This is a manual-reset object. */ + /* This is a manual-reset object. */ HANDLE hev_unsleep; /* Pipe read delay is canceled if this is set */ - /* This is a manual-reset object. */ + /* This is a manual-reset object. */ HANDLE hpipe; /* Pipe read end handle. */ LONG die_p; /* Thread must exit ASAP if non-zero */ BOOL eof_p : 1; /* Set when thread saw EOF */ @@ -180,7 +205,7 @@ struct ntpipe_slurp_stream_shared_data }; #define MAX_SLURP_STREAMS 32 -struct ntpipe_slurp_stream_shared_data +struct ntpipe_slurp_stream_shared_data shared_data_block[MAX_SLURP_STREAMS]={{0}}; struct ntpipe_slurp_stream @@ -193,7 +218,7 @@ DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-input", lstream_ntpipe_slurp, sizeof (struct ntpipe_slurp_stream)); /* This function is thread-safe, and is called from either thread - context. It serializes freeing shared dtata structure */ + context. It serializes freeing shared data structure */ static void slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s) { @@ -208,7 +233,7 @@ slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s) } static struct ntpipe_slurp_stream_shared_data* -slurper_allocate_shared_data() +slurper_allocate_shared_data (void) { int i=0; for (i=0; idie_p) break; - /* Block until the client finishes with retireving the rest of + /* Block until the client finishes with retrieving the rest of pipe data */ WaitForSingleObject (s->hev_thread, INFINITE); } @@ -341,11 +366,12 @@ get_ntpipe_input_stream_waitable (Lstream *stream) return s->thread_data->hev_caller; } -static int -ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size) +static Lstream_data_count +ntpipe_slurp_reader (Lstream *stream, unsigned char *data, + Lstream_data_count size) { /* This function must be called from the main thread only */ - struct ntpipe_slurp_stream_shared_data* s = + struct ntpipe_slurp_stream_shared_data* s = NTPIPE_SLURP_STREAM_DATA(stream)->thread_data; if (!s->die_p) @@ -354,7 +380,7 @@ ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size) /* Disallow pipe read delay for the thread: we need a character ASAP */ SetEvent (s->hev_unsleep); - + /* Check if we have a character ready. Give it a short delay, for the thread to awake from pipe delay, just ion case*/ wait_result = WaitForSingleObject (s->hev_caller, 2); @@ -405,7 +431,7 @@ ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size) ReadFile (s->hpipe, data, min (bytes_available, size), &bytes_read, NULL); } - + /* Now we can unblock thread, so it attempts to read more */ SetEvent (s->hev_thread); return bytes_read + 1; @@ -414,11 +440,11 @@ ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size) return 0; } -static int +static int ntpipe_slurp_closer (Lstream *stream) { /* This function must be called from the main thread only */ - struct ntpipe_slurp_stream_shared_data* s = + struct ntpipe_slurp_stream_shared_data* s = NTPIPE_SLURP_STREAM_DATA(stream)->thread_data; /* Force thread to stop */ @@ -449,13 +475,13 @@ init_slurp_stream (void) #define NTPIPE_SHOVE_STREAM_DATA(stream) \ LSTREAM_TYPE_DATA (stream, ntpipe_shove) -#define MAX_SHOVE_BUFFER_SIZE 128 - +#define MAX_SHOVE_BUFFER_SIZE 512 + struct ntpipe_shove_stream { LPARAM user_data; /* Any user data stored in the stream object */ HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */ - /* This is an auto-reset object. */ + /* This is an auto-reset object. */ HANDLE hpipe; /* Pipe write end handle. */ HANDLE hthread; /* Reader thread handle. */ char buffer[MAX_SHOVE_BUFFER_SIZE]; /* Buffer being written */ @@ -469,6 +495,7 @@ struct ntpipe_shove_stream DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-output", lstream_ntpipe_shove, sizeof (struct ntpipe_shove_stream)); +#ifndef HAVE_MSG_SELECT static DWORD WINAPI shove_thread (LPVOID vparam) { @@ -476,21 +503,24 @@ shove_thread (LPVOID vparam) for (;;) { - DWORD bytes_written; + DWORD bytes_written; /* Block on event and wait for a job */ InterlockedIncrement (&s->idle_p); WaitForSingleObject (s->hev_thread, INFINITE); - if (s->die_p) - break; - - /* Write passed buffer */ - if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL) - || bytes_written != s->size) + /* Write passed buffer if any */ + if (s->size > 0) { - s->error_p = TRUE; - InterlockedIncrement (&s->die_p); + if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL) + || bytes_written != s->size) + { + s->error_p = TRUE; + InterlockedIncrement (&s->die_p); + } + /* Set size to zero so we won't write it again if the closer sets + die_p and kicks us */ + s->size = 0; } if (s->die_p) @@ -513,7 +543,7 @@ make_ntpipe_output_stream (HANDLE hpipe, LPARAM param) s->hpipe = hpipe; s->user_data = param; - /* Create reader thread. This could fail, so do not + /* Create reader thread. This could fail, so do not create the event until thread is created */ s->hthread = CreateThread (NULL, 0, shove_thread, (LPVOID)s, CREATE_SUSPENDED, &thread_id_unused); @@ -523,6 +553,15 @@ make_ntpipe_output_stream (HANDLE hpipe, LPARAM param) return Qnil; } + /* Set the priority of the thread higher so we don't end up waiting + on it to send things. */ + if (!SetThreadPriority (s->hthread, THREAD_PRIORITY_HIGHEST)) + { + CloseHandle (s->hthread); + Lstream_delete (lstr); + return Qnil; + } + /* hev_thread is an auto-reset event, initially nonsignaled */ s->hev_thread = CreateEvent (NULL, FALSE, FALSE, NULL); @@ -540,9 +579,11 @@ get_ntpipe_output_stream_param (Lstream *stream) struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); return s->user_data; } +#endif -static int -ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size) +static Lstream_data_count +ntpipe_shove_writer (Lstream *stream, const unsigned char *data, + Lstream_data_count size) { struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); @@ -562,6 +603,10 @@ ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size) /* Start output */ InterlockedDecrement (&s->idle_p); SetEvent (s->hev_thread); + /* Give it a chance to run -- this dramatically improves performance + of things like crypt. */ + if (xSwitchToThread) /* not in Win9x or NT 3.51 */ + (void) xSwitchToThread (); return size; } @@ -580,14 +625,18 @@ ntpipe_shove_closer (Lstream *stream) /* Force thread stop */ InterlockedIncrement (&s->die_p); - /* Close pipe handle, possibly breaking it */ - CloseHandle (s->hpipe); - - /* Thread will end upon unblocking */ + /* Thread will end upon unblocking. If it's already unblocked this will + do nothing, but the thread won't look at die_p until it's written any + pending output. */ SetEvent (s->hev_thread); /* Wait while thread terminates */ WaitForSingleObject (s->hthread, INFINITE); + + /* Close pipe handle, possibly breaking it */ + CloseHandle (s->hpipe); + + /* Close the thread handle */ CloseHandle (s->hthread); /* Destroy the event */ @@ -617,8 +666,8 @@ struct winsock_stream SOCKET s; /* Socket handle (which is a Win32 handle) */ OVERLAPPED ov; /* Overlapped I/O structure */ void* buffer; /* Buffer. Allocated for input stream only */ - unsigned int bufsize; /* Number of bytes last read */ - unsigned int bufpos; /* Psition in buffer for next fetch */ + unsigned long bufsize; /* Number of bytes last read */ + unsigned long bufpos; /* Position in buffer for next fetch */ unsigned int error_p :1; /* I/O Error seen */ unsigned int eof_p :1; /* EOF Error seen */ unsigned int pending_p :1; /* There is a pending I/O operation */ @@ -650,8 +699,8 @@ winsock_initiate_read (struct winsock_stream *str) str->eof_p = 1; } -static int -winsock_reader (Lstream *stream, unsigned char *data, size_t size) +static Lstream_data_count +winsock_reader (Lstream *stream, unsigned char *data, Lstream_data_count size) { struct winsock_stream *str = WINSOCK_STREAM_DATA (stream); @@ -683,9 +732,9 @@ winsock_reader (Lstream *stream, unsigned char *data, size_t size) return 0; if (str->error_p) return -1; - + /* Return as much of buffer as we have */ - size = min (size, (size_t) (str->bufsize - str->bufpos)); + size = min (size, (Lstream_data_count) (str->bufsize - str->bufpos)); memcpy (data, (void*)((BYTE*)str->buffer + str->bufpos), size); str->bufpos += size; @@ -696,8 +745,9 @@ winsock_reader (Lstream *stream, unsigned char *data, size_t size) return size; } -static int -winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size) +static Lstream_data_count +winsock_writer (Lstream *stream, const unsigned char *data, + Lstream_data_count size) { struct winsock_stream *str = WINSOCK_STREAM_DATA (stream); @@ -724,11 +774,14 @@ winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size) if (size == 0) return 0; - + { ResetEvent (str->ov.hEvent); - - if (WriteFile ((HANDLE)str->s, data, size, NULL, &str->ov) + + /* Docs indicate that 4th parameter to WriteFile can be NULL since this is + * an overlapped operation. This fails on Win95 with winsock 1.x so we + * supply a spare address which is ignored by Win95 anyway. Sheesh. */ + if (WriteFile ((HANDLE)str->s, data, size, (LPDWORD)&str->buffer, &str->ov) || GetLastError() == ERROR_IO_PENDING) str->pending_p = 1; else @@ -767,7 +820,7 @@ winsock_was_blocked_p (Lstream *stream) } static Lisp_Object -make_winsock_stream_1 (SOCKET s, LPARAM param, CONST char *mode) +make_winsock_stream_1 (SOCKET s, LPARAM param, const char *mode) { Lisp_Object obj; Lstream *lstr = Lstream_new (lstream_winsock, mode); @@ -835,7 +888,7 @@ init_winsock_stream (void) /************************************************************************/ static int -mswindows_user_event_p (struct Lisp_Event* sevt) +mswindows_user_event_p (Lisp_Event* sevt) { return (sevt->event_type == key_press_event || sevt->event_type == button_press_event @@ -843,18 +896,18 @@ mswindows_user_event_p (struct Lisp_Event* sevt) || sevt->event_type == misc_user_event); } -/* +/* * Add an emacs event to the proper dispatch queue */ -static void +void mswindows_enqueue_dispatch_event (Lisp_Object event) { int user_p = mswindows_user_event_p (XEVENT(event)); enqueue_event (event, - user_p ? &mswindows_u_dispatch_event_queue : - &mswindows_s_dispatch_event_queue, + user_p ? &mswindows_u_dispatch_event_queue : + &mswindows_s_dispatch_event_queue, user_p ? &mswindows_u_dispatch_event_queue_tail : - &mswindows_s_dispatch_event_queue_tail); + &mswindows_s_dispatch_event_queue_tail); /* Avoid blocking on WaitMessage */ PostMessage (NULL, XM_BUMPQUEUE, 0, 0); @@ -871,10 +924,11 @@ mswindows_enqueue_misc_user_event (Lisp_Object channel, Lisp_Object function, Lisp_Object object) { Lisp_Object event = Fmake_event (Qnil, Qnil); - struct Lisp_Event* e = XEVENT (event); + Lisp_Event* e = XEVENT (event); e->event_type = misc_user_event; e->channel = channel; + e->timestamp = GetTickCount (); e->event.misc.function = function; e->event.misc.object = object; @@ -882,24 +936,24 @@ mswindows_enqueue_misc_user_event (Lisp_Object channel, Lisp_Object function, } void -mswindows_enqueue_magic_event (HWND hwnd, UINT message) +mswindows_enqueue_magic_event (HWND hwnd, UINT msg) { Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); - struct Lisp_Event* event = XEVENT (emacs_event); + Lisp_Event* event = XEVENT (emacs_event); event->channel = hwnd ? mswindows_find_frame (hwnd) : Qnil; event->timestamp = GetMessageTime(); event->event_type = magic_event; - EVENT_MSWINDOWS_MAGIC_TYPE (event) = message; + EVENT_MSWINDOWS_MAGIC_TYPE (event) = msg; mswindows_enqueue_dispatch_event (emacs_event); } static void -mswindows_enqueue_process_event (struct Lisp_Process* p) +mswindows_enqueue_process_event (Lisp_Process* p) { Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); - struct Lisp_Event* event = XEVENT (emacs_event); + Lisp_Event* event = XEVENT (emacs_event); Lisp_Object process; XSETPROCESS (process, p); @@ -911,37 +965,47 @@ mswindows_enqueue_process_event (struct Lisp_Process* p) } static void -mswindows_enqueue_mouse_button_event (HWND hwnd, UINT message, POINTS where, DWORD when) +mswindows_enqueue_mouse_button_event (HWND hwnd, UINT msg, POINTS where, + int mods, DWORD when) { + int downp = (msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || + msg == WM_RBUTTONDOWN); /* We always use last message time, because mouse button events may get delayed, and XEmacs double click recognition will fail */ Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); - struct Lisp_Event* event = XEVENT(emacs_event); + Lisp_Event* event = XEVENT (emacs_event); - event->channel = mswindows_find_frame(hwnd); + mswindows_handle_sticky_modifiers (0, 0, downp, 0); + event->channel = mswindows_find_frame (hwnd); event->timestamp = when; event->event.button.button = - (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 : - ((message==WM_RBUTTONDOWN || message==WM_RBUTTONUP) ? 3 : 2); + (msg==WM_LBUTTONDOWN || msg==WM_LBUTTONUP) ? 1 : + ((msg==WM_RBUTTONDOWN || msg==WM_RBUTTONUP) ? 3 : 2); event->event.button.x = where.x; event->event.button.y = where.y; - event->event.button.modifiers = mswindows_modifier_state (NULL, 0); - - if (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN || - message==WM_RBUTTONDOWN) + event->event.button.modifiers = mswindows_modifier_state (NULL, mods, 0); + + if (downp) { event->event_type = button_press_event; SetCapture (hwnd); + /* we need this to make sure the main window regains the focus + from control subwindows */ + if (GetFocus() != hwnd) + { + SetFocus (hwnd); + mswindows_enqueue_magic_event (hwnd, WM_SETFOCUS); + } } else { event->event_type = button_release_event; ReleaseCapture (); } - + mswindows_enqueue_dispatch_event (emacs_event); } @@ -949,7 +1013,7 @@ static void mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods) { Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); - struct Lisp_Event* event = XEVENT(emacs_event); + Lisp_Event* event = XEVENT(emacs_event); event->channel = mswindows_find_console(hwnd); event->timestamp = GetMessageTime(); @@ -964,19 +1028,19 @@ mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods) * Give a preference to user events over non-user ones. */ static Lisp_Object -mswindows_dequeue_dispatch_event () +mswindows_dequeue_dispatch_event (void) { Lisp_Object event; - struct Lisp_Event* sevt; + Lisp_Event* sevt; assert (!NILP(mswindows_u_dispatch_event_queue) || !NILP(mswindows_s_dispatch_event_queue)); event = dequeue_event ( - NILP(mswindows_u_dispatch_event_queue) ? - &mswindows_s_dispatch_event_queue : + NILP(mswindows_u_dispatch_event_queue) ? + &mswindows_s_dispatch_event_queue : &mswindows_u_dispatch_event_queue, - NILP(mswindows_u_dispatch_event_queue) ? + NILP(mswindows_u_dispatch_event_queue) ? &mswindows_s_dispatch_event_queue_tail : &mswindows_u_dispatch_event_queue_tail); @@ -993,42 +1057,35 @@ mswindows_dequeue_dispatch_event () /* * Remove and return the first emacs event on the dispatch queue that matches - * the supplied event - * Timeout event matches if interval_id equals to that of the given event. + * the supplied event. + * Timeout event matches if interval_id is equal to that of the given event. * Keypress event matches if logical AND between modifiers bitmask of the - * event in the queue and that of the given event is non-zero - * For all other event types, this function asserts. + * event in the queue and that of the given event is non-zero. + * For all other event types, this function aborts. */ Lisp_Object -mswindows_cancel_dispatch_event (struct Lisp_Event* match) +mswindows_cancel_dispatch_event (Lisp_Event *match) { Lisp_Object event; - Lisp_Object previous_event=Qnil; + Lisp_Object previous_event = Qnil; int user_p = mswindows_user_event_p (match); - Lisp_Object* head = user_p ? &mswindows_u_dispatch_event_queue : - &mswindows_s_dispatch_event_queue; - Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail : - &mswindows_s_dispatch_event_queue_tail; + Lisp_Object* head = user_p ? &mswindows_u_dispatch_event_queue : + &mswindows_s_dispatch_event_queue; + Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail : + &mswindows_s_dispatch_event_queue_tail; assert (match->event_type == timeout_event || match->event_type == key_press_event); EVENT_CHAIN_LOOP (event, *head) { - int found = 1; - if (XEVENT_TYPE (event) != match->event_type) - found = 0; - if (found && match->event_type == timeout_event - && (XEVENT(event)->event.timeout.interval_id != - match->event.timeout.interval_id)) - found = 0; - if (found && match->event_type == key_press_event - && ((XEVENT(event)->event.key.modifiers & - match->event.key.modifiers) == 0)) - found = 0; - - if (found) + Lisp_Event *e = XEVENT (event); + if ((e->event_type == match->event_type) && + ((e->event_type == timeout_event) ? + (e->event.timeout.interval_id == match->event.timeout.interval_id) : + /* Must be key_press_event */ + ((e->event.key.modifiers & match->event.key.modifiers) != 0))) { if (NILP (previous_event)) dequeue_event (head, tail); @@ -1038,7 +1095,7 @@ mswindows_cancel_dispatch_event (struct Lisp_Event* match) if (EQ (*tail, event)) *tail = previous_event; } - + return event; } previous_event = event; @@ -1046,6 +1103,7 @@ mswindows_cancel_dispatch_event (struct Lisp_Event* match) return Qnil; } +#ifndef HAVE_MSG_SELECT /************************************************************************/ /* Waitable handles manipulation */ /************************************************************************/ @@ -1078,9 +1136,10 @@ remove_waitable_handle (HANDLE h) if (ix < 0) return; - mswindows_waitable_handles [ix] = + mswindows_waitable_handles [ix] = mswindows_waitable_handles [--mswindows_waitable_count]; } +#endif /* HAVE_MSG_SELECT */ /************************************************************************/ @@ -1101,7 +1160,7 @@ mswindows_protect_modal_loop (Lisp_Object (*bfun) (Lisp_Object barg), { Lisp_Object tmp; - ++mswindows_in_modal_loop; + ++mswindows_in_modal_loop; tmp = condition_case_1 (Qt, bfun, barg, mswindows_modal_loop_error_handler, Qnil); @@ -1125,7 +1184,7 @@ mswindows_unmodalize_signal_maybe (void) } /* - * This is an unsafe part of event pump, guarded by + * This is an unsafe part of event pump, guarded by * condition_case. See mswindows_pump_outstanding_events */ static Lisp_Object @@ -1149,7 +1208,7 @@ mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d) Fdeallocate_event (event); UNGCPRO; - + /* Qt becomes return value of mswindows_pump_outstanding_events once we get here */ return Qt; @@ -1164,7 +1223,7 @@ mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d) * neither are waitable handles checked. The function pumps * thus only dispatch events already queued, as well as those * resulted in dispatching thereof. This is done by setting - * module local variable mswidows_in_modal_loop to nonzero. + * module local variable mswindows_in_modal_loop to nonzero. * * Return value is Qt if no errors was trapped, or Qunbound if * there was an error. @@ -1182,7 +1241,7 @@ mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d) * If the value of mswindows_error_caught_in_modal_loop is not * nil already upon entry, the function just returns non-nil. * This situation means that a new event has been queued while - * cancleng mode. The event will be dequeued on the next regular + * in cancel mode. The event will be dequeued on the next regular * call of next-event; the pump is off since error is caught. * The caller must *unconditionally* cancel modal loop if the * value returned by this function is nil. Otherwise, everything @@ -1197,29 +1256,83 @@ mswindows_pump_outstanding_events (void) Lisp_Object result = Qt; struct gcpro gcpro1; GCPRO1 (result); - + if (NILP(mswindows_error_caught_in_modal_loop)) - result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil); + result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil); UNGCPRO; return result; } -static void -mswindows_drain_windows_queue () +/* + * KEYBOARD_ONLY_P is set to non-zero when we are called from + * QUITP, and are interesting in keyboard messages only. + */ +static void +mswindows_drain_windows_queue (void) { MSG msg; + + /* should call mswindows_need_event_in_modal_loop() if in modal loop */ + assert (!mswindows_in_modal_loop); + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { + char class_name_buf [sizeof (XEMACS_CLASS) + 2] = ""; + + /* Don't translate messages destined for a dialog box, this + makes keyboard traversal work. I think?? */ + if (mswindows_is_dialog_msg (&msg)) + { + mswindows_unmodalize_signal_maybe (); + continue; + } + + /* We have to translate messages that are not sent to an XEmacs + frame. This is so that key presses work ok in things like + edit fields. However, we *musn't* translate message for XEmacs + frames as this is handled in the wnd proc. + We also have to avoid generating paint magic events for windows + that aren't XEmacs frames */ + /* GetClassName will truncate a longer class name. By adding one + extra character, we are forcing textual comparison to fail + if the name is longer than XEMACS_CLASS */ + + GetClassName (msg.hwnd, class_name_buf, sizeof (class_name_buf) - 1); + if (stricmp (class_name_buf, XEMACS_CLASS) != 0) + { + /* Not an XEmacs frame */ + TranslateMessage (&msg); + } + else if (msg.message == WM_PAINT) + { + struct mswindows_frame* msframe; + + /* hdc will be NULL unless this is a subwindow - in which case we + shouldn't have received a paint message for it here. */ + assert (msg.wParam == 0); + + /* Queue a magic event for handling when safe */ + msframe = + FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (msg.hwnd))); + if (!msframe->paint_pending) + { + msframe->paint_pending = 1; + mswindows_enqueue_magic_event (msg.hwnd, WM_PAINT); + } + /* Don't dispatch. WM_PAINT is always the last message in the + queue so it's OK to just return. */ + return; + } DispatchMessage (&msg); mswindows_unmodalize_signal_maybe (); } } -/* - * This is a special flavour of the mswindows_need_event function, +/* + * This is a special flavor of the mswindows_need_event function, * used while in event pump. Actually, there is only kind of events * allowed while in event pump: a timer. An attempt to fetch any - * other event leads to a dealock, as there's no source of user input + * other event leads to a deadlock, as there's no source of user input * ('cause event pump mirrors windows modal loop, which is a sole * owner of thread message queue). * @@ -1248,7 +1361,7 @@ mswindows_need_event_in_modal_loop (int badly_p) /* We'll deadlock if go waiting */ if (mswindows_pending_timers_count == 0) error ("Deadlock due to an attempt to call next-event in a wrong context"); - + /* Fetch and dispatch any pending timers */ GetMessage (&msg, NULL, WM_TIMER, WM_TIMER); DispatchMessage (&msg); @@ -1273,10 +1386,6 @@ mswindows_need_event (int badly_p) return; } - /* Have to drain Windows message queue first, otherwise, we may miss - quit char when called from quit_p */ - mswindows_drain_windows_queue (); - while (NILP (mswindows_u_dispatch_event_queue) && NILP (mswindows_s_dispatch_event_queue)) { @@ -1285,7 +1394,7 @@ mswindows_need_event (int badly_p) SELECT_TYPE temp_mask = input_wait_mask; EMACS_TIME sometime; EMACS_SELECT_TIME select_time_to_block, *pointer_to_this; - + if (badly_p) pointer_to_this = 0; else @@ -1294,10 +1403,12 @@ mswindows_need_event (int badly_p) EMACS_TIME_TO_SELECT_TIME (sometime, select_time_to_block); pointer_to_this = &select_time_to_block; } + active = select (MAXDESC, &temp_mask, 0, 0, pointer_to_this); - + if (active == 0) { + assert (!badly_p); return; /* timeout */ } else if (active > 0) @@ -1306,32 +1417,51 @@ mswindows_need_event (int badly_p) { mswindows_drain_windows_queue (); } - - /* Look for a process event */ - for (i = 0; i < MAXDESC-1; i++) + else { - if (FD_ISSET (i, &temp_mask)) +#ifdef HAVE_TTY + /* Look for a TTY event */ + for (i = 0; i < MAXDESC-1; i++) { - if (FD_ISSET (i, &process_only_mask)) - { - struct Lisp_Process *p = - get_process_from_usid (FD_TO_USID(i)); - - mswindows_enqueue_process_event (p); - } - else if (FD_ISSET (i, &tty_only_mask)) + /* To avoid race conditions (among other things, an infinite + loop when called from Fdiscard_input()), we must return + user events ahead of process events. */ + if (FD_ISSET (i, &temp_mask) && FD_ISSET (i, &tty_only_mask)) { - /* do we care about tty events? Do we - ever get tty events? */ + struct console *c = tty_find_console_from_fd (i); + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + Lisp_Event* event = XEVENT (emacs_event); + + assert (c); + if (read_event_from_tty_or_stream_desc (event, c, i)) + { + mswindows_enqueue_dispatch_event (emacs_event); + return; + } } - else + } +#endif + /* Look for a process event */ + for (i = 0; i < MAXDESC-1; i++) + { + if (FD_ISSET (i, &temp_mask)) { - /* We might get here when a fake event came - through a signal. Return a dummy event, so - that a cycle of the command loop will - occur. */ - drain_signal_event_pipe (); - mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); + if (FD_ISSET (i, &process_only_mask)) + { + Lisp_Process *p = + get_process_from_usid (FD_TO_USID(i)); + + mswindows_enqueue_process_event (p); + } + else + { + /* We might get here when a fake event came + through a signal. Return a dummy event, so + that a cycle of the command loop will + occur. */ + drain_signal_event_pipe (); + mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); + } } } } @@ -1340,7 +1470,7 @@ mswindows_need_event (int badly_p) { if (errno != EINTR) { - /* something bad happended */ + /* something bad happened */ assert(0); } } @@ -1350,67 +1480,79 @@ mswindows_need_event (int badly_p) } #else /* Now try getting a message or process event */ - active = MsgWaitForMultipleObjects (mswindows_waitable_count, - mswindows_waitable_handles, - FALSE, badly_p ? INFINITE : 0, - QS_ALLINPUT); - - /* This will assert if handle being waited for becomes abandoned. - Not the case currently tho */ - assert ((!badly_p && active == WAIT_TIMEOUT) || - (active >= WAIT_OBJECT_0 && - active <= WAIT_OBJECT_0 + mswindows_waitable_count)); - - if (active == WAIT_TIMEOUT) - { - /* No luck trying - just return what we've already got */ - return; - } - else if (active == WAIT_OBJECT_0 + mswindows_waitable_count) - { - /* Got your message, thanks */ - mswindows_drain_windows_queue (); - } - else - { - int ix = active - WAIT_OBJECT_0; - /* First, try to find which process' ouptut has signaled */ - struct Lisp_Process *p = - get_process_from_usid (HANDLE_TO_USID (mswindows_waitable_handles[ix])); - if (p != NULL) - { - /* Found a signaled process input handle */ - mswindows_enqueue_process_event (p); - } - else - { - /* None. This means that the process handle itself has signaled. - Remove the handle from the wait vector, and make status_ntoify - note the exited process */ - mswindows_waitable_handles [ix] = - mswindows_waitable_handles [--mswindows_waitable_count]; - kick_status_notify (); - /* Have to return something: there may be no accompanying - process event */ - mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); - } - } + active = MsgWaitForMultipleObjects (mswindows_waitable_count, + mswindows_waitable_handles, + FALSE, badly_p ? INFINITE : 0, + QS_ALLINPUT); + + /* This will assert if handle being waited for becomes abandoned. + Not the case currently tho */ + assert ((!badly_p && active == WAIT_TIMEOUT) || + (active >= WAIT_OBJECT_0 && + active <= WAIT_OBJECT_0 + mswindows_waitable_count)); + + if (active == WAIT_TIMEOUT) + { + /* No luck trying - just return what we've already got */ + return; + } + else if (active == WAIT_OBJECT_0 + mswindows_waitable_count) + { + /* Got your message, thanks */ + mswindows_drain_windows_queue (); + } + else + { + int ix = active - WAIT_OBJECT_0; + /* First, try to find which process' output has signaled */ + Lisp_Process *p = + get_process_from_usid (HANDLE_TO_USID (mswindows_waitable_handles[ix])); + if (p != NULL) + { + /* Found a signaled process input handle */ + mswindows_enqueue_process_event (p); + } + else + { + /* None. This means that the process handle itself has signaled. + Remove the handle from the wait vector, and make status_notify + note the exited process */ + mswindows_waitable_handles [ix] = + mswindows_waitable_handles [--mswindows_waitable_count]; + kick_status_notify (); + /* We need to return a process event here so that + (1) accept-process-output will return when called on this + process, and (2) status notifications will happen in + accept-process-output, sleep-for, and sit-for. */ + /* #### horrible kludge till my real process fixes go in. + */ + if (!NILP (Vprocess_list)) + { + Lisp_Object vaffanculo = XCAR (Vprocess_list); + mswindows_enqueue_process_event (XPROCESS (vaffanculo)); + } + else /* trash me soon. */ + /* Have to return something: there may be no accompanying + process event */ + mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); + } + } #endif - } /* while */ + } /* while */ } /************************************************************************/ /* Event generators */ /************************************************************************/ -/* +/* * Callback procedure for synchronous timer messages */ static void CALLBACK mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime) { Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); - struct Lisp_Event *event = XEVENT (emacs_event); + Lisp_Event *event = XEVENT (emacs_event); if (KillTimer (NULL, id_timer)) --mswindows_pending_timers_count; @@ -1419,11 +1561,13 @@ mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime) event->timestamp = dwtime; event->event_type = timeout_event; event->event.timeout.interval_id = id_timer; + event->event.timeout.function = Qnil; + event->event.timeout.object = Qnil; mswindows_enqueue_dispatch_event (emacs_event); } -/* +/* * Callback procedure for dde messages * * We execute a dde Open("file") by simulating a file drop, so dde support @@ -1434,9 +1578,9 @@ HDDEDATA CALLBACK mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv, HSZ hszTopic, HSZ hszItem, HDDEDATA hdata, DWORD dwData1, DWORD dwData2) -{ +{ switch (uType) - { + { case XTYP_CONNECT: if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)) return (HDDEDATA)TRUE; @@ -1449,24 +1593,24 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv, { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } }; if (!(hszItem || DdeCmpStringHandles (hszItem, mswindows_dde_service)) && - !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))); + !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))) return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs, sizeof (pairs), 0L, 0, uFmt, 0)); } - return (HDDEDATA)NULL; + return (HDDEDATA)NULL; case XTYP_EXECUTE: if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)) { DWORD len = DdeGetData (hdata, NULL, 0, 0); - char *cmd = alloca (len+1); + LPBYTE cmd = (LPBYTE) alloca (len+1); char *end; char *filename; struct gcpro gcpro1, gcpro2; Lisp_Object l_dndlist = Qnil; Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); Lisp_Object frmcons, devcons, concons; - struct Lisp_Event *event = XEVENT (emacs_event); + Lisp_Event *event = XEVENT (emacs_event); DdeGetData (hdata, cmd, len, 0); cmd[len] = '\0'; @@ -1498,7 +1642,7 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv, if (*end) return DDE_FNOTPROCESSED; -#ifdef __CYGWIN32__ +#ifdef CYGWIN filename = alloca (cygwin32_win32_to_posix_path_list_buf_size (cmd) + 5); strcpy (filename, "file:"); cygwin32_win32_to_posix_path_list (cmd, filename+5); @@ -1534,604 +1678,1298 @@ mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv, UNGCPRO; return (HDDEDATA) DDE_FACK; } - DdeFreeDataHandle (hdata); + DdeFreeDataHandle (hdata); return (HDDEDATA) DDE_FNOTPROCESSED; - default: - return (HDDEDATA) NULL; - } + default: + return (HDDEDATA) NULL; + } } #endif /* - * The windows procedure for the window class XEMACS_CLASS + * Helper to do repainting - repaints can happen both from the windows + * procedure and from magic events */ -LRESULT WINAPI -mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +static void +mswindows_handle_paint (struct frame *frame) { - /* Note: Remember to initialise emacs_event and event before use. - This code calls code that can GC. You must GCPRO before calling such code. */ - Lisp_Object emacs_event = Qnil; - Lisp_Object fobj = Qnil; + HWND hwnd = FRAME_MSWINDOWS_HANDLE (frame); - struct Lisp_Event *event; - struct frame *frame; - struct mswindows_frame* msframe; - - switch (message) - { - case WM_ERASEBKGND: - /* Erase background only during non-dynamic sizing */ - msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); - if (msframe->sizing && !mswindows_dynamic_frame_resize) - goto defproc; - return 1; - - case WM_CLOSE: - fobj = mswindows_find_frame (hwnd); - mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt)); - break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: + /* According to the docs we need to check GetUpdateRect() before + actually doing a WM_PAINT */ + if (GetUpdateRect (hwnd, NULL, FALSE)) { - BYTE keymap[256]; - int has_AltGr = mswindows_current_layout_has_AltGr (); - int mods; - Lisp_Object keysym; - - GetKeyboardState (keymap); - mods = mswindows_modifier_state (keymap, has_AltGr); + PAINTSTRUCT paintStruct; + int x, y, width, height; - /* Handle those keys that TranslateMessage won't generate a WM_CHAR for */ - if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam, mods))) - mswindows_enqueue_keypress_event (hwnd, keysym, mods); - else + BeginPaint (hwnd, &paintStruct); + x = paintStruct.rcPaint.left; + y = paintStruct.rcPaint.top; + width = paintStruct.rcPaint.right - paintStruct.rcPaint.left; + height = paintStruct.rcPaint.bottom - paintStruct.rcPaint.top; + /* Normally we want to ignore expose events when child + windows are unmapped, however once we are in the guts of + WM_PAINT we need to make sure that we don't register + unmaps then because they will not actually occur. */ + /* #### commenting out the next line seems to fix some problems + but not all. only andy currently understands this stuff and + he needs to review it more carefully. --ben */ + if (!check_for_ignored_expose (frame, x, y, width, height)) { - int quit_ch = CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd))); - BYTE keymap_orig[256]; - MSG msg = { hwnd, message, wParam, lParam, GetMessageTime(), (GetMessagePos()) }; - memcpy (keymap_orig, keymap, 256); - - /* Remove shift modifier from an ascii character */ - mods &= ~MOD_SHIFT; - - /* Clear control and alt modifiers out of the keymap */ - keymap [VK_RCONTROL] = 0; - keymap [VK_LMENU] = 0; - if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80) || !(keymap [VK_RMENU] & 0x80)) - { - keymap [VK_LCONTROL] = 0; - keymap [VK_CONTROL] = 0; - keymap [VK_RMENU] = 0; - keymap [VK_MENU] = 0; - } - SetKeyboardState (keymap); + hold_ignored_expose_registration = 1; + mswindows_redraw_exposed_area (frame, x, y, width, height); + hold_ignored_expose_registration = 0; + } + EndPaint (hwnd, &paintStruct); + } +} - /* Have some WM_[SYS]CHARS in the queue */ - TranslateMessage (&msg); +/* + * Returns 1 if a key is a real modifier or special key, which + * is better handled by DefWindowProc + */ +static int +key_needs_default_processing_p (UINT vkey) +{ + if (mswindows_alt_by_itself_activates_menu && vkey == VK_MENU + /* if we let ALT activate the menu like this, then sticky ALT-modified + keystrokes become impossible. */ + && !modifier_keys_are_sticky) + return 1; - while (PeekMessage (&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE) - || PeekMessage (&msg, hwnd, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE)) - { - int mods1 = mods; - WPARAM ch = msg.wParam; - - /* If a quit char with no modifiers other than control and - shift, then mark it with a fake modifier, which is removed - upon dequeueing the event */ - /* #### This might also not withstand localization, if - quit character is not a latin-1 symbol */ - if (((quit_ch < ' ' && (mods & MOD_CONTROL) && quit_ch + 'a' - 1 == ch) - || (quit_ch >= ' ' && !(mods & MOD_CONTROL) && quit_ch == ch)) - && ((mods & ~(MOD_CONTROL | MOD_SHIFT)) == 0)) - { - mods1 |= FAKE_MOD_QUIT; - ++mswindows_quit_chars_count; - } + return 0; +} - mswindows_enqueue_keypress_event (hwnd, make_char(ch), mods1); - } /* while */ - SetKeyboardState (keymap_orig); - } /* else */ - } - /* F10 causes menu activation by default. We do not want this */ - if (wParam != VK_F10) - goto defproc; - break; - - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - /* Real middle mouse button has nothing to do with emulated one: - if one wants to exercise fingers playing chords on the mouse, - he is allowed to do that! */ - mswindows_enqueue_mouse_button_event (hwnd, message, - MAKEPOINTS (lParam), GetMessageTime()); - break; - - case WM_LBUTTONUP: - msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); - msframe->last_click_time = GetMessageTime(); - - KillTimer (hwnd, BUTTON_2_TIMER_ID); - msframe->button2_need_lbutton = 0; - if (msframe->ignore_next_lbutton_up) - { - msframe->ignore_next_lbutton_up = 0; - } - else if (msframe->button2_is_down) - { - msframe->button2_is_down = 0; - msframe->ignore_next_rbutton_up = 1; - mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, - MAKEPOINTS (lParam), GetMessageTime()); - } - else - { - if (msframe->button2_need_rbutton) - { - msframe->button2_need_rbutton = 0; - mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, - MAKEPOINTS (lParam), GetMessageTime()); - } - mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP, - MAKEPOINTS (lParam), GetMessageTime()); - } - break; +/* key-handling code is always ugly. It just ends up working out + that way. - case WM_RBUTTONUP: - msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); - msframe->last_click_time = GetMessageTime(); + #### Most of the sticky-modifier code below is copied from similar + code in event-Xt.c. They should somehow or other be merged. - KillTimer (hwnd, BUTTON_2_TIMER_ID); - msframe->button2_need_rbutton = 0; - if (msframe->ignore_next_rbutton_up) - { - msframe->ignore_next_rbutton_up = 0; - } - else if (msframe->button2_is_down) - { - msframe->button2_is_down = 0; - msframe->ignore_next_lbutton_up = 1; - mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, - MAKEPOINTS (lParam), GetMessageTime()); - } - else - { - if (msframe->button2_need_lbutton) - { - msframe->button2_need_lbutton = 0; - mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, - MAKEPOINTS (lParam), GetMessageTime()); - } - mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP, - MAKEPOINTS (lParam), GetMessageTime()); - } - break; + Here are some pointers: - case WM_LBUTTONDOWN: - msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + -- DOWN_MASK indicates which modifiers should be treated as "down" + when the corresponding upstroke happens. It gets reset for + a particular modifier when that modifier goes up, and reset + for all modifiers when a non-modifier key is pressed. Example: - if (msframe->button2_need_lbutton) - { - KillTimer (hwnd, BUTTON_2_TIMER_ID); - msframe->button2_need_lbutton = 0; - msframe->button2_need_rbutton = 0; - if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam))) - { - mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, - MAKEPOINTS (lParam), GetMessageTime()); - msframe->button2_is_down = 1; - } - else - { - mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, - msframe->last_click_point, msframe->last_click_time); - mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, - MAKEPOINTS (lParam), GetMessageTime()); - } - } - else - { - mswindows_set_chord_timer (hwnd); - msframe->button2_need_rbutton = 1; - msframe->last_click_point = MAKEPOINTS (lParam); - } - msframe->last_click_time = GetMessageTime(); - break; + I press Control-A-Shift and then release Control-A-Shift. + I want the Shift key to be sticky but not the Control key. - case WM_RBUTTONDOWN: - msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + -- If a modifier key is sticky, I can unstick it by pressing + the modifier key again. */ - if (msframe->button2_need_rbutton) - { - KillTimer (hwnd, BUTTON_2_TIMER_ID); - msframe->button2_need_lbutton = 0; - msframe->button2_need_rbutton = 0; - if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam))) - { - mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, - MAKEPOINTS (lParam), GetMessageTime()); - msframe->button2_is_down = 1; - } - else - { - mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, - msframe->last_click_point, msframe->last_click_time); - mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, - MAKEPOINTS (lParam), GetMessageTime()); - } - } - else - { - mswindows_set_chord_timer (hwnd); - msframe->button2_need_lbutton = 1; - msframe->last_click_point = MAKEPOINTS (lParam); - } - msframe->last_click_time = GetMessageTime(); - break; - - case WM_TIMER: - if (wParam == BUTTON_2_TIMER_ID) - { - msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); - KillTimer (hwnd, BUTTON_2_TIMER_ID); +static WPARAM last_downkey; +static int need_to_add_mask, down_mask; - if (msframe->button2_need_lbutton) - { - msframe->button2_need_lbutton = 0; - mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, - msframe->last_click_point, msframe->last_click_time); - } - else if (msframe->button2_need_rbutton) - { - msframe->button2_need_rbutton = 0; - mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, - msframe->last_click_point, msframe->last_click_time); - } - } - else - assert ("Spurious timer fired" == 0); - break; +#define XEMSW_LCONTROL (1<<0) +#define XEMSW_RCONTROL (1<<1) +#define XEMSW_LSHIFT (1<<2) +#define XEMSW_RSHIFT (1<<3) +#define XEMSW_LMENU (1<<4) +#define XEMSW_RMENU (1<<5) - case WM_MOUSEMOVE: - /* Optimization: don't report mouse movement while size is changind */ - msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); - if (!msframe->sizing) - { - /* When waiting for the second mouse button to finish - button2 emulation, and have moved too far, just pretend - as if timer has expired. This impoves drag-select feedback */ - if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton) - && !mswindows_button2_near_enough (msframe->last_click_point, - MAKEPOINTS (lParam))) +static int +mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam, + int downp, int keyp) +{ + int mods = 0; + + if (!modifier_keys_are_sticky) /* Optimize for non-sticky modifiers */ + return 0; + + if (! (keyp && + (wParam == VK_CONTROL || wParam == VK_LCONTROL || + wParam == VK_RCONTROL || + wParam == VK_MENU || wParam == VK_LMENU || + wParam == VK_RMENU || + wParam == VK_SHIFT || wParam == VK_LSHIFT || + wParam == VK_RSHIFT))) + { /* Not a modifier key */ + if (downp && keyp && !last_downkey) + last_downkey = wParam; + /* If I hold press-and-release the Control key and then press + and hold down the right arrow, I want it to auto-repeat + Control-Right. On the other hand, if I do the same but + manually press the Right arrow a bunch of times, I want + to see one Control-Right and then a bunch of Rights. + This means that we need to distinguish between an + auto-repeated key and a key pressed and released a bunch + of times. */ + else if ((downp && !keyp) || + (downp && keyp && last_downkey && + (wParam != last_downkey || + /* the "previous key state" bit indicates autorepeat */ + ! (lParam & (1 << 30))))) { - KillTimer (hwnd, BUTTON_2_TIMER_ID); - SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0); + need_to_add_mask = 0; + last_downkey = 0; } + if (downp) + down_mask = 0; - emacs_event = Fmake_event (Qnil, Qnil); - event = XEVENT(emacs_event); - - event->channel = mswindows_find_frame(hwnd); - event->timestamp = GetMessageTime(); - event->event_type = pointer_motion_event; - event->event.motion.x = MAKEPOINTS(lParam).x; - event->event.motion.y = MAKEPOINTS(lParam).y; - event->event.motion.modifiers = mswindows_modifier_state (NULL, 0); - - mswindows_enqueue_dispatch_event (emacs_event); + mods = need_to_add_mask; } - break; - - case WM_CANCELMODE: - ReleaseCapture (); - /* Queue a `cancel-mode-internal' misc user event, so mouse - selection would be canceled if any */ - mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd), - Qcancel_mode_internal, Qnil); - break; - -#ifdef HAVE_TOOLBARS - case WM_NOTIFY: + else /* Modifier key pressed */ { - LPTOOLTIPTEXT tttext = (LPTOOLTIPTEXT)lParam; - Lisp_Object btext; - if (tttext->hdr.code == TTN_NEEDTEXT) + /* If a non-modifier key was pressed in the middle of a bunch + of modifiers, then it unsticks all the modifiers that were + previously pressed. We cannot unstick the modifiers until + now because we want to check for auto-repeat of the + non-modifier key. */ + + if (last_downkey) { - /* find out which toolbar */ - frame = XFRAME (mswindows_find_frame (hwnd)); - btext = mswindows_get_toolbar_button_text ( frame, - tttext->hdr.idFrom ); - - tttext->lpszText = NULL; - tttext->hinst = NULL; - - if (!NILP(btext)) - { - /* I think this is safe since the text will only go away - when the toolbar does...*/ - tttext->lpszText=XSTRING_DATA (btext); - } -#if 0 - tttext->uFlags |= TTF_DI_SETITEM; -#endif - } - } - break; -#endif - - case WM_PAINT: - { - PAINTSTRUCT paintStruct; - - frame = XFRAME (mswindows_find_frame (hwnd)); + last_downkey = 0; + need_to_add_mask = 0; + } - BeginPaint (hwnd, &paintStruct); - mswindows_redraw_exposed_area (frame, - paintStruct.rcPaint.left, paintStruct.rcPaint.top, - paintStruct.rcPaint.right, paintStruct.rcPaint.bottom); - EndPaint (hwnd, &paintStruct); +#define FROB(mask) \ +do { \ + if (downp && keyp) \ + { \ + /* If modifier key is already sticky, \ + then unstick it. Note that we do \ + not test down_mask to deal with the \ + unlikely but possible case that the \ + modifier key auto-repeats. */ \ + if (need_to_add_mask & mask) \ + { \ + need_to_add_mask &= ~mask; \ + down_mask &= ~mask; \ + } \ + else \ + down_mask |= mask; \ + } \ + else \ + { \ + if (down_mask & mask) \ + { \ + down_mask &= ~mask; \ + need_to_add_mask |= mask; \ + } \ + } \ +} while (0) + + if ((wParam == VK_CONTROL && (lParam & 0x1000000)) + || wParam == VK_RCONTROL) + FROB (XEMSW_RCONTROL); + if ((wParam == VK_CONTROL && !(lParam & 0x1000000)) + || wParam == VK_LCONTROL) + FROB (XEMSW_LCONTROL); + + if ((wParam == VK_SHIFT && (lParam & 0x1000000)) + || wParam == VK_RSHIFT) + FROB (XEMSW_RSHIFT); + if ((wParam == VK_SHIFT && !(lParam & 0x1000000)) + || wParam == VK_LSHIFT) + FROB (XEMSW_LSHIFT); + + if ((wParam == VK_MENU && (lParam & 0x1000000)) + || wParam == VK_RMENU) + FROB (XEMSW_RMENU); + if ((wParam == VK_MENU && !(lParam & 0x1000000)) + || wParam == VK_LMENU) + FROB (XEMSW_LMENU); } - break; +#undef FROB - case WM_SIZE: - /* We only care about this message if our size has really changed */ - if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED) + if (mods && downp) { - RECT rect; - int columns, rows; - - fobj = mswindows_find_frame (hwnd); - frame = XFRAME (fobj); - msframe = FRAME_MSWINDOWS_DATA (frame); + BYTE keymap[256]; - /* We cannot handle frame map and unmap hooks right in - this routine, because these may throw. We queue - magic events to run these hooks instead - kkm */ + GetKeyboardState (keymap); - if (wParam==SIZE_MINIMIZED) + if (mods & XEMSW_LCONTROL) { - /* Iconified */ - FRAME_VISIBLE_P (frame) = 0; - mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME); + keymap [VK_CONTROL] |= 0x80; + keymap [VK_LCONTROL] |= 0x80; } - else + if (mods & XEMSW_RCONTROL) { - GetClientRect(hwnd, &rect); - FRAME_PIXWIDTH(frame) = rect.right; - FRAME_PIXHEIGHT(frame) = rect.bottom; - - pixel_to_real_char_size (frame, rect.right, rect.bottom, - &FRAME_MSWINDOWS_CHARWIDTH (frame), - &FRAME_MSWINDOWS_CHARHEIGHT (frame)); + keymap [VK_CONTROL] |= 0x80; + keymap [VK_RCONTROL] |= 0x80; + } - pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows); - change_frame_size (frame, rows, columns, 1); + if (mods & XEMSW_LSHIFT) + { + keymap [VK_SHIFT] |= 0x80; + keymap [VK_LSHIFT] |= 0x80; + } + if (mods & XEMSW_RSHIFT) + { + keymap [VK_SHIFT] |= 0x80; + keymap [VK_RSHIFT] |= 0x80; + } - /* If we are inside frame creation, we have to apply geometric - properties now. */ - if (FRAME_MSWINDOWS_TARGET_RECT (frame)) - { - /* Yes, we have to size again */ - mswindows_size_frame_internal ( frame, - FRAME_MSWINDOWS_TARGET_RECT - (frame)); - /* Reset so we do not get here again. The SetWindowPos call in - * mswindows_size_frame_internal can cause recursion here. */ - if (FRAME_MSWINDOWS_TARGET_RECT (frame)) - { - xfree (FRAME_MSWINDOWS_TARGET_RECT (frame)); - FRAME_MSWINDOWS_TARGET_RECT (frame) = 0; - } - } - else - { - if (!msframe->sizing && !FRAME_VISIBLE_P (frame)) - mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME); - FRAME_VISIBLE_P (frame) = 1; - - if (!msframe->sizing || mswindows_dynamic_frame_resize) - redisplay (); - } + if (mods & XEMSW_LMENU) + { + keymap [VK_MENU] |= 0x80; + keymap [VK_LMENU] |= 0x80; + } + if (mods & XEMSW_RMENU) + { + keymap [VK_MENU] |= 0x80; + keymap [VK_RMENU] |= 0x80; } + + SetKeyboardState (keymap); + return 1; } - break; - /* Misc magic events which only require that the frame be identified */ - case WM_SETFOCUS: - case WM_KILLFOCUS: - mswindows_enqueue_magic_event (hwnd, message); - break; + return 0; +} - case WM_WINDOWPOSCHANGING: - { - WINDOWPOS *wp = (LPWINDOWPOS) lParam; - WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) }; - GetWindowPlacement(hwnd, &wpl); - - /* Only interested if size is changing and we're not being iconified */ - if (wpl.showCmd != SW_SHOWMINIMIZED - && wpl.showCmd != SW_SHOWMAXIMIZED - && !(wp->flags & SWP_NOSIZE)) - { - RECT ncsize = { 0, 0, 0, 0 }; - int pixwidth, pixheight; - AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE), - GetMenu(hwnd) != NULL, - GetWindowLong (hwnd, GWL_EXSTYLE)); - - round_size_to_real_char (XFRAME (mswindows_find_frame (hwnd)), - wp->cx - (ncsize.right - ncsize.left), - wp->cy - (ncsize.bottom - ncsize.top), - &pixwidth, &pixheight); - - /* Convert client sizes to window sizes */ - pixwidth += (ncsize.right - ncsize.left); - pixheight += (ncsize.bottom - ncsize.top); - - if (wpl.showCmd != SW_SHOWMAXIMIZED) - { - /* Adjust so that the bottom or right doesn't move if it's - * the top or left that's being changed */ - RECT rect; - GetWindowRect (hwnd, &rect); - - if (rect.left != wp->x) - wp->x += wp->cx - pixwidth; - if (rect.top != wp->y) - wp->y += wp->cy - pixheight; - } +static void +clear_sticky_modifiers (void) +{ + need_to_add_mask = 0; + last_downkey = 0; + down_mask = 0; +} - wp->cx = pixwidth; - wp->cy = pixheight; - } - /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts - window position if the user tries to track window too small */ - } - goto defproc; +#ifdef DEBUG_XEMACS - case WM_ENTERSIZEMOVE: - msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); - msframe->sizing = 1; - return 0; +#if 0 - case WM_EXITSIZEMOVE: - msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); - msframe->sizing = 0; - /* Queue noop event */ - mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); - return 0; +static void +output_modifier_keyboard_state (void) +{ + BYTE keymap[256]; + + GetKeyboardState (keymap); + + stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", + keymap[VK_MENU] & 0x80 ? 1 : 0, + keymap[VK_MENU] & 0x1 ? 1 : 0, + keymap[VK_LMENU] & 0x80 ? 1 : 0, + keymap[VK_LMENU] & 0x1 ? 1 : 0, + keymap[VK_RMENU] & 0x80 ? 1 : 0, + keymap[VK_RMENU] & 0x1 ? 1 : 0); + stderr_out ("GetKeyboardState VK_CONTROL %d %d VK_LCONTROL %d %d VK_RCONTROL %d %d\n", + keymap[VK_CONTROL] & 0x80 ? 1 : 0, + keymap[VK_CONTROL] & 0x1 ? 1 : 0, + keymap[VK_LCONTROL] & 0x80 ? 1 : 0, + keymap[VK_LCONTROL] & 0x1 ? 1 : 0, + keymap[VK_RCONTROL] & 0x80 ? 1 : 0, + keymap[VK_RCONTROL] & 0x1 ? 1 : 0); + stderr_out ("GetKeyboardState VK_SHIFT %d %d VK_LSHIFT %d %d VK_RSHIFT %d %d\n", + keymap[VK_SHIFT] & 0x80 ? 1 : 0, + keymap[VK_SHIFT] & 0x1 ? 1 : 0, + keymap[VK_LSHIFT] & 0x80 ? 1 : 0, + keymap[VK_LSHIFT] & 0x1 ? 1 : 0, + keymap[VK_RSHIFT] & 0x80 ? 1 : 0, + keymap[VK_RSHIFT] & 0x1 ? 1 : 0); +} -#ifdef HAVE_SCROLLBARS - case WM_VSCROLL: - case WM_HSCROLL: +#endif + +/* try to debug the stuck-alt-key problem. + + #### this happens only inconsistently, and may only happen when using + StickyKeys in the Win2000 accessibility section of the control panel, + which is extremely broken for other reasons. */ + +static void +output_alt_keyboard_state (void) +{ + BYTE keymap[256]; + SHORT keystate[3]; + // SHORT asyncstate[3]; + + GetKeyboardState (keymap); + keystate[0] = GetKeyState (VK_MENU); + keystate[1] = GetKeyState (VK_LMENU); + keystate[2] = GetKeyState (VK_RMENU); + /* Doing this interferes with key processing. */ +/* asyncstate[0] = GetAsyncKeyState (VK_MENU); */ +/* asyncstate[1] = GetAsyncKeyState (VK_LMENU); */ +/* asyncstate[2] = GetAsyncKeyState (VK_RMENU); */ + + stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", + keymap[VK_MENU] & 0x80 ? 1 : 0, + keymap[VK_MENU] & 0x1 ? 1 : 0, + keymap[VK_LMENU] & 0x80 ? 1 : 0, + keymap[VK_LMENU] & 0x1 ? 1 : 0, + keymap[VK_RMENU] & 0x80 ? 1 : 0, + keymap[VK_RMENU] & 0x1 ? 1 : 0); + stderr_out ("GetKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", + keystate[0] & 0x8000 ? 1 : 0, + keystate[0] & 0x1 ? 1 : 0, + keystate[1] & 0x8000 ? 1 : 0, + keystate[1] & 0x1 ? 1 : 0, + keystate[2] & 0x8000 ? 1 : 0, + keystate[2] & 0x1 ? 1 : 0); +/* stderr_out ("GetAsyncKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", */ +/* asyncstate[0] & 0x8000 ? 1 : 0, */ +/* asyncstate[0] & 0x1 ? 1 : 0, */ +/* asyncstate[1] & 0x8000 ? 1 : 0, */ +/* asyncstate[1] & 0x1 ? 1 : 0, */ +/* asyncstate[2] & 0x8000 ? 1 : 0, */ +/* asyncstate[2] & 0x1 ? 1 : 0); */ +} + +#endif /* DEBUG_XEMACS */ + + +/* + * The windows procedure for the window class XEMACS_CLASS + */ +LRESULT WINAPI +mswindows_wnd_proc (HWND hwnd, UINT message_, WPARAM wParam, LPARAM lParam) +{ + /* Note: Remember to initialize emacs_event and event before use. + This code calls code that can GC. You must GCPRO before calling such code. */ + Lisp_Object emacs_event = Qnil; + Lisp_Object fobj = Qnil; + + Lisp_Event *event; + struct frame *frame; + struct mswindows_frame* msframe; + + /* Not perfect but avoids crashes. There is potential for wierd + behavior here. */ + if (gc_in_progress) + goto defproc; + + assert (!GetWindowLong (hwnd, GWL_USERDATA)); + switch (message_) { - /* Direction of scroll is determined by scrollbar instance. */ - int code = (int) LOWORD(wParam); - int pos = (short int) HIWORD(wParam); - HWND hwndScrollBar = (HWND) lParam; - struct gcpro gcpro1, gcpro2; - - mswindows_handle_scrollbar_event (hwndScrollBar, code, pos); - GCPRO2 (emacs_event, fobj); - if (UNBOUNDP(mswindows_pump_outstanding_events())) /* Can GC */ + case WM_DESTROYCLIPBOARD: + /* We own the clipboard and someone else wants it. Delete our + cached copy of the clipboard contents so we'll ask for it from + Windows again when someone does a paste, and destroy any memory + objects we hold on the clipboard that are not in the list of types + that Windows will delete itself. */ + mswindows_destroy_selection (QCLIPBOARD); + handle_selection_clear (QCLIPBOARD); + break; + + case WM_ERASEBKGND: + /* Erase background only during non-dynamic sizing */ + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + if (msframe->sizing && !mswindows_dynamic_frame_resize) + goto defproc; + return 1; + + case WM_CLOSE: + fobj = mswindows_find_frame (hwnd); + mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt)); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + + /* See Win95 comment under WM_KEYDOWN */ + { + BYTE keymap[256]; + int should_set_keymap = 0; + +#ifdef DEBUG_XEMACS + if (debug_mswindows_events) + { + stderr_out ("%s wparam=%d lparam=%d\n", + message_ == WM_KEYUP ? "WM_KEYUP" : "WM_SYSKEYUP", + wParam, (int)lParam); + output_alt_keyboard_state (); + } +#endif /* DEBUG_XEMACS */ + + mswindows_handle_sticky_modifiers (wParam, lParam, 0, 1); + if (wParam == VK_CONTROL) + { + GetKeyboardState (keymap); + keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80; + should_set_keymap = 1; + } + else if (wParam == VK_MENU) + { + GetKeyboardState (keymap); + keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80; + should_set_keymap = 1; + } + + if (should_set_keymap) + // && (message_ != WM_SYSKEYUP + // || NILP (Vmenu_accelerator_enabled))) + SetKeyboardState (keymap); + + } + + if (key_needs_default_processing_p (wParam)) + goto defproc; + else + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + + /* In some locales the right-hand Alt key is labelled AltGr. This key + * should produce alternative characters when combined with another key. + * eg on a German keyboard pressing AltGr+q should produce '@'. + * AltGr generates exactly the same keystrokes as LCtrl+RAlt. But if + * TranslateMessage() is called with *any* combination of Ctrl+Alt down, + * it translates as if AltGr were down. + * We get round this by removing all modifiers from the keymap before + * calling TranslateMessage() unless AltGr is *really* down. */ + { + BYTE keymap_trans[256]; + BYTE keymap_orig[256]; + BYTE keymap_sticky[256]; + int has_AltGr = mswindows_current_layout_has_AltGr (); + int mods = 0; + int extendedp = lParam & 0x1000000; + Lisp_Object keysym; + int sticky_changed; + +#ifdef DEBUG_XEMACS + if (debug_mswindows_events) + { + stderr_out ("%s wparam=%d lparam=%d\n", + message_ == WM_KEYDOWN ? "WM_KEYDOWN" : "WM_SYSKEYDOWN", + wParam, (int)lParam); + output_alt_keyboard_state (); + } +#endif /* DEBUG_XEMACS */ + + GetKeyboardState (keymap_orig); + frame = XFRAME (mswindows_find_frame (hwnd)); + if ((sticky_changed = + mswindows_handle_sticky_modifiers (wParam, lParam, 1, 1))) + { + GetKeyboardState (keymap_sticky); + if (keymap_sticky[VK_MENU] & 0x80) + { + message_ = WM_SYSKEYDOWN; + /* We have to set the "context bit" so that the + TranslateMessage() call below that generates the + SYSCHAR message does its thing; see the documentation + on WM_SYSKEYDOWN */ + lParam |= 1 << 29; + } + } + else + memcpy (keymap_sticky, keymap_orig, 256); + + mods = mswindows_modifier_state (keymap_sticky, (DWORD) -1, has_AltGr); + + /* Handle non-printables */ + if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods, + extendedp))) + { + mswindows_enqueue_keypress_event (hwnd, keysym, mods); + if (sticky_changed) + SetKeyboardState (keymap_orig); + } + else /* Normal keys & modifiers */ + { + Emchar quit_ch = + CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd))); + POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) }; + MSG msg, tranmsg; + int potential_accelerator = 0; + int got_accelerator = 0; + + msg.hwnd = hwnd; + msg.message = message_; + msg.wParam = wParam; + msg.lParam = lParam; + msg.time = GetMessageTime(); + msg.pt = pnt; + + /* GetKeyboardState() does not work as documented on Win95. We have + * to loosely track Left and Right modifiers on behalf of the OS, + * without screwing up Windows NT which tracks them properly. */ + if (wParam == VK_CONTROL) + { + keymap_orig[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80; + keymap_sticky[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80; + } + else if (wParam == VK_MENU) + { + keymap_orig[extendedp ? VK_RMENU : VK_LMENU] |= 0x80; + keymap_sticky[extendedp ? VK_RMENU : VK_LMENU] |= 0x80; + } + + if (!NILP (Vmenu_accelerator_enabled) && + !(mods & XEMACS_MOD_SHIFT) && message_ == WM_SYSKEYDOWN) + potential_accelerator = 1; + + /* Remove shift modifier from an ascii character */ + mods &= ~XEMACS_MOD_SHIFT; + + memcpy (keymap_trans, keymap_sticky, 256); + + /* Clear control and alt modifiers unless AltGr is pressed */ + keymap_trans[VK_RCONTROL] = 0; + keymap_trans[VK_LMENU] = 0; + if (!has_AltGr || !(keymap_trans[VK_LCONTROL] & 0x80) + || !(keymap_trans[VK_RMENU] & 0x80)) + { + keymap_trans[VK_LCONTROL] = 0; + keymap_trans[VK_CONTROL] = 0; + keymap_trans[VK_RMENU] = 0; + keymap_trans[VK_MENU] = 0; + } + SetKeyboardState (keymap_trans); + + /* Maybe generate some WM_[SYS]CHARs in the queue */ + TranslateMessage (&msg); + + while (PeekMessage (&tranmsg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE) + || PeekMessage (&tranmsg, hwnd, WM_SYSCHAR, WM_SYSCHAR, + PM_REMOVE)) + { + int mods1 = mods; + WPARAM ch = tranmsg.wParam; + + /* If a quit char with no modifiers other than control and + shift, then mark it with a fake modifier, which is removed + upon dequeueing the event */ + /* #### This might also not withstand localization, if + quit character is not a latin-1 symbol */ + if (((quit_ch < ' ' && (mods & XEMACS_MOD_CONTROL) + && quit_ch + 'a' - 1 == ch) + || (quit_ch >= ' ' && !(mods & XEMACS_MOD_CONTROL) + && quit_ch == ch)) + && ((mods & ~(XEMACS_MOD_CONTROL | XEMACS_MOD_SHIFT)) + == 0)) + { + mods1 |= FAKE_MOD_QUIT; + ++mswindows_quit_chars_count; + } + else if (potential_accelerator && !got_accelerator && + mswindows_char_is_accelerator (frame, ch)) + { + got_accelerator = 1; + break; + } + mswindows_enqueue_keypress_event (hwnd, make_char (ch), mods1); + } /* while */ + + /* This generates WM_SYSCHAR messages, which are interpreted + by DefWindowProc as the menu selections. */ + if (got_accelerator) + { + SetKeyboardState (keymap_sticky); + TranslateMessage (&msg); + SetKeyboardState (keymap_orig); + goto defproc; + } + + SetKeyboardState (keymap_orig); + } /* else */ + } + + if (key_needs_default_processing_p (wParam)) + goto defproc; + else + break; + + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + /* Real middle mouse button has nothing to do with emulated one: + if one wants to exercise fingers playing chords on the mouse, + he is allowed to do that! */ + mswindows_enqueue_mouse_button_event (hwnd, message_, + MAKEPOINTS (lParam), + wParam &~ MK_MBUTTON, + GetMessageTime()); + break; + + case WM_LBUTTONUP: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + msframe->last_click_time = GetMessageTime(); + + KillTimer (hwnd, BUTTON_2_TIMER_ID); + msframe->button2_need_lbutton = 0; + if (msframe->ignore_next_lbutton_up) { - /* Error during event pumping - cancel scroll */ - SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0); + msframe->ignore_next_lbutton_up = 0; } - UNGCPRO; - break; - } + else if (msframe->button2_is_down) + { + msframe->button2_is_down = 0; + msframe->ignore_next_rbutton_up = 1; + mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, + MAKEPOINTS (lParam), + wParam + &~ (MK_LBUTTON | MK_MBUTTON + | MK_RBUTTON), + GetMessageTime()); + } + else + { + if (msframe->button2_need_rbutton) + { + msframe->button2_need_rbutton = 0; + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, + MAKEPOINTS (lParam), + wParam &~ MK_LBUTTON, + GetMessageTime()); + } + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP, + MAKEPOINTS (lParam), + wParam &~ MK_LBUTTON, + GetMessageTime()); + } + break; + + case WM_RBUTTONUP: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + msframe->last_click_time = GetMessageTime(); + + KillTimer (hwnd, BUTTON_2_TIMER_ID); + msframe->button2_need_rbutton = 0; + if (msframe->ignore_next_rbutton_up) + { + msframe->ignore_next_rbutton_up = 0; + } + else if (msframe->button2_is_down) + { + msframe->button2_is_down = 0; + msframe->ignore_next_lbutton_up = 1; + mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, + MAKEPOINTS (lParam), + wParam + &~ (MK_LBUTTON | MK_MBUTTON + | MK_RBUTTON), + GetMessageTime()); + } + else + { + if (msframe->button2_need_lbutton) + { + msframe->button2_need_lbutton = 0; + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, + MAKEPOINTS (lParam), + wParam &~ MK_RBUTTON, + GetMessageTime()); + } + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP, + MAKEPOINTS (lParam), + wParam &~ MK_RBUTTON, + GetMessageTime()); + } + break; + + case WM_LBUTTONDOWN: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + + if (msframe->button2_need_lbutton) + { + KillTimer (hwnd, BUTTON_2_TIMER_ID); + msframe->button2_need_lbutton = 0; + msframe->button2_need_rbutton = 0; + if (mswindows_button2_near_enough (msframe->last_click_point, + MAKEPOINTS (lParam))) + { + mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, + MAKEPOINTS (lParam), + wParam + &~ (MK_LBUTTON | MK_MBUTTON + | MK_RBUTTON), + GetMessageTime()); + msframe->button2_is_down = 1; + } + else + { + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, + msframe->last_click_point, + msframe->last_click_mods + &~ MK_RBUTTON, + msframe->last_click_time); + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, + MAKEPOINTS (lParam), + wParam &~ MK_LBUTTON, + GetMessageTime()); + } + } + else + { + mswindows_set_chord_timer (hwnd); + msframe->button2_need_rbutton = 1; + msframe->last_click_point = MAKEPOINTS (lParam); + msframe->last_click_mods = wParam; + } + msframe->last_click_time = GetMessageTime(); + break; + + case WM_RBUTTONDOWN: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + + if (msframe->button2_need_rbutton) + { + KillTimer (hwnd, BUTTON_2_TIMER_ID); + msframe->button2_need_lbutton = 0; + msframe->button2_need_rbutton = 0; + if (mswindows_button2_near_enough (msframe->last_click_point, + MAKEPOINTS (lParam))) + { + mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, + MAKEPOINTS (lParam), + wParam + &~ (MK_LBUTTON | MK_MBUTTON + | MK_RBUTTON), + GetMessageTime()); + msframe->button2_is_down = 1; + } + else + { + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, + msframe->last_click_point, + msframe->last_click_mods + &~ MK_LBUTTON, + msframe->last_click_time); + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, + MAKEPOINTS (lParam), + wParam &~ MK_RBUTTON, + GetMessageTime()); + } + } + else + { + mswindows_set_chord_timer (hwnd); + msframe->button2_need_lbutton = 1; + msframe->last_click_point = MAKEPOINTS (lParam); + msframe->last_click_mods = wParam; + } + msframe->last_click_time = GetMessageTime(); + break; + + case WM_TIMER: + if (wParam == BUTTON_2_TIMER_ID) + { + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + KillTimer (hwnd, BUTTON_2_TIMER_ID); + + if (msframe->button2_need_lbutton) + { + msframe->button2_need_lbutton = 0; + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, + msframe->last_click_point, + msframe->last_click_mods + &~ MK_RBUTTON, + msframe->last_click_time); + } + else if (msframe->button2_need_rbutton) + { + msframe->button2_need_rbutton = 0; + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, + msframe->last_click_point, + msframe->last_click_mods + &~ MK_LBUTTON, + msframe->last_click_time); + } + } + else + assert ("Spurious timer fired" == 0); + break; + + case WM_MOUSEMOVE: + /* Optimization: don't report mouse movement while size is changing */ + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + if (!msframe->sizing) + { + /* When waiting for the second mouse button to finish + button2 emulation, and have moved too far, just pretend + as if timer has expired. This improves drag-select feedback */ + if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton) + && !mswindows_button2_near_enough (msframe->last_click_point, + MAKEPOINTS (lParam))) + { + KillTimer (hwnd, BUTTON_2_TIMER_ID); + SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0); + } + + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = mswindows_find_frame(hwnd); + event->timestamp = GetMessageTime(); + event->event_type = pointer_motion_event; + event->event.motion.x = MAKEPOINTS(lParam).x; + event->event.motion.y = MAKEPOINTS(lParam).y; + event->event.motion.modifiers = + mswindows_modifier_state (NULL, wParam, 0); + + mswindows_enqueue_dispatch_event (emacs_event); + } + break; + + case WM_CANCELMODE: + ReleaseCapture (); + /* Queue a `cancel-mode-internal' misc user event, so mouse + selection would be canceled if any */ + mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd), + Qcancel_mode_internal, Qnil); + break; + + case WM_NOTIFY: + { + LPNMHDR nmhdr = (LPNMHDR)lParam; + + if (nmhdr->code == TTN_NEEDTEXT) + { +#ifdef HAVE_TOOLBARS + LPTOOLTIPTEXT tttext = (LPTOOLTIPTEXT)lParam; + Lisp_Object btext; + + /* find out which toolbar */ + frame = XFRAME (mswindows_find_frame (hwnd)); + btext = mswindows_get_toolbar_button_text ( frame, + nmhdr->idFrom ); + + tttext->lpszText = NULL; + tttext->hinst = NULL; + + if (!NILP(btext)) + { + /* I think this is safe since the text will only go away + when the toolbar does...*/ + LISP_STRING_TO_EXTERNAL (btext, tttext->lpszText, Qnative); + } #endif + } + /* handle tree view callbacks */ + else if (nmhdr->code == TVN_SELCHANGED) + { + NM_TREEVIEW* ptree = (NM_TREEVIEW*)lParam; + frame = XFRAME (mswindows_find_frame (hwnd)); + mswindows_handle_gui_wm_command (frame, 0, ptree->itemNew.lParam); + } + /* handle tab control callbacks */ + else if (nmhdr->code == TCN_SELCHANGE) + { + TC_ITEM item; + int idx = SendMessage (nmhdr->hwndFrom, TCM_GETCURSEL, 0, 0); + frame = XFRAME (mswindows_find_frame (hwnd)); -#ifdef HAVE_MENUBARS - case WM_INITMENU: - if (UNBOUNDP (mswindows_handle_wm_initmenu ( - (HMENU) wParam, - XFRAME (mswindows_find_frame (hwnd))))) - SendMessage (hwnd, WM_CANCELMODE, 0, 0); - break; - - case WM_INITMENUPOPUP: - if (!HIWORD(lParam)) + item.mask = TCIF_PARAM; + SendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM)idx, + (LPARAM)&item); + + mswindows_handle_gui_wm_command (frame, 0, item.lParam); + } + } + break; + + case WM_PAINT: + /* hdc will be NULL unless this is a subwindow - in which case we + shouldn't have received a paint message for it here. */ + assert (wParam == 0); + + /* Can't queue a magic event because windows goes modal and sends paint + messages directly to the windows procedure when doing solid drags + and the message queue doesn't get processed. */ + mswindows_handle_paint (XFRAME (mswindows_find_frame (hwnd))); + break; + + case WM_SIZE: + /* We only care about this message if our size has really changed */ + if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED) + { + RECT rect; + int columns, rows; + + fobj = mswindows_find_frame (hwnd); + frame = XFRAME (fobj); + msframe = FRAME_MSWINDOWS_DATA (frame); + + /* We cannot handle frame map and unmap hooks right in + this routine, because these may throw. We queue + magic events to run these hooks instead - kkm */ + + if (wParam==SIZE_MINIMIZED) + { + /* Iconified */ + FRAME_VISIBLE_P (frame) = 0; + mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME); + } + else + { + GetClientRect(hwnd, &rect); + FRAME_PIXWIDTH(frame) = rect.right; + FRAME_PIXHEIGHT(frame) = rect.bottom; + + pixel_to_real_char_size (frame, rect.right, rect.bottom, + &FRAME_MSWINDOWS_CHARWIDTH (frame), + &FRAME_MSWINDOWS_CHARHEIGHT (frame)); + + pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows); + change_frame_size (frame, rows, columns, 1); + + /* If we are inside frame creation, we have to apply geometric + properties now. */ + if (FRAME_MSWINDOWS_TARGET_RECT (frame)) + { + /* Yes, we have to size again */ + mswindows_size_frame_internal ( frame, + FRAME_MSWINDOWS_TARGET_RECT + (frame)); + /* Reset so we do not get here again. The SetWindowPos call in + * mswindows_size_frame_internal can cause recursion here. */ + if (FRAME_MSWINDOWS_TARGET_RECT (frame)) + { + xfree (FRAME_MSWINDOWS_TARGET_RECT (frame)); + FRAME_MSWINDOWS_TARGET_RECT (frame) = 0; + } + } + else + { + if (!msframe->sizing && !FRAME_VISIBLE_P (frame)) + mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME); + FRAME_VISIBLE_P (frame) = 1; + + if (!msframe->sizing || mswindows_dynamic_frame_resize) + redisplay (); + } + } + } + break; + + case WM_DISPLAYCHANGE: { - if (UNBOUNDP (mswindows_handle_wm_initmenupopup ( - (HMENU) wParam, - XFRAME (mswindows_find_frame (hwnd))))) - SendMessage (hwnd, WM_CANCELMODE, 0, 0); + struct device *d; + DWORD message_tick = GetMessageTime (); + + fobj = mswindows_find_frame (hwnd); + frame = XFRAME (fobj); + d = XDEVICE (FRAME_DEVICE (frame)); + + /* Do this only once per message. XEmacs can receive this message + through as many frames as it currently has open. Message time + will be the same for all these messages. Despite extreme + efficiency, the code below has about one in 4 billion + probability that the HDC is not recreated, provided that + XEmacs is running sufficiently longer than 52 days. */ + if (DEVICE_MSWINDOWS_UPDATE_TICK(d) != message_tick) + { + DEVICE_MSWINDOWS_UPDATE_TICK(d) = message_tick; + DeleteDC (DEVICE_MSWINDOWS_HCDC(d)); + DEVICE_MSWINDOWS_HCDC(d) = CreateCompatibleDC (NULL); + } } - break; + break; -#endif /* HAVE_MENUBARS */ + /* Misc magic events which only require that the frame be identified */ + case WM_SETFOCUS: + case WM_KILLFOCUS: + mswindows_enqueue_magic_event (hwnd, message_); + break; - case WM_COMMAND: - { - WORD id = LOWORD (wParam); - HWND cid = (HWND)lParam; - frame = XFRAME (mswindows_find_frame (hwnd)); + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS *wp = (LPWINDOWPOS) lParam; + WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) }; + GetWindowPlacement(hwnd, &wpl); + + /* Only interested if size is changing and we're not being iconified */ + if (wpl.showCmd != SW_SHOWMINIMIZED + && wpl.showCmd != SW_SHOWMAXIMIZED + && !(wp->flags & SWP_NOSIZE)) + { + RECT ncsize = { 0, 0, 0, 0 }; + int pixwidth, pixheight; + AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE), + GetMenu(hwnd) != NULL, + GetWindowLong (hwnd, GWL_EXSTYLE)); + + round_size_to_real_char (XFRAME (mswindows_find_frame (hwnd)), + wp->cx - (ncsize.right - ncsize.left), + wp->cy - (ncsize.bottom - ncsize.top), + &pixwidth, &pixheight); + + /* Convert client sizes to window sizes */ + pixwidth += (ncsize.right - ncsize.left); + pixheight += (ncsize.bottom - ncsize.top); + + if (wpl.showCmd != SW_SHOWMAXIMIZED) + { + /* Adjust so that the bottom or right doesn't move if it's + * the top or left that's being changed */ + RECT rect; + GetWindowRect (hwnd, &rect); + + if (rect.left != wp->x) + wp->x += wp->cx - pixwidth; + if (rect.top != wp->y) + wp->y += wp->cy - pixheight; + } + + wp->cx = pixwidth; + wp->cy = pixheight; + } + /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts + window position if the user tries to track window too small */ + } + goto defproc; -#ifdef HAVE_TOOLBARS - if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id))) + case WM_ENTERSIZEMOVE: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + msframe->sizing = 1; + return 0; + + case WM_EXITSIZEMOVE: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + msframe->sizing = 0; + /* Queue noop event */ + mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); + return 0; + +#ifdef HAVE_SCROLLBARS + case WM_VSCROLL: + case WM_HSCROLL: + { + /* Direction of scroll is determined by scrollbar instance. */ + int code = (int) LOWORD(wParam); + int pos = (short int) HIWORD(wParam); + HWND hwndScrollBar = (HWND) lParam; + struct gcpro gcpro1, gcpro2; + + mswindows_handle_scrollbar_event (hwndScrollBar, code, pos); + GCPRO2 (emacs_event, fobj); + if (UNBOUNDP(mswindows_pump_outstanding_events())) /* Can GC */ + { + /* Error during event pumping - cancel scroll */ + SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0); + } + UNGCPRO; break; -#endif + } -#ifdef HAVE_MENUBARS - if (!NILP (mswindows_handle_wm_command (frame, id))) + case WM_MOUSEWHEEL: + { + int keys = LOWORD (wParam); /* Modifier key flags */ + int delta = (short) HIWORD (wParam); /* Wheel rotation amount */ + struct gcpro gcpro1, gcpro2; + + if (mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd), keys, delta)) + { + GCPRO2 (emacs_event, fobj); + mswindows_pump_outstanding_events (); /* Can GC */ + UNGCPRO; + } + else + goto defproc; break; + } #endif - /* Bite me - a spurious command. This cannot happen. */ - error ("XEMACS BUG: Cannot decode command message"); - } - break; +#ifdef HAVE_MENUBARS + case WM_INITMENU: + if (UNBOUNDP (mswindows_handle_wm_initmenu ( + (HMENU) wParam, + XFRAME (mswindows_find_frame (hwnd))))) + SendMessage (hwnd, WM_CANCELMODE, 0, 0); + break; -#ifdef HAVE_DRAGNDROP - case WM_DROPFILES: /* implementation ripped-off from event-Xt.c */ - { - UINT filecount, i, len; - POINT point; - char* filename; -#ifdef __CYGWIN32__ - char* fname; -#endif - Lisp_Object l_dndlist = Qnil, l_item = Qnil; - struct gcpro gcpro1, gcpro2, gcpro3; + case WM_INITMENUPOPUP: + if (!HIWORD(lParam)) + { + if (UNBOUNDP (mswindows_handle_wm_initmenupopup ( + (HMENU) wParam, + XFRAME (mswindows_find_frame (hwnd))))) + SendMessage (hwnd, WM_CANCELMODE, 0, 0); + } + break; + +#endif /* HAVE_MENUBARS */ + + case WM_COMMAND: + { + WORD id = LOWORD (wParam); + WORD nid = HIWORD (wParam); + HWND cid = (HWND)lParam; + frame = XFRAME (mswindows_find_frame (hwnd)); - emacs_event = Fmake_event (Qnil, Qnil); - event = XEVENT(emacs_event); +#ifdef HAVE_TOOLBARS + if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id))) + break; +#endif + /* widgets in a buffer only eval a callback for suitable events.*/ + switch (nid) + { + case BN_CLICKED: + case EN_CHANGE: + case CBN_EDITCHANGE: + case CBN_SELCHANGE: + if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id))) + return 0; + } + /* menubars always must come last since the hashtables do not + always exist*/ +#ifdef HAVE_MENUBARS + if (!NILP (mswindows_handle_wm_command (frame, id))) + break; +#endif - GCPRO3 (emacs_event, l_dndlist, l_item); + return DefWindowProc (hwnd, message_, wParam, lParam); + /* Bite me - a spurious command. This used to not be able to + happen but with the introduction of widgets its now + possible. */ + } + break; - if (!DragQueryPoint ((HANDLE) wParam, &point)) - point.x = point.y = -1; /* outside client area */ + case WM_CTLCOLORBTN: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORSTATIC: + case WM_CTLCOLORSCROLLBAR: + { + /* if we get an opportunity to paint a widget then do so if + there is an appropriate face */ + HWND crtlwnd = (HWND)lParam; + LONG ii = GetWindowLong (crtlwnd, GWL_USERDATA); + if (ii) + { + Lisp_Object image_instance; + VOID_TO_LISP (image_instance, ii); + if (IMAGE_INSTANCEP (image_instance) + && + IMAGE_INSTANCE_TYPE_P (image_instance, IMAGE_WIDGET)) + { + /* set colors for the buttons */ + HDC hdc = (HDC)wParam; + if (last_widget_brushed != ii) + { + if (widget_brush) + DeleteObject (widget_brush); + widget_brush = CreateSolidBrush + (COLOR_INSTANCE_MSWINDOWS_COLOR + (XCOLOR_INSTANCE + (FACE_BACKGROUND + (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), + XIMAGE_INSTANCE_FRAME (image_instance))))); + } + last_widget_brushed = ii; + SetTextColor + (hdc, + COLOR_INSTANCE_MSWINDOWS_COLOR + (XCOLOR_INSTANCE + (FACE_FOREGROUND + (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), + XIMAGE_INSTANCE_FRAME (image_instance))))); + SetBkMode (hdc, OPAQUE); + SetBkColor + (hdc, + COLOR_INSTANCE_MSWINDOWS_COLOR + (XCOLOR_INSTANCE + (FACE_BACKGROUND + (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), + XIMAGE_INSTANCE_FRAME (image_instance))))); + return (LRESULT)widget_brush; + } + } + } + goto defproc; - event->event_type = misc_user_event; - event->channel = mswindows_find_frame(hwnd); - event->timestamp = GetMessageTime(); - event->event.misc.button = 1; /* #### Should try harder */ - event->event.misc.modifiers = mswindows_modifier_state (NULL, 0); - event->event.misc.x = point.x; - event->event.misc.y = point.y; - event->event.misc.function = Qdragdrop_drop_dispatch; +#ifdef HAVE_DRAGNDROP + case WM_DROPFILES: /* implementation ripped-off from event-Xt.c */ + { + UINT filecount, i, len; + POINT point; + char* filename; + char* fname; + + Lisp_Object l_dndlist = Qnil, l_item = Qnil; + struct gcpro gcpro1, gcpro2, gcpro3; + + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + GCPRO3 (emacs_event, l_dndlist, l_item); + + if (!DragQueryPoint ((HDROP) wParam, &point)) + point.x = point.y = -1; /* outside client area */ + + event->event_type = misc_user_event; + event->channel = mswindows_find_frame(hwnd); + event->timestamp = GetMessageTime(); + event->event.misc.button = 1; /* #### Should try harder */ + event->event.misc.modifiers = mswindows_modifier_state (NULL, + (DWORD) -1, 0); + event->event.misc.x = point.x; + event->event.misc.y = point.y; + event->event.misc.function = Qdragdrop_drop_dispatch; + + filecount = DragQueryFile ((HDROP) wParam, 0xffffffff, NULL, 0); + for (i=0; i/ part and + * because they may contain reserved characters. But that's OK - + * they just need to be good enough to keep dragdrop.el happy. */ + fname = (char *)xmalloc (len+1); + DragQueryFile ((HANDLE) wParam, i, fname, len+1); + + /* May be a shell link aka "shortcut" - replace fname if so */ +#if !(defined(CYGWIN) || defined(MINGW)) + /* cygwin doesn't define this COM stuff */ + if (!stricmp (fname + strlen (fname) - 4, ".LNK")) + { + IShellLink* psl; + + if (CoCreateInstance (&CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, &IID_IShellLink, &psl) == S_OK) + { + IPersistFile* ppf; + + if (psl->lpVtbl->QueryInterface (psl, &IID_IPersistFile, + &ppf) == S_OK) + { + WORD wsz[MAX_PATH]; + WIN32_FIND_DATA wfd; + LPSTR resolved = (char *) xmalloc (MAX_PATH+1); + + MultiByteToWideChar (CP_ACP,0, fname, -1, wsz, MAX_PATH); + + if ((ppf->lpVtbl->Load (ppf, wsz, STGM_READ) == S_OK) && + (psl->lpVtbl->GetPath (psl, resolved, MAX_PATH, + &wfd, 0)==S_OK)) + { + xfree (fname); + fname = resolved; + len = strlen (fname); + } + + ppf->lpVtbl->Release (ppf); + } + + psl->lpVtbl->Release (psl); + } + } +#endif - filecount = DragQueryFile ((HANDLE) wParam, 0xffffffff, NULL, 0); - for (i=0; i/ part and - * because they may contain reserved characters. But that's OK. */ -#ifdef __CYGWIN32__ - fname = (char *)xmalloc (len+1); - DragQueryFile ((HANDLE) wParam, i, fname, len+1); - filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5); - strcpy (filename, "file:"); - cygwin32_win32_to_posix_path_list (fname, filename+5); - xfree (fname); +#ifdef CYGWIN + filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5); + strcpy (filename, "file:"); + cygwin32_win32_to_posix_path_list (fname, filename+5); #else - filename = (char *)xmalloc (len+6); - strcpy (filename, "file:"); - DragQueryFile ((HANDLE) wParam, i, filename+5, len+1); - dostounix_filename (filename+5); + filename = (char *)xmalloc (len+6); + strcat (strcpy (filename, "file:"), fname); + dostounix_filename (filename+5); #endif - l_item = make_string (filename, strlen (filename)); - l_dndlist = Fcons (l_item, l_dndlist); - xfree (filename); - } - DragFinish ((HANDLE) wParam); + xfree (fname); + l_item = make_string (filename, strlen (filename)); + l_dndlist = Fcons (l_item, l_dndlist); + xfree (filename); + } + DragFinish ((HDROP) wParam); - event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist); - mswindows_enqueue_dispatch_event (emacs_event); - UNGCPRO; - } - break; + event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist); + mswindows_enqueue_dispatch_event (emacs_event); + UNGCPRO; + } + break; #endif - defproc: - default: - return DefWindowProc (hwnd, message, wParam, lParam); - } + defproc: + default: + return DefWindowProc (hwnd, message_, wParam, lParam); + } return (0); } @@ -2177,8 +3015,10 @@ mswindows_current_layout_has_AltGr (void) time when a key typed at autorepeat rate of 30 cps! */ static HKL last_hkl = 0; static int last_hkl_has_AltGr; + HKL current_hkl = (HKL) -1; - HKL current_hkl = GetKeyboardLayout (0); + if (xGetKeyboardLayout) /* not in NT 3.5 */ + current_hkl = xGetKeyboardLayout (0); if (current_hkl != last_hkl) { TCHAR c; @@ -2197,29 +3037,46 @@ mswindows_current_layout_has_AltGr (void) /* Returns the state of the modifier keys in the format expected by the * Lisp_Event key_data, button_data and motion_data modifiers member */ -int mswindows_modifier_state (BYTE* keymap, int has_AltGr) +static int +mswindows_modifier_state (BYTE* keymap, DWORD fwKeys, int has_AltGr) { int mods = 0; + int keys_is_real = 0; + BYTE keymap2[256]; + + if (fwKeys == (DWORD) -1) + fwKeys = mswindows_last_mouse_button_state; + else + { + keys_is_real = 1; + mswindows_last_mouse_button_state = fwKeys; + } if (keymap == NULL) { - keymap = (BYTE*) alloca(256); + keymap = keymap2; GetKeyboardState (keymap); has_AltGr = mswindows_current_layout_has_AltGr (); } + /* #### should look at fwKeys for MK_CONTROL. I don't understand how + AltGr works. */ if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80)) { - mods |= (keymap [VK_LMENU] & 0x80) ? MOD_META : 0; - mods |= (keymap [VK_RCONTROL] & 0x80) ? MOD_CONTROL : 0; + mods |= (keymap [VK_LMENU] & 0x80) ? XEMACS_MOD_META : 0; + mods |= (keymap [VK_RCONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0; } else { - mods |= (keymap [VK_MENU] & 0x80) ? MOD_META : 0; - mods |= (keymap [VK_CONTROL] & 0x80) ? MOD_CONTROL : 0; + mods |= (keymap [VK_MENU] & 0x80) ? XEMACS_MOD_META : 0; + mods |= (keymap [VK_CONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0; } - mods |= (keymap [VK_SHIFT] & 0x80) ? MOD_SHIFT : 0; + mods |= (keys_is_real ? fwKeys & MK_SHIFT : (keymap [VK_SHIFT] & 0x80)) + ? XEMACS_MOD_SHIFT : 0; + mods |= fwKeys & MK_LBUTTON ? XEMACS_MOD_BUTTON1 : 0; + mods |= fwKeys & MK_MBUTTON ? XEMACS_MOD_BUTTON2 : 0; + mods |= fwKeys & MK_RBUTTON ? XEMACS_MOD_BUTTON3 : 0; return mods; } @@ -2228,68 +3085,102 @@ int mswindows_modifier_state (BYTE* keymap, int has_AltGr) * Translate a mswindows virtual key to a keysym. * Only returns non-Qnil for keys that don't generate WM_CHAR messages * or whose ASCII codes (like space) xemacs doesn't like. - * Virtual key values are defined in winresrc.h - * XXX I'm not sure that KEYSYM("name") is the best thing to use here. */ -Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods) +Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods, + int extendedp) { - switch (mswindows_key) - { - /* First the predefined ones */ - case VK_BACK: return QKbackspace; - case VK_TAB: return QKtab; - case '\n': return QKlinefeed; /* No VK_LINEFEED in winresrc.h */ - case VK_RETURN: return QKreturn; - case VK_ESCAPE: return QKescape; - case VK_SPACE: return QKspace; - case VK_DELETE: return QKdelete; - - /* The rest */ - case VK_CLEAR: return KEYSYM ("clear"); /* Should do ^L ? */ - case VK_PRIOR: return KEYSYM ("prior"); - case VK_NEXT: return KEYSYM ("next"); - case VK_END: return KEYSYM ("end"); - case VK_HOME: return KEYSYM ("home"); - case VK_LEFT: return KEYSYM ("left"); - case VK_UP: return KEYSYM ("up"); - case VK_RIGHT: return KEYSYM ("right"); - case VK_DOWN: return KEYSYM ("down"); - case VK_SELECT: return KEYSYM ("select"); - case VK_PRINT: return KEYSYM ("print"); - case VK_EXECUTE: return KEYSYM ("execute"); - case VK_SNAPSHOT: return KEYSYM ("print"); - case VK_INSERT: return KEYSYM ("insert"); - case VK_HELP: return KEYSYM ("help"); -#if 0 /* XXX What are these supposed to do? */ - case VK_LWIN return KEYSYM (""); - case VK_RWIN return KEYSYM (""); + if (extendedp) /* Keys not present on a 82 key keyboard */ + { + switch (mswindows_key) + { + case VK_CANCEL: return KEYSYM ("pause"); + case VK_RETURN: return KEYSYM ("kp-enter"); + case VK_PRIOR: return KEYSYM ("prior"); + case VK_NEXT: return KEYSYM ("next"); + case VK_END: return KEYSYM ("end"); + case VK_HOME: return KEYSYM ("home"); + case VK_LEFT: return KEYSYM ("left"); + case VK_UP: return KEYSYM ("up"); + case VK_RIGHT: return KEYSYM ("right"); + case VK_DOWN: return KEYSYM ("down"); + case VK_INSERT: return KEYSYM ("insert"); + case VK_DELETE: return QKdelete; +#if 0 /* FSF Emacs allows these to return configurable syms/mods */ + case VK_LWIN return KEYSYM (""); + case VK_RWIN return KEYSYM (""); #endif - case VK_APPS: return KEYSYM ("menu"); - case VK_F1: return KEYSYM ("f1"); - case VK_F2: return KEYSYM ("f2"); - case VK_F3: return KEYSYM ("f3"); - case VK_F4: return KEYSYM ("f4"); - case VK_F5: return KEYSYM ("f5"); - case VK_F6: return KEYSYM ("f6"); - case VK_F7: return KEYSYM ("f7"); - case VK_F8: return KEYSYM ("f8"); - case VK_F9: return KEYSYM ("f9"); - case VK_F10: return KEYSYM ("f10"); - case VK_F11: return KEYSYM ("f11"); - case VK_F12: return KEYSYM ("f12"); - case VK_F13: return KEYSYM ("f13"); - case VK_F14: return KEYSYM ("f14"); - case VK_F15: return KEYSYM ("f15"); - case VK_F16: return KEYSYM ("f16"); - case VK_F17: return KEYSYM ("f17"); - case VK_F18: return KEYSYM ("f18"); - case VK_F19: return KEYSYM ("f19"); - case VK_F20: return KEYSYM ("f20"); - case VK_F21: return KEYSYM ("f21"); - case VK_F22: return KEYSYM ("f22"); - case VK_F23: return KEYSYM ("f23"); - case VK_F24: return KEYSYM ("f24"); - } + case VK_APPS: return KEYSYM ("menu"); + } + } + else + { + switch (mswindows_key) + { + case VK_BACK: return QKbackspace; + case VK_TAB: return QKtab; + case '\n': return QKlinefeed; + case VK_CLEAR: return KEYSYM ("clear"); + case VK_RETURN: return QKreturn; + case VK_PAUSE: return KEYSYM ("pause"); + case VK_ESCAPE: return QKescape; + case VK_SPACE: return QKspace; + case VK_PRIOR: return KEYSYM ("kp-prior"); + case VK_NEXT: return KEYSYM ("kp-next"); + case VK_END: return KEYSYM ("kp-end"); + case VK_HOME: return KEYSYM ("kp-home"); + case VK_LEFT: return KEYSYM ("kp-left"); + case VK_UP: return KEYSYM ("kp-up"); + case VK_RIGHT: return KEYSYM ("kp-right"); + case VK_DOWN: return KEYSYM ("kp-down"); + case VK_SELECT: return KEYSYM ("select"); + case VK_PRINT: return KEYSYM ("print"); + case VK_EXECUTE: return KEYSYM ("execute"); + case VK_SNAPSHOT: return KEYSYM ("print"); + case VK_INSERT: return KEYSYM ("kp-insert"); + case VK_DELETE: return KEYSYM ("kp-delete"); + case VK_HELP: return KEYSYM ("help"); + case VK_NUMPAD0: return KEYSYM ("kp-0"); + case VK_NUMPAD1: return KEYSYM ("kp-1"); + case VK_NUMPAD2: return KEYSYM ("kp-2"); + case VK_NUMPAD3: return KEYSYM ("kp-3"); + case VK_NUMPAD4: return KEYSYM ("kp-4"); + case VK_NUMPAD5: return KEYSYM ("kp-5"); + case VK_NUMPAD6: return KEYSYM ("kp-6"); + case VK_NUMPAD7: return KEYSYM ("kp-7"); + case VK_NUMPAD8: return KEYSYM ("kp-8"); + case VK_NUMPAD9: return KEYSYM ("kp-9"); + case VK_MULTIPLY: return KEYSYM ("kp-multiply"); + case VK_ADD: return KEYSYM ("kp-add"); + case VK_SEPARATOR: return KEYSYM ("kp-separator"); + case VK_SUBTRACT: return KEYSYM ("kp-subtract"); + case VK_DECIMAL: return KEYSYM ("kp-decimal"); + case VK_DIVIDE: return KEYSYM ("kp-divide"); + case VK_F1: return KEYSYM ("f1"); + case VK_F2: return KEYSYM ("f2"); + case VK_F3: return KEYSYM ("f3"); + case VK_F4: return KEYSYM ("f4"); + case VK_F5: return KEYSYM ("f5"); + case VK_F6: return KEYSYM ("f6"); + case VK_F7: return KEYSYM ("f7"); + case VK_F8: return KEYSYM ("f8"); + case VK_F9: return KEYSYM ("f9"); + case VK_F10: return KEYSYM ("f10"); + case VK_F11: return KEYSYM ("f11"); + case VK_F12: return KEYSYM ("f12"); + case VK_F13: return KEYSYM ("f13"); + case VK_F14: return KEYSYM ("f14"); + case VK_F15: return KEYSYM ("f15"); + case VK_F16: return KEYSYM ("f16"); + case VK_F17: return KEYSYM ("f17"); + case VK_F18: return KEYSYM ("f18"); + case VK_F19: return KEYSYM ("f19"); + case VK_F20: return KEYSYM ("f20"); + case VK_F21: return KEYSYM ("f21"); + case VK_F22: return KEYSYM ("f22"); + case VK_F23: return KEYSYM ("f23"); + case VK_F24: return KEYSYM ("f24"); + } + } return Qnil; } @@ -2347,7 +3238,7 @@ emacs_mswindows_add_timeout (EMACS_TIME thyme) static void emacs_mswindows_remove_timeout (int id) { - struct Lisp_Event match_against; + Lisp_Event match_against; Lisp_Object emacs_event; if (KillTimer (NULL, id)) @@ -2383,13 +3274,13 @@ emacs_mswindows_event_pending_p (int user_p) * Return the next event */ static void -emacs_mswindows_next_event (struct Lisp_Event *emacs_event) +emacs_mswindows_next_event (Lisp_Event *emacs_event) { Lisp_Object event, event2; mswindows_need_event (1); - event = mswindows_dequeue_dispatch_event (!NILP(mswindows_u_dispatch_event_queue)); + event = mswindows_dequeue_dispatch_event (); XSETEVENT (event2, emacs_event); Fcopy_event (event, event2); Fdeallocate_event (event); @@ -2399,13 +3290,21 @@ emacs_mswindows_next_event (struct Lisp_Event *emacs_event) * Handle a magic event off the dispatch queue. */ static void -emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event) +emacs_mswindows_handle_magic_event (Lisp_Event *emacs_event) { switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)) { case XM_BUMPQUEUE: break; - + + case WM_PAINT: + { + struct frame *f = XFRAME (EVENT_CHANNEL (emacs_event)); + mswindows_handle_paint (f); + (FRAME_MSWINDOWS_DATA (f))->paint_pending = 0; + } + break; + case WM_SETFOCUS: case WM_KILLFOCUS: { @@ -2413,18 +3312,19 @@ emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event) struct frame *f = XFRAME (frame); int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS); Lisp_Object conser; + struct gcpro gcpro1; - /* struct gcpro gcpro1; */ - - /* Clear sticky modifiers here (if we had any) */ + /* On focus change, clear all memory of sticky modifiers + to avoid non-intuitive behavior. */ + clear_sticky_modifiers (); conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil)); - /* GCPRO1 (conser); XXX Not necessary? */ + GCPRO1 (conser); emacs_handle_focus_change_preliminary (conser); /* Under X the stuff up to here is done in the X event handler. I Don't know why */ emacs_handle_focus_change_final (conser); - /* UNGCPRO; */ + UNGCPRO; } break; @@ -2433,13 +3333,13 @@ emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event) case XM_UNMAPFRAME: { Lisp_Object frame = EVENT_CHANNEL (emacs_event); - va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) + va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == XM_MAPFRAME ? - Qmap_frame_hook : Qunmap_frame_hook, + Qmap_frame_hook : Qunmap_frame_hook, 1, frame); } break; - + /* #### What about Enter & Leave */ #if 0 va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook : @@ -2451,8 +3351,9 @@ emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event) } } +#ifndef HAVE_MSG_SELECT static HANDLE -get_process_input_waitable (struct Lisp_Process *process) +get_process_input_waitable (Lisp_Process *process) { Lisp_Object instr, outstr, p; XSETPROCESS (p, process); @@ -2463,12 +3364,12 @@ get_process_input_waitable (struct Lisp_Process *process) ? get_winsock_stream_waitable (XLSTREAM (instr)) : get_ntpipe_input_stream_waitable (XLSTREAM (instr))); #else - return get_ntpipe_input_stream_waitable (XLSTREAM (instr)); + return get_ntpipe_input_stream_waitable (XLSTREAM (instr)); #endif } static void -emacs_mswindows_select_process (struct Lisp_Process *process) +emacs_mswindows_select_process (Lisp_Process *process) { HANDLE hev = get_process_input_waitable (process); @@ -2493,22 +3394,35 @@ emacs_mswindows_select_process (struct Lisp_Process *process) } static void -emacs_mswindows_unselect_process (struct Lisp_Process *process) +emacs_mswindows_unselect_process (Lisp_Process *process) { /* Process handle is removed in the event loop as soon as it is signaled, so don't bother here about it */ HANDLE hev = get_process_input_waitable (process); remove_waitable_handle (hev); } +#endif /* HAVE_MSG_SELECT */ static void emacs_mswindows_select_console (struct console *con) { +#ifdef HAVE_MSG_SELECT + if (CONSOLE_MSWINDOWS_P (con)) + return; /* mswindows consoles are automatically selected */ + + event_stream_unixoid_select_console (con); +#endif } static void emacs_mswindows_unselect_console (struct console *con) { +#ifdef HAVE_MSG_SELECT + if (CONSOLE_MSWINDOWS_P (con)) + return; /* mswindows consoles are automatically selected */ + + event_stream_unixoid_unselect_console (con); +#endif } static void @@ -2519,27 +3433,32 @@ emacs_mswindows_quit_p (void) if (mswindows_in_modal_loop) return; - /* Drain windows queue. This sets up number of quit - characters in in the queue */ + /* Drain windows queue. This sets up number of quit characters in + the queue */ mswindows_drain_windows_queue (); if (mswindows_quit_chars_count > 0) { /* Yes there's a hidden one... Throw it away */ - struct Lisp_Event match_against; + Lisp_Event match_against; Lisp_Object emacs_event; + int critical_p = 0; match_against.event_type = key_press_event; match_against.event.key.modifiers = FAKE_MOD_QUIT; - emacs_event = mswindows_cancel_dispatch_event (&match_against); - assert (!NILP (emacs_event)); + while (mswindows_quit_chars_count-- > 0) + { + emacs_event = mswindows_cancel_dispatch_event (&match_against); + assert (!NILP (emacs_event)); - Vquit_flag = (XEVENT(emacs_event)->event.key.modifiers & MOD_SHIFT - ? Qcritical : Qt); + if (XEVENT(emacs_event)->event.key.modifiers & XEMACS_MOD_SHIFT) + critical_p = 1; - Fdeallocate_event(emacs_event); - --mswindows_quit_chars_count; + Fdeallocate_event(emacs_event); + } + + Vquit_flag = critical_p ? Qcritical : Qt; } } @@ -2617,7 +3536,7 @@ emacs_mswindows_create_stream_pair (void* inhandle, void* outhandle, USID emacs_mswindows_delete_stream_pair (Lisp_Object instream, - Lisp_Object outstream) + Lisp_Object outstream) { /* Oh nothing special here for Win32 at all */ #if defined (HAVE_UNIX_PROCESSES) @@ -2646,12 +3565,18 @@ emacs_mswindows_delete_stream_pair (Lisp_Object instream, : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream)))); } +static int +emacs_mswindows_current_event_timestamp (struct console *c) +{ + return GetTickCount (); +} + #ifndef HAVE_X_WINDOWS /* This is called from GC when a process object is about to be freed. If we've still got pointers to it in this file, we're gonna lose hard. */ void -debug_process_finalization (struct Lisp_Process *p) +debug_process_finalization (Lisp_Process *p) { #if 0 /* #### */ Lisp_Object instr, outstr; @@ -2669,26 +3594,17 @@ debug_process_finalization (struct Lisp_Process *p) /************************************************************************/ /* initialization */ /************************************************************************/ - + void -vars_of_event_mswindows (void) +reinit_vars_of_event_mswindows (void) { - mswindows_u_dispatch_event_queue = Qnil; - staticpro (&mswindows_u_dispatch_event_queue); - mswindows_u_dispatch_event_queue_tail = Qnil; - - mswindows_s_dispatch_event_queue = Qnil; - staticpro (&mswindows_s_dispatch_event_queue); - mswindows_s_dispatch_event_queue_tail = Qnil; - - mswindows_error_caught_in_modal_loop = Qnil; - staticpro (&mswindows_error_caught_in_modal_loop); mswindows_in_modal_loop = 0; mswindows_pending_timers_count = 0; mswindows_event_stream = xnew (struct event_stream); mswindows_event_stream->event_pending_p = emacs_mswindows_event_pending_p; + mswindows_event_stream->force_event_pending = 0; mswindows_event_stream->next_event_cb = emacs_mswindows_next_event; mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event; mswindows_event_stream->add_timeout_cb = emacs_mswindows_add_timeout; @@ -2697,10 +3613,10 @@ vars_of_event_mswindows (void) mswindows_event_stream->select_console_cb = emacs_mswindows_select_console; mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console; #ifdef HAVE_MSG_SELECT - mswindows_event_stream->select_process_cb = - (void (*)(struct Lisp_Process*))event_stream_unixoid_select_process; - mswindows_event_stream->unselect_process_cb = - (void (*)(struct Lisp_Process*))event_stream_unixoid_unselect_process; + mswindows_event_stream->select_process_cb = + (void (*)(Lisp_Process*))event_stream_unixoid_select_process; + mswindows_event_stream->unselect_process_cb = + (void (*)(Lisp_Process*))event_stream_unixoid_unselect_process; mswindows_event_stream->create_stream_pair_cb = event_stream_unixoid_create_stream_pair; mswindows_event_stream->delete_stream_pair_cb = event_stream_unixoid_delete_stream_pair; #else @@ -2709,8 +3625,52 @@ vars_of_event_mswindows (void) mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair; mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair; #endif + mswindows_event_stream->current_event_timestamp_cb = + emacs_mswindows_current_event_timestamp; +} + +void +vars_of_event_mswindows (void) +{ + reinit_vars_of_event_mswindows (); + + mswindows_u_dispatch_event_queue = Qnil; + staticpro (&mswindows_u_dispatch_event_queue); + mswindows_u_dispatch_event_queue_tail = Qnil; + dump_add_root_object (&mswindows_u_dispatch_event_queue_tail); + + mswindows_s_dispatch_event_queue = Qnil; + staticpro (&mswindows_s_dispatch_event_queue); + mswindows_s_dispatch_event_queue_tail = Qnil; + dump_add_root_object (&mswindows_s_dispatch_event_queue_tail); + + mswindows_error_caught_in_modal_loop = Qnil; + staticpro (&mswindows_error_caught_in_modal_loop); + + +#ifdef DEBUG_XEMACS + DEFVAR_INT ("debug-mswindows-events", &debug_mswindows_events /* +If non-zero, display debug information about Windows events that XEmacs sees. +Information is displayed in a console window. Currently defined values are: + +1 == non-verbose output +2 == verbose output + +#### Unfortunately, not yet implemented. +*/ ); + debug_mswindows_events = 0; +#endif + + DEFVAR_BOOL ("mswindows-alt-by-itself-activates-menu", + &mswindows_alt_by_itself_activates_menu /* +*Controls whether pressing and releasing the Alt key activates the menubar. +This applies only if no intervening key was pressed. See also +`menu-accelerator-enabled', which is probably the behavior you actually want. +Default is t. +*/ ); - DEFVAR_BOOL ("mswindows-dynamic-frame-resize", &mswindows_dynamic_frame_resize /* + DEFVAR_BOOL ("mswindows-dynamic-frame-resize", + &mswindows_dynamic_frame_resize /* *Controls redrawing frame contents during mouse-drag or keyboard resize operation. When non-nil, the frame is redrawn while being resized. When nil, frame is not redrawn, and exposed areas are filled with default @@ -2720,8 +3680,8 @@ settings. Default is t on fast machines, nil on slow. */ ); -/* The description copied verbatim from nt-emacs. (C) Geoff Voelker */ - DEFVAR_INT ("mswindows-mouse-button-tolerance", &mswindows_mouse_button_tolerance /* + DEFVAR_INT ("mswindows-mouse-button-tolerance", + &mswindows_mouse_button_tolerance /* *Analogue of double click interval for faking middle mouse events. The value is the minimum time in milliseconds that must elapse between left/right button down events before they are considered distinct events. @@ -2730,22 +3690,23 @@ button down event is generated instead. If negative or zero, currently set system default is used instead. */ ); -/* The description copied verbatim from nt-emacs. (C) Geoff Voelker */ DEFVAR_INT ("mswindows-num-mouse-buttons", &mswindows_num_mouse_buttons /* Number of physical mouse buttons. */ ); - DEFVAR_INT ("mswindows-mouse-button-max-skew-x", &mswindows_mouse_button_max_skew_x /* + DEFVAR_INT ("mswindows-mouse-button-max-skew-x", + &mswindows_mouse_button_max_skew_x /* *Maximum horizontal distance in pixels between points in which left and -right button clicks occured for them to be translated into single +right button clicks occurred for them to be translated into single middle button event. Clicks must occur in time not longer than defined by the variable `mswindows-mouse-button-tolerance'. If negative or zero, currently set system default is used instead. */ ); - DEFVAR_INT ("mswindows-mouse-button-max-skew-y", &mswindows_mouse_button_max_skew_y /* + DEFVAR_INT ("mswindows-mouse-button-max-skew-y", + &mswindows_mouse_button_max_skew_y /* *Maximum vertical distance in pixels between points in which left and -right button clicks occured for them to be translated into single +right button clicks occurred for them to be translated into single middle button event. Clicks must occur in time not longer than defined by the variable `mswindows-mouse-button-tolerance'. If negative or zero, currently set system default is used instead. @@ -2754,6 +3715,7 @@ If negative or zero, currently set system default is used instead. mswindows_mouse_button_max_skew_x = 0; mswindows_mouse_button_max_skew_y = 0; mswindows_mouse_button_tolerance = 0; + mswindows_alt_by_itself_activates_menu = 1; } void @@ -2778,9 +3740,7 @@ init_event_mswindows_late (void) windows_fd = open("/dev/windows", O_RDONLY | O_NONBLOCK, 0); assert (windows_fd>=0); FD_SET (windows_fd, &input_wait_mask); - /* for some reason I get blocks on the signal event pipe, which is - bad... - signal_event_pipe_initialized = 0; */ + FD_ZERO(&zero_mask); #endif event_stream = mswindows_event_stream;