Mercurial > hg > xemacs-beta
diff src/event-msw.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | a5df635868b2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/event-msw.c Mon Aug 13 11:28:15 2007 +0200 @@ -0,0 +1,3086 @@ +/* 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) 1997 Jonathan Harris. + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +/* Authorship: + + 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 <config.h> +#include "lisp.h" + +#include "console-msw.h" + +#ifdef HAVE_SCROLLBARS +# include "scrollbar-msw.h" +#endif + +#ifdef HAVE_MENUBARS +# include "menubar-msw.h" +#endif + +#ifdef HAVE_DRAGNDROP +# include "dragdrop.h" +#endif + +#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 "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" +#include "console-tty.h" +#elif defined(__CYGWIN32__) +typedef unsigned int SOCKET; +#endif +#include <io.h> +#include <errno.h> + +#if defined (__CYGWIN32__) && !defined (CYGWIN_VERSION_DLL_MAJOR) +typedef NMHDR *LPNMHDR; +#endif + +#ifdef HAVE_MENUBARS +#define ADJR_MENUFLAG TRUE +#else +#define ADJR_MENUFLAG FALSE +#endif + +/* Fake key modifier which is attached to a quit char event. + Removed upon dequeueing an event */ +#define FAKE_MOD_QUIT 0x80 + +/* 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); +extern Lisp_Object +mswindows_handle_gui_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, + int extendedp); +static int mswindows_modifier_state (BYTE* keymap, 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 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 + +/* + * Two separate queues, for efficiency, one (_u_) for user events, and + * another (_s_) for non-user ones. We always return events out of the + * first one until it is empty and only then proceed with the second + * one. + */ +static Lisp_Object mswindows_u_dispatch_event_queue, mswindows_u_dispatch_event_queue_tail; +static Lisp_Object mswindows_s_dispatch_event_queue, mswindows_s_dispatch_event_queue_tail; + +/* 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() + Decremented in mswindows_dequeue_dispatch_event() */ +int mswindows_quit_chars_count = 0; + +/* These are Lisp integers; see DEFVARS in this file for description. */ +int mswindows_dynamic_frame_resize; +int mswindows_meta_activates_menu; +int mswindows_num_mouse_buttons; +int mswindows_mouse_button_max_skew_x; +int mswindows_mouse_button_max_skew_y; +int mswindows_mouse_button_tolerance; + +/* This is the event signaled by the event pump. + See mswindows_pump_outstanding_events for comments */ +static Lisp_Object mswindows_error_caught_in_modal_loop; +static int mswindows_in_modal_loop; + +/* Count of wound timers */ +static int mswindows_pending_timers_count; + +/************************************************************************/ +/* Pipe instream - reads process output */ +/************************************************************************/ + +#define PIPE_READ_DELAY 20 + +#define HANDLE_TO_USID(h) ((USID)(h)) + +#define NTPIPE_SLURP_STREAM_DATA(stream) \ + LSTREAM_TYPE_DATA (stream, ntpipe_slurp) + +/* 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 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 + shell. In this case, we abandon the thread, and let it live until + all such processes exit. While struct ntpipe_slurp_stream is + deallocated in this case, ntpipe_slurp_stream_shared_data are not. */ + +struct ntpipe_slurp_stream_shared_data +{ + HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */ + /* This is a manual-reset object. */ + HANDLE hev_caller; /* Caller blocks on this, and we signal it */ + /* This is a manual-reset object. */ + HANDLE hev_unsleep; /* Pipe read delay is canceled if this is set */ + /* 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 */ + BOOL error_p : 1; /* Read error other than EOF/broken pipe */ + BOOL inuse_p : 1; /* this structure is in use */ + LONG lock_count; /* Client count of this struct, 0=safe to free */ + BYTE onebyte; /* One byte buffer read by thread */ +}; + +#define MAX_SLURP_STREAMS 32 +struct ntpipe_slurp_stream_shared_data +shared_data_block[MAX_SLURP_STREAMS]={{0}}; + +struct ntpipe_slurp_stream +{ + LPARAM user_data; /* Any user data stored in the stream object */ + struct ntpipe_slurp_stream_shared_data* thread_data; +}; + +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 data structure */ +static void +slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data* s) +{ + if (InterlockedDecrement (&s->lock_count) == 0) + { + /* Destroy events */ + CloseHandle (s->hev_thread); + CloseHandle (s->hev_caller); + CloseHandle (s->hev_unsleep); + s->inuse_p = 0; + } +} + +static struct ntpipe_slurp_stream_shared_data* +slurper_allocate_shared_data() +{ + int i=0; + for (i=0; i<MAX_SLURP_STREAMS; i++) + { + if (!shared_data_block[i].inuse_p) + { + shared_data_block[i].inuse_p=1; + return &shared_data_block[i]; + } + } + return (struct ntpipe_slurp_stream_shared_data*)0; +} + +static DWORD WINAPI +slurp_thread (LPVOID vparam) +{ + struct ntpipe_slurp_stream_shared_data *s = + (struct ntpipe_slurp_stream_shared_data*)vparam; + + for (;;) + { + /* Read one byte from the pipe */ + DWORD actually_read; + if (!ReadFile (s->hpipe, &s->onebyte, 1, &actually_read, NULL)) + { + DWORD err = GetLastError (); + if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA) + s->eof_p = TRUE; + else + s->error_p = TRUE; + } + else if (actually_read == 0) + s->eof_p = TRUE; + + /* We must terminate on an error or eof */ + if (s->eof_p || s->error_p) + InterlockedIncrement (&s->die_p); + + /* Before we notify caller, we unsignal our event. */ + ResetEvent (s->hev_thread); + + /* Now we got something to notify caller, either a byte or an + error/eof indication. Before we do, allow internal pipe + buffer to accumulate little bit more data. + Reader function pulses this event before waiting for + a character, to avoid pipe delay, and to get the byte + immediately. */ + if (!s->die_p) + WaitForSingleObject (s->hev_unsleep, PIPE_READ_DELAY); + + /* Either make event loop generate a process event, or + inblock reader */ + SetEvent (s->hev_caller); + + /* Cleanup and exit if we're shot off */ + if (s->die_p) + break; + + /* Block until the client finishes with retrieving the rest of + pipe data */ + WaitForSingleObject (s->hev_thread, INFINITE); + } + + slurper_free_shared_data_maybe (s); + + return 0; +} + +static Lisp_Object +make_ntpipe_input_stream (HANDLE hpipe, LPARAM param) +{ + Lisp_Object obj; + Lstream *lstr = Lstream_new (lstream_ntpipe_slurp, "r"); + struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA (lstr); + DWORD thread_id_unused; + HANDLE hthread; + + /* We deal only with pipes, for we're using PeekNamedPipe api */ + assert (GetFileType (hpipe) == FILE_TYPE_PIPE); + + s->thread_data = slurper_allocate_shared_data(); + + /* Create reader thread. This could fail, so do not create events + until thread is created */ + hthread = CreateThread (NULL, 0, slurp_thread, (LPVOID)s->thread_data, + CREATE_SUSPENDED, &thread_id_unused); + if (hthread == NULL) + { + Lstream_delete (lstr); + s->thread_data->inuse_p=0; + return Qnil; + } + + /* Shared data are initially owned by both main and slurper + threads. */ + s->thread_data->lock_count = 2; + s->thread_data->die_p = 0; + s->thread_data->eof_p = FALSE; + s->thread_data->error_p = FALSE; + s->thread_data->hpipe = hpipe; + s->user_data = param; + + /* hev_thread is a manual-reset event, initially signaled */ + s->thread_data->hev_thread = CreateEvent (NULL, TRUE, TRUE, NULL); + /* hev_caller is a manual-reset event, initially nonsignaled */ + s->thread_data->hev_caller = CreateEvent (NULL, TRUE, FALSE, NULL); + /* hev_unsleep is a manual-reset event, initially nonsignaled */ + s->thread_data->hev_unsleep = CreateEvent (NULL, TRUE, FALSE, NULL); + + /* Now let it go */ + ResumeThread (hthread); + CloseHandle (hthread); + + lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE; + XSETLSTREAM (obj, lstr); + return obj; +} + +static LPARAM +get_ntpipe_input_stream_param (Lstream *stream) +{ + struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream); + return s->user_data; +} + +static HANDLE +get_ntpipe_input_stream_waitable (Lstream *stream) +{ + struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream); + return s->thread_data->hev_caller; +} + +static ssize_t +ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size) +{ + /* This function must be called from the main thread only */ + struct ntpipe_slurp_stream_shared_data* s = + NTPIPE_SLURP_STREAM_DATA(stream)->thread_data; + + if (!s->die_p) + { + DWORD wait_result; + /* 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); + + /* Revert to the normal sleep behavior. */ + ResetEvent (s->hev_unsleep); + + /* If there's no byte buffered yet, give up */ + if (wait_result == WAIT_TIMEOUT) + { + errno = EAGAIN; + return -1; + } + } + + /* Reset caller unlock event now, as we've handled the pending + process output event */ + ResetEvent (s->hev_caller); + + /* It is now safe to do anything with contents of S, except for + changing s->die_p, which still should be interlocked */ + + if (s->eof_p) + return 0; + if (s->error_p || s->die_p) + return -1; + + /* Ok, there were no error neither eof - we've got a byte from the + pipe */ + *(data++) = s->onebyte; + --size; + + { + DWORD bytes_read = 0; + if (size > 0) + { + DWORD bytes_available; + + /* If the api call fails, return at least one byte already + read. ReadFile in thread will return error */ + if (PeekNamedPipe (s->hpipe, NULL, 0, NULL, &bytes_available, NULL)) + { + + /* Fetch available bytes. The same consideration applies, + so do not check for errors. ReadFile in the thread will + fail if the next call fails. */ + if (bytes_available) + 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; + } + } + return 0; +} + +static int +ntpipe_slurp_closer (Lstream *stream) +{ + /* This function must be called from the main thread only */ + struct ntpipe_slurp_stream_shared_data* s = + NTPIPE_SLURP_STREAM_DATA(stream)->thread_data; + + /* Force thread to stop */ + InterlockedIncrement (&s->die_p); + + /* Set events which could possibly block slurper. Let it finish soon + or later. */ + SetEvent (s->hev_unsleep); + SetEvent (s->hev_thread); + + /* Unlock and maybe free shared data */ + slurper_free_shared_data_maybe (s); + + return 0; +} + +static void +init_slurp_stream (void) +{ + LSTREAM_HAS_METHOD (ntpipe_slurp, reader); + LSTREAM_HAS_METHOD (ntpipe_slurp, closer); +} + +/************************************************************************/ +/* Pipe outstream - writes process input */ +/************************************************************************/ + +#define NTPIPE_SHOVE_STREAM_DATA(stream) \ + LSTREAM_TYPE_DATA (stream, ntpipe_shove) + +#define MAX_SHOVE_BUFFER_SIZE 128 + +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. */ + HANDLE hpipe; /* Pipe write end handle. */ + HANDLE hthread; /* Reader thread handle. */ + char buffer[MAX_SHOVE_BUFFER_SIZE]; /* Buffer being written */ + DWORD size; /* Number of bytes to write */ + LONG die_p; /* Thread must exit ASAP if non-zero */ + LONG idle_p; /* Non-zero if thread is waiting for job */ + BOOL error_p : 1; /* Read error other than EOF/broken pipe */ + BOOL blocking_p : 1;/* Last write attempt would cause blocking */ +}; + +DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-output", lstream_ntpipe_shove, + sizeof (struct ntpipe_shove_stream)); + +#ifndef HAVE_MSG_SELECT +static DWORD WINAPI +shove_thread (LPVOID vparam) +{ + struct ntpipe_shove_stream *s = (struct ntpipe_shove_stream*) vparam; + + for (;;) + { + 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) + { + s->error_p = TRUE; + InterlockedIncrement (&s->die_p); + } + + if (s->die_p) + break; + } + + return 0; +} + +static Lisp_Object +make_ntpipe_output_stream (HANDLE hpipe, LPARAM param) +{ + Lisp_Object obj; + Lstream *lstr = Lstream_new (lstream_ntpipe_shove, "w"); + struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA (lstr); + DWORD thread_id_unused; + + s->die_p = 0; + s->error_p = FALSE; + s->hpipe = hpipe; + s->user_data = param; + + /* 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); + if (s->hthread == NULL) + { + Lstream_delete (lstr); + return Qnil; + } + + /* hev_thread is an auto-reset event, initially nonsignaled */ + s->hev_thread = CreateEvent (NULL, FALSE, FALSE, NULL); + + /* Now let it go */ + ResumeThread (s->hthread); + + lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE; + XSETLSTREAM (obj, lstr); + return obj; +} + +static LPARAM +get_ntpipe_output_stream_param (Lstream *stream) +{ + struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); + return s->user_data; +} +#endif + +static ssize_t +ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size) +{ + struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); + + if (s->error_p) + return -1; + + s->blocking_p = !s->idle_p; + if (s->blocking_p) + return 0; + + if (size>MAX_SHOVE_BUFFER_SIZE) + return 0; + + memcpy (s->buffer, data, size); + s->size = size; + + /* Start output */ + InterlockedDecrement (&s->idle_p); + SetEvent (s->hev_thread); + return size; +} + +static int +ntpipe_shove_was_blocked_p (Lstream *stream) +{ + struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); + return s->blocking_p; +} + +static int +ntpipe_shove_closer (Lstream *stream) +{ + struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); + + /* Force thread stop */ + InterlockedIncrement (&s->die_p); + + /* Close pipe handle, possibly breaking it */ + CloseHandle (s->hpipe); + + /* Thread will end upon unblocking */ + SetEvent (s->hev_thread); + + /* Wait while thread terminates */ + WaitForSingleObject (s->hthread, INFINITE); + CloseHandle (s->hthread); + + /* Destroy the event */ + CloseHandle (s->hev_thread); + + return 0; +} + +static void +init_shove_stream (void) +{ + LSTREAM_HAS_METHOD (ntpipe_shove, writer); + LSTREAM_HAS_METHOD (ntpipe_shove, was_blocked_p); + LSTREAM_HAS_METHOD (ntpipe_shove, closer); +} + +/************************************************************************/ +/* Winsock I/O stream */ +/************************************************************************/ +#if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT) + +#define WINSOCK_READ_BUFFER_SIZE 1024 + +struct winsock_stream +{ + LPARAM user_data; /* Any user data stored in the stream object */ + 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; /* 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 */ + unsigned int blocking_p :1; /* Last write attempt would block */ +}; + +#define WINSOCK_STREAM_DATA(stream) LSTREAM_TYPE_DATA (stream, winsock) + +DEFINE_LSTREAM_IMPLEMENTATION ("winsock", lstream_winsock, + sizeof (struct winsock_stream)); + +static void +winsock_initiate_read (struct winsock_stream *str) +{ + ResetEvent (str->ov.hEvent); + str->bufpos = 0; + + if (!ReadFile ((HANDLE)str->s, str->buffer, WINSOCK_READ_BUFFER_SIZE, + &str->bufsize, &str->ov)) + { + if (GetLastError () == ERROR_IO_PENDING) + str->pending_p = 1; + else if (GetLastError () == ERROR_HANDLE_EOF) + str->eof_p = 1; + else + str->error_p = 1; + } + else if (str->bufsize == 0) + str->eof_p = 1; +} + +static ssize_t +winsock_reader (Lstream *stream, unsigned char *data, size_t size) +{ + struct winsock_stream *str = WINSOCK_STREAM_DATA (stream); + + /* If the current operation is not yet complete, there's nothing to + give back */ + if (str->pending_p) + { + if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT) + { + errno = EAGAIN; + return -1; + } + else + { + if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &str->bufsize, TRUE)) + { + if (GetLastError() == ERROR_HANDLE_EOF) + str->bufsize = 0; + else + str->error_p = 1; + } + if (str->bufsize == 0) + str->eof_p = 1; + str->pending_p = 0; + } + } + + if (str->eof_p) + 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)); + memcpy (data, (void*)((BYTE*)str->buffer + str->bufpos), size); + str->bufpos += size; + + /* Read more if buffer is exhausted */ + if (str->bufsize == str->bufpos) + winsock_initiate_read (str); + + return size; +} + +static ssize_t +winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size) +{ + struct winsock_stream *str = WINSOCK_STREAM_DATA (stream); + + if (str->pending_p) + { + if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT) + { + str->blocking_p = 1; + return -1; + } + else + { + DWORD dw_unused; + if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &dw_unused, TRUE)) + str->error_p = 1; + str->pending_p = 0; + } + } + + str->blocking_p = 0; + + if (str->error_p) + return -1; + + if (size == 0) + return 0; + + { + ResetEvent (str->ov.hEvent); + + /* 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 + str->error_p = 1; + } + + return str->error_p ? -1 : size; +} + +static int +winsock_closer (Lstream *lstr) +{ + struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr); + + if (lstr->flags & LSTREAM_FL_READ) + shutdown (str->s, 0); + else + shutdown (str->s, 1); + + CloseHandle ((HANDLE)str->s); + if (str->pending_p) + WaitForSingleObject (str->ov.hEvent, INFINITE); + + if (lstr->flags & LSTREAM_FL_READ) + xfree (str->buffer); + + CloseHandle (str->ov.hEvent); + return 0; +} + +static int +winsock_was_blocked_p (Lstream *stream) +{ + struct winsock_stream *str = WINSOCK_STREAM_DATA (stream); + return str->blocking_p; +} + +static Lisp_Object +make_winsock_stream_1 (SOCKET s, LPARAM param, CONST char *mode) +{ + Lisp_Object obj; + Lstream *lstr = Lstream_new (lstream_winsock, mode); + struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr); + + str->s = s; + str->blocking_p = 0; + str->error_p = 0; + str->eof_p = 0; + str->pending_p = 0; + str->user_data = param; + + xzero (str->ov); + str->ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + + if (lstr->flags & LSTREAM_FL_READ) + { + str->buffer = xmalloc (WINSOCK_READ_BUFFER_SIZE); + winsock_initiate_read (str); + } + + lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE; + XSETLSTREAM (obj, lstr); + return obj; +} + +static Lisp_Object +make_winsock_input_stream (SOCKET s, LPARAM param) +{ + return make_winsock_stream_1 (s, param, "r"); +} + +static Lisp_Object +make_winsock_output_stream (SOCKET s, LPARAM param) +{ + return make_winsock_stream_1 (s, param, "w"); +} + +static HANDLE +get_winsock_stream_waitable (Lstream *lstr) +{ + struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr); + return str->ov.hEvent; +} + +static LPARAM +get_winsock_stream_param (Lstream *lstr) +{ + struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr); + return str->user_data; +} + +static void +init_winsock_stream (void) +{ + LSTREAM_HAS_METHOD (winsock, reader); + LSTREAM_HAS_METHOD (winsock, writer); + LSTREAM_HAS_METHOD (winsock, closer); + LSTREAM_HAS_METHOD (winsock, was_blocked_p); +} +#endif /* defined (HAVE_SOCKETS) */ + +/************************************************************************/ +/* Dispatch queue management */ +/************************************************************************/ + +static int +mswindows_user_event_p (struct Lisp_Event* sevt) +{ + return (sevt->event_type == key_press_event + || sevt->event_type == button_press_event + || sevt->event_type == button_release_event + || sevt->event_type == misc_user_event); +} + +/* + * Add an emacs event to the proper dispatch queue + */ +static 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_tail : + &mswindows_s_dispatch_event_queue_tail); + + /* Avoid blocking on WaitMessage */ + PostMessage (NULL, XM_BUMPQUEUE, 0, 0); +} + +/* + * Add a misc-user event to the dispatch queue. + * + * Stuff it into our own dispatch queue, so we have something + * to return from next_event callback. + */ +void +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); + + e->event_type = misc_user_event; + e->channel = channel; + e->event.misc.function = function; + e->event.misc.object = object; + + mswindows_enqueue_dispatch_event (event); +} + +void +mswindows_enqueue_magic_event (HWND hwnd, UINT message) +{ + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + struct 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; + + mswindows_enqueue_dispatch_event (emacs_event); +} + +static void +mswindows_enqueue_process_event (struct Lisp_Process* p) +{ + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + struct Lisp_Event* event = XEVENT (emacs_event); + Lisp_Object process; + XSETPROCESS (process, p); + + event->event_type = process_event; + event->timestamp = GetTickCount (); + event->event.process.process = process; + + mswindows_enqueue_dispatch_event (emacs_event); +} + +static void +mswindows_enqueue_mouse_button_event (HWND hwnd, UINT message, POINTS where, DWORD when) +{ + + /* 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); + + 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); + 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_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); +} + +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); + + event->channel = mswindows_find_console(hwnd); + event->timestamp = GetMessageTime(); + event->event_type = key_press_event; + event->event.key.keysym = keysym; + event->event.key.modifiers = mods; + mswindows_enqueue_dispatch_event (emacs_event); +} + +/* + * Remove and return the first emacs event on the dispatch queue. + * Give a preference to user events over non-user ones. + */ +static Lisp_Object +mswindows_dequeue_dispatch_event () +{ + Lisp_Object event; + struct 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 : + &mswindows_u_dispatch_event_queue, + NILP(mswindows_u_dispatch_event_queue) ? + &mswindows_s_dispatch_event_queue_tail : + &mswindows_u_dispatch_event_queue_tail); + + sevt = XEVENT(event); + if (sevt->event_type == key_press_event + && (sevt->event.key.modifiers & FAKE_MOD_QUIT)) + { + sevt->event.key.modifiers &= ~FAKE_MOD_QUIT; + --mswindows_quit_chars_count; + } + + return event; +} + +/* + * Remove and return the first emacs event on the dispatch queue that matches + * 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 aborts. + */ + +Lisp_Object +mswindows_cancel_dispatch_event (struct Lisp_Event *match) +{ + Lisp_Object event; + 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; + + assert (match->event_type == timeout_event + || match->event_type == key_press_event); + + EVENT_CHAIN_LOOP (event, *head) + { + struct 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); + else + { + XSET_EVENT_NEXT (previous_event, XEVENT_NEXT (event)); + if (EQ (*tail, event)) + *tail = previous_event; + } + + return event; + } + previous_event = event; + } + return Qnil; +} + +#ifndef HAVE_MSG_SELECT +/************************************************************************/ +/* Waitable handles manipulation */ +/************************************************************************/ +static int +find_waitable_handle (HANDLE h) +{ + int i; + for (i = 0; i < mswindows_waitable_count; ++i) + if (mswindows_waitable_handles[i] == h) + return i; + + return -1; +} + +static BOOL +add_waitable_handle (HANDLE h) +{ + assert (find_waitable_handle (h) < 0); + if (mswindows_waitable_count == MAX_WAITABLE) + return FALSE; + + mswindows_waitable_handles [mswindows_waitable_count++] = h; + return TRUE; +} + +static void +remove_waitable_handle (HANDLE h) +{ + int ix = find_waitable_handle (h); + if (ix < 0) + return; + + mswindows_waitable_handles [ix] = + mswindows_waitable_handles [--mswindows_waitable_count]; +} +#endif /* HAVE_MSG_SELECT */ + + +/************************************************************************/ +/* Event pump */ +/************************************************************************/ + +static Lisp_Object +mswindows_modal_loop_error_handler (Lisp_Object cons_sig_data, + Lisp_Object u_n_u_s_e_d) +{ + mswindows_error_caught_in_modal_loop = cons_sig_data; + return Qunbound; +} + +Lisp_Object +mswindows_protect_modal_loop (Lisp_Object (*bfun) (Lisp_Object barg), + Lisp_Object barg) +{ + Lisp_Object tmp; + + ++mswindows_in_modal_loop; + tmp = condition_case_1 (Qt, + bfun, barg, + mswindows_modal_loop_error_handler, Qnil); + --mswindows_in_modal_loop; + + return tmp; +} + +void +mswindows_unmodalize_signal_maybe (void) +{ + if (!NILP (mswindows_error_caught_in_modal_loop)) + { + /* Got an error while messages were pumped while + in window procedure - have to resignal */ + Lisp_Object sym = XCAR (mswindows_error_caught_in_modal_loop); + Lisp_Object data = XCDR (mswindows_error_caught_in_modal_loop); + mswindows_error_caught_in_modal_loop = Qnil; + Fsignal (sym, data); + } +} + +/* + * This is an unsafe part of event pump, guarded by + * condition_case. See mswindows_pump_outstanding_events + */ +static Lisp_Object +mswindows_unsafe_pump_events (Lisp_Object u_n_u_s_e_d) +{ + /* This function can call lisp */ + Lisp_Object event = Fmake_event (Qnil, Qnil); + struct gcpro gcpro1; + int do_redisplay = 0; + GCPRO1 (event); + + while (detect_input_pending ()) + { + Fnext_event (event, Qnil); + Fdispatch_event (event); + do_redisplay = 1; + } + + if (do_redisplay) + redisplay (); + + Fdeallocate_event (event); + UNGCPRO; + + /* Qt becomes return value of mswindows_pump_outstanding_events + once we get here */ + return Qt; +} + +/* + * This function pumps emacs events, while available, by using + * next_message/dispatch_message loop. Errors are trapped around + * the loop so the function always returns. + * + * Windows message queue is not looked into during the call, + * 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 mswindows_in_modal_loop to nonzero. + * + * Return value is Qt if no errors was trapped, or Qunbound if + * there was an error. + * + * In case of error, a cons representing the error, in the + * form (SIGNAL . DATA), is stored in the module local variable + * mswindows_error_caught_in_modal_loop. This error is signaled + * again when DispatchMessage returns. Thus, Windows internal + * modal loops are protected against throws, which are proven + * to corrupt internal Windows structures. + * + * In case of success, mswindows_error_caught_in_modal_loop is + * assigned Qnil. + * + * 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 + * 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 + * will become frozen until the modal loop exits under normal + * condition (scrollbar drag is released, menu closed etc.) + */ +Lisp_Object +mswindows_pump_outstanding_events (void) +{ + /* This function can call lisp */ + + 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); + UNGCPRO; + return result; +} + +static void +mswindows_drain_windows_queue () +{ + MSG msg; + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + { + /* we have to translate messages that are not sent to the main + window. this is so that key presses work ok in things like + edit fields. however, we *musn't* translate message for the + main window as this is handled in the wnd proc. */ + if ( GetWindowLong (msg.hwnd, GWL_STYLE) & WS_CHILD ) + { + TranslateMessage (&msg); + } + DispatchMessage (&msg); + mswindows_unmodalize_signal_maybe (); + } +} + +/* + * 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 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). + * + * To detect this, we use a counter of active timers, and allow + * fetching WM_TIMER messages. Instead of trying to fetch a WM_TIMER + * which will never come when there are no pending timers, which leads + * to deadlock, we simply signal an error. + */ +static void +mswindows_need_event_in_modal_loop (int badly_p) +{ + MSG msg; + + /* Check if already have one */ + if (!NILP (mswindows_u_dispatch_event_queue) + || !NILP (mswindows_s_dispatch_event_queue)) + return; + + /* No event is ok */ + if (!badly_p) + return; + + /* We do not check the _u_ queue, because timers go to _s_ */ + while (NILP (mswindows_s_dispatch_event_queue)) + { + /* 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); + } +} + +/* + * This drains the event queue and fills up two internal queues until + * an event of a type specified by USER_P is retrieved. + * + * + * Used by emacs_mswindows_event_pending_p and emacs_mswindows_next_event + */ +static void +mswindows_need_event (int badly_p) +{ + int active; + + if (mswindows_in_modal_loop) + { + mswindows_need_event_in_modal_loop (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)) + { +#ifdef HAVE_MSG_SELECT + int i; + 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 + { + EMACS_SET_SECS_USECS (sometime, 0, 0); + 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) + { + if (FD_ISSET (windows_fd, &temp_mask)) + { + mswindows_drain_windows_queue (); + } +#ifdef HAVE_TTY + /* Look for a TTY event */ + for (i = 0; i < MAXDESC-1; i++) + { + /* 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)) + { + struct console *c = tty_find_console_from_fd (i); + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + struct 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; + } + } + } +#endif + /* Look for a process event */ + for (i = 0; i < MAXDESC-1; i++) + { + if (FD_ISSET (i, &temp_mask)) + { + 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 + { + /* 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); + } + } + } + } + else if (active==-1) + { + if (errno != EINTR) + { + /* something bad happened */ + assert(0); + } + } + else + { + assert(0); + } +#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' output 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_notify + 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); + } + } +#endif + } /* 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); + + if (KillTimer (NULL, id_timer)) + --mswindows_pending_timers_count; + + event->channel = Qnil; + 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 + * depends on dnd support. + */ +#ifdef HAVE_DRAGNDROP +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; + return (HDDEDATA)FALSE; + + case XTYP_WILDCONNECT: + { + /* We only support one {service,topic} pair */ + HSZPAIR pairs[2] = { + { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } }; + + if (!(hszItem || DdeCmpStringHandles (hszItem, mswindows_dde_service)) && + !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))); + return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs, + sizeof (pairs), 0L, 0, uFmt, 0)); + } + 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); + 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); + + DdeGetData (hdata, cmd, len, 0); + cmd[len] = '\0'; + DdeFreeDataHandle (hdata); + + /* Check syntax & that it's an [Open("foo")] command, which we + * treat like a file drop */ + /* #### Ought to be generalised and accept some other commands */ + if (*cmd == '[') + cmd++; + if (strnicmp (cmd, MSWINDOWS_DDE_ITEM_OPEN, + strlen (MSWINDOWS_DDE_ITEM_OPEN))) + return DDE_FNOTPROCESSED; + cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN); + while (*cmd==' ') + cmd++; + if (*cmd!='(' || *(cmd+1)!='\"') + return DDE_FNOTPROCESSED; + end = (cmd+=2); + while (*end && *end!='\"') + end++; + if (!*end) + return DDE_FNOTPROCESSED; + *end = '\0'; + if (*(++end)!=')') + return DDE_FNOTPROCESSED; + if (*(++end)==']') + end++; + if (*end) + return DDE_FNOTPROCESSED; + +#ifdef __CYGWIN32__ + filename = alloca (cygwin32_win32_to_posix_path_list_buf_size (cmd) + 5); + strcpy (filename, "file:"); + cygwin32_win32_to_posix_path_list (cmd, filename+5); +#else + dostounix_filename (cmd); + filename = alloca (strlen (cmd)+6); + strcpy (filename, "file:"); + strcat (filename, cmd); +#endif + GCPRO2 (emacs_event, l_dndlist); + l_dndlist = make_string (filename, strlen (filename)); + + /* Find a mswindows frame */ + event->channel = Qnil; + FRAME_LOOP_NO_BREAK (frmcons, devcons, concons) + { + Lisp_Object frame = XCAR (frmcons); + if (FRAME_TYPE_P (XFRAME (frame), mswindows)) + event->channel = frame; + }; + assert (!NILP (event->channel)); + + event->timestamp = GetTickCount(); + event->event_type = misc_user_event; + event->event.misc.button = 1; + event->event.misc.modifiers = 0; + event->event.misc.x = -1; + event->event.misc.y = -1; + event->event.misc.function = Qdragdrop_drop_dispatch; + event->event.misc.object = Fcons (Qdragdrop_URL, + Fcons (l_dndlist, Qnil)); + mswindows_enqueue_dispatch_event (emacs_event); + UNGCPRO; + return (HDDEDATA) DDE_FACK; + } + DdeFreeDataHandle (hdata); + return (HDDEDATA) DDE_FNOTPROCESSED; + + default: + return (HDDEDATA) NULL; + } +} +#endif + +/* + * 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; + + 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_KEYUP: + case WM_SYSKEYUP: + /* See Win95 comment under WM_KEYDOWN */ + { + BYTE keymap[256]; + + if (wParam == VK_CONTROL) + { + GetKeyboardState (keymap); + keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80; + SetKeyboardState (keymap); + } + else if (wParam == VK_MENU) + { + GetKeyboardState (keymap); + keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80; + SetKeyboardState (keymap); + } + }; + goto defproc; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + /* In some locales the right-hand Alt key is labelled AltGr. This key + * should produce alternative charcaters 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[256]; + int has_AltGr = mswindows_current_layout_has_AltGr (); + int mods; + int extendedp = lParam & 0x1000000; + Lisp_Object keysym; + + GetKeyboardState (keymap); + mods = mswindows_modifier_state (keymap, has_AltGr); + + /* Handle non-printables */ + if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods, + extendedp))) + mswindows_enqueue_keypress_event (hwnd, keysym, mods); + else /* Normal keys & modifiers */ + { + int quit_ch = CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd))); + BYTE keymap_orig[256]; + POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) }; + MSG msg; + + 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 [extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80; + else if (wParam == VK_MENU) + keymap [extendedp ? VK_RMENU : VK_LMENU] |= 0x80; + + memcpy (keymap_orig, keymap, 256); + + /* Remove shift modifier from an ascii character */ + mods &= ~MOD_SHIFT; + + /* Clear control and alt modifiers unless AltGr is pressed */ + 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); + + /* Maybe generate some WM_[SYS]CHARs in the queue */ + TranslateMessage (&msg); + + 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; + } + + 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 && (mswindows_meta_activates_menu || wParam != VK_MENU)) + 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; + + 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), 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; + + 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), 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; + + 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), 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); + + 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; + + 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, 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...*/ + GET_C_STRING_EXT_DATA_ALLOCA (btext, FORMAT_OS, + tttext->lpszText); + } +#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 index = SendMessage (nmhdr->hwndFrom, TCM_GETCURSEL, 0, 0); + frame = XFRAME (mswindows_find_frame (hwnd)); + + item.mask = TCIF_PARAM; + SendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM)index, + (LPARAM)&item); + + mswindows_handle_gui_wm_command (frame, 0, item.lParam); + } + } + break; + + case WM_PAINT: + { + /* According to the docs we need to check GetUpdateRect() before + actually doing a WM_PAINT */ + if (GetUpdateRect (hwnd, NULL, FALSE)) + { + PAINTSTRUCT paintStruct; + int x, y, width, height; + + frame = XFRAME (mswindows_find_frame (hwnd)); + + 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. */ + if (!check_for_ignored_expose (frame, x, y, width, height)) + { + hold_ignored_expose_registration = 1; + mswindows_redraw_exposed_area (frame, x, y, width, height); + hold_ignored_expose_registration = 0; + } + + EndPaint (hwnd, &paintStruct); + } + else + goto defproc; + } + 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; + + /* 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_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; + + 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; + } + + 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 + +#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)) + { + 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)); + +#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 + + 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; + + 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) + && + !NILP (XIMAGE_INSTANCE_WIDGET_FACE (image_instance))) + { + /* 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_SUBWINDOW_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_SUBWINDOW_FRAME (image_instance))))); + SetBkMode (hdc, OPAQUE); + SetBkColor + (hdc, + COLOR_INSTANCE_MSWINDOWS_COLOR + (XCOLOR_INSTANCE + (FACE_BACKGROUND + (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), + XIMAGE_INSTANCE_SUBWINDOW_FRAME (image_instance))))); + return (LRESULT)widget_brush; + } + } + } + goto defproc; + +#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; + + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + GCPRO3 (emacs_event, l_dndlist, l_item); + + if (!DragQueryPoint ((HANDLE) 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, 0); + event->event.misc.x = point.x; + event->event.misc.y = point.y; + event->event.misc.function = Qdragdrop_drop_dispatch; + + filecount = DragQueryFile ((HANDLE) wParam, 0xffffffff, NULL, 0); + for (i=0; i<filecount; i++) + { + len = DragQueryFile ((HANDLE) wParam, i, NULL, 0); + /* The URLs that we make here aren't correct according to section + * 3.10 of rfc1738 because they're missing the //<host>/ 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); +#else + filename = (char *)xmalloc (len+6); + strcpy (filename, "file:"); + DragQueryFile ((HANDLE) wParam, i, filename+5, len+1); + dostounix_filename (filename+5); +#endif + l_item = make_string (filename, strlen (filename)); + l_dndlist = Fcons (l_item, l_dndlist); + xfree (filename); + } + DragFinish ((HANDLE) wParam); + + 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); + } + return (0); +} + + +/************************************************************************/ +/* keyboard, mouse & other helpers for the windows procedure */ +/************************************************************************/ +static void +mswindows_set_chord_timer (HWND hwnd) +{ + int interval; + + /* We get one third half system double click threshold */ + if (mswindows_mouse_button_tolerance <= 0) + interval = GetDoubleClickTime () / 3; + else + interval = mswindows_mouse_button_tolerance; + + SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0); +} + +static int +mswindows_button2_near_enough (POINTS p1, POINTS p2) +{ + int dx, dy; + if (mswindows_mouse_button_max_skew_x <= 0) + dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2; + else + dx = mswindows_mouse_button_max_skew_x; + + if (mswindows_mouse_button_max_skew_y <= 0) + dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2; + else + dy = mswindows_mouse_button_max_skew_y; + + return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy; +} + +static int +mswindows_current_layout_has_AltGr (void) +{ + /* This simple caching mechanism saves 10% of CPU + 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 = GetKeyboardLayout (0); + if (current_hkl != last_hkl) + { + TCHAR c; + last_hkl_has_AltGr = 0; + /* In this loop, we query whether a character requires + AltGr to be down to generate it. If at least such one + found, this means that the layout does regard AltGr */ + for (c = ' '; c <= 0xFFU && c != 0 && !last_hkl_has_AltGr; ++c) + if (HIBYTE (VkKeyScan (c)) == 6) + last_hkl_has_AltGr = 1; + last_hkl = current_hkl; + } + return last_hkl_has_AltGr; +} + + +/* 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) +{ + int mods = 0; + + if (keymap == NULL) + { + keymap = (BYTE*) alloca(256); + GetKeyboardState (keymap); + has_AltGr = mswindows_current_layout_has_AltGr (); + } + + 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; + } + else + { + mods |= (keymap [VK_MENU] & 0x80) ? MOD_META : 0; + mods |= (keymap [VK_CONTROL] & 0x80) ? MOD_CONTROL : 0; + } + + mods |= (keymap [VK_SHIFT] & 0x80) ? MOD_SHIFT : 0; + + return mods; +} + +/* + * 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 + */ +Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods, + int extendedp) +{ + if (extendedp) /* Keys not present on a 82 key keyboard */ + { + switch (mswindows_key) + { + 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; + } + } + 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_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"); +#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_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; +} + +/* + * Find the console that matches the supplied mswindows window handle + */ +Lisp_Object +mswindows_find_console (HWND hwnd) +{ + /* We only support one console */ + return XCAR (Vconsole_list); +} + +/* + * Find the frame that matches the supplied mswindows window handle + */ +static Lisp_Object +mswindows_find_frame (HWND hwnd) +{ + LONG l = GetWindowLong (hwnd, XWL_FRAMEOBJ); + Lisp_Object f; + if (l == 0) + { + /* We are in progress of frame creation. Return the frame + being created, as it still not remembered in the window + extra storage. */ + assert (!NILP (Vmswindows_frame_being_created)); + return Vmswindows_frame_being_created; + } + VOID_TO_LISP (f, l); + return f; +} + + +/************************************************************************/ +/* methods */ +/************************************************************************/ + +static int +emacs_mswindows_add_timeout (EMACS_TIME thyme) +{ + int milliseconds; + EMACS_TIME current_time; + EMACS_GET_TIME (current_time); + EMACS_SUB_TIME (thyme, thyme, current_time); + milliseconds = EMACS_SECS (thyme) * 1000 + + (EMACS_USECS (thyme) + 500) / 1000; + if (milliseconds < 1) + milliseconds = 1; + ++mswindows_pending_timers_count; + return SetTimer (NULL, 0, milliseconds, + (TIMERPROC) mswindows_wm_timer_callback); +} + +static void +emacs_mswindows_remove_timeout (int id) +{ + struct Lisp_Event match_against; + Lisp_Object emacs_event; + + if (KillTimer (NULL, id)) + --mswindows_pending_timers_count; + + /* If there is a dispatch event generated by this + timeout in the queue, we have to remove it too. */ + match_against.event_type = timeout_event; + match_against.event.timeout.interval_id = id; + emacs_event = mswindows_cancel_dispatch_event (&match_against); + if (!NILP (emacs_event)) + Fdeallocate_event(emacs_event); +} + +/* If `user_p' is false, then return whether there are any win32, timeout, + * or subprocess events pending (that is, whether + * emacs_mswindows_next_event() would return immediately without blocking). + * + * if `user_p' is true, then return whether there are any *user generated* + * events available (that is, whether there are keyboard or mouse-click + * events ready to be read). This also implies that + * emacs_mswindows_next_event() would not block. + */ +static int +emacs_mswindows_event_pending_p (int user_p) +{ + mswindows_need_event (0); + return (!NILP (mswindows_u_dispatch_event_queue) + || (!user_p && !NILP (mswindows_s_dispatch_event_queue))); +} + +/* + * Return the next event + */ +static void +emacs_mswindows_next_event (struct Lisp_Event *emacs_event) +{ + Lisp_Object event, event2; + + mswindows_need_event (1); + + event = mswindows_dequeue_dispatch_event (); + XSETEVENT (event2, emacs_event); + Fcopy_event (event, event2); + Fdeallocate_event (event); +} + +/* + * Handle a magic event off the dispatch queue. + */ +static void +emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event) +{ + switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)) + { + case XM_BUMPQUEUE: + break; + + case WM_SETFOCUS: + case WM_KILLFOCUS: + { + Lisp_Object frame = EVENT_CHANNEL (emacs_event); + struct frame *f = XFRAME (frame); + int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS); + Lisp_Object conser; + + /* struct gcpro gcpro1; */ + + /* Clear sticky modifiers here (if we had any) */ + + conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil)); + /* GCPRO1 (conser); XXX Not necessary? */ + 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; */ + + } + break; + + case XM_MAPFRAME: + case XM_UNMAPFRAME: + { + Lisp_Object frame = EVENT_CHANNEL (emacs_event); + va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) + == XM_MAPFRAME ? + 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 : + Qmouse_leave_frame_hook, 1, frame); +#endif + + default: + assert(0); + } +} + +#ifndef HAVE_MSG_SELECT +static HANDLE +get_process_input_waitable (struct Lisp_Process *process) +{ + Lisp_Object instr, outstr, p; + XSETPROCESS (p, process); + get_process_streams (process, &instr, &outstr); + assert (!NILP (instr)); +#if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT) + return (network_connection_p (p) + ? get_winsock_stream_waitable (XLSTREAM (instr)) + : get_ntpipe_input_stream_waitable (XLSTREAM (instr))); +#else + return get_ntpipe_input_stream_waitable (XLSTREAM (instr)); +#endif +} + +static void +emacs_mswindows_select_process (struct Lisp_Process *process) +{ + HANDLE hev = get_process_input_waitable (process); + + if (!add_waitable_handle (hev)) + error ("Too many active processes"); + +#ifdef HAVE_WIN32_PROCESSES + { + Lisp_Object p; + XSETPROCESS (p, process); + if (!network_connection_p (p)) + { + HANDLE hprocess = get_nt_process_handle (process); + if (!add_waitable_handle (hprocess)) + { + remove_waitable_handle (hev); + error ("Too many active processes"); + } + } + } +#endif +} + +static void +emacs_mswindows_unselect_process (struct 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 +emacs_mswindows_quit_p (void) +{ + MSG msg; + + /* Quit cannot happen in modal loop: all program + input is dedicated to Windows. */ + if (mswindows_in_modal_loop) + return; + + /* Drain windows queue. This sets up number of quit characters in the queue + * (and also processes wm focus change, move, resize, etc messages). + * We don't want to process WM_PAINT messages because this function can be + * called from almost anywhere and the windows' states may be changing. */ + while (PeekMessage (&msg, NULL, 0, WM_PAINT-1, PM_REMOVE) || + PeekMessage (&msg, NULL, WM_PAINT+1, WM_USER-1, PM_REMOVE)) + DispatchMessage (&msg); + + if (mswindows_quit_chars_count > 0) + { + /* Yes there's a hidden one... Throw it away */ + struct Lisp_Event match_against; + Lisp_Object emacs_event; + + 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)); + + Vquit_flag = (XEVENT(emacs_event)->event.key.modifiers & MOD_SHIFT + ? Qcritical : Qt); + + Fdeallocate_event(emacs_event); + --mswindows_quit_chars_count; + } +} + +USID +emacs_mswindows_create_stream_pair (void* inhandle, void* outhandle, + Lisp_Object* instream, + Lisp_Object* outstream, + int flags) +{ + /* Handles for streams */ + HANDLE hin, hout; + /* fds. These just stored along with the streams, and are closed in + delete stream pair method, because we need to handle fake unices + here. */ + int fdi, fdo; + + /* Decode inhandle and outhandle. Their meaning depends on + the process implementation being used. */ +#if defined (HAVE_WIN32_PROCESSES) + /* We're passed in Windows handles. That's what we like most... */ + hin = (HANDLE) inhandle; + hout = (HANDLE) outhandle; + fdi = fdo = -1; +#elif defined (HAVE_UNIX_PROCESSES) + /* We are passed UNIX fds. This must be Cygwin. + Fetch os handles */ + hin = inhandle >= 0 ? (HANDLE)get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE; + hout = outhandle >= 0 ? (HANDLE)get_osfhandle ((int)outhandle) : INVALID_HANDLE_VALUE; + fdi=(int)inhandle; + fdo=(int)outhandle; +#else +#error "So, WHICH kind of processes do you want?" +#endif + + *instream = (hin == INVALID_HANDLE_VALUE + ? Qnil +#if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT) + : flags & STREAM_NETWORK_CONNECTION + ? make_winsock_input_stream ((SOCKET)hin, fdi) +#endif + : make_ntpipe_input_stream (hin, fdi)); + +#ifdef HAVE_WIN32_PROCESSES + *outstream = (hout == INVALID_HANDLE_VALUE + ? Qnil +#if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT) + : flags & STREAM_NETWORK_CONNECTION + ? make_winsock_output_stream ((SOCKET)hout, fdo) +#endif + : make_ntpipe_output_stream (hout, fdo)); +#elif defined (HAVE_UNIX_PROCESSES) + *outstream = (fdo >= 0 + ? make_filedesc_output_stream (fdo, 0, -1, LSTR_BLOCKED_OK) + : Qnil); + +#if defined(HAVE_UNIX_PROCESSES) && defined(HAVE_PTYS) + /* FLAGS is process->pty_flag for UNIX_PROCESSES */ + if ((flags & STREAM_PTY_FLUSHING) && fdo >= 0) + { + Bufbyte eof_char = get_eof_char (fdo); + int pty_max_bytes = get_pty_max_bytes (fdo); + filedesc_stream_set_pty_flushing (XLSTREAM(*outstream), pty_max_bytes, eof_char); + } +#endif +#endif + + return (NILP (*instream) + ? USID_ERROR +#if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT) + : flags & STREAM_NETWORK_CONNECTION + ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream))) +#endif + : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (*instream)))); +} + +USID +emacs_mswindows_delete_stream_pair (Lisp_Object instream, + Lisp_Object outstream) +{ + /* Oh nothing special here for Win32 at all */ +#if defined (HAVE_UNIX_PROCESSES) + int in = (NILP(instream) + ? -1 +#if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT) + : LSTREAM_TYPE_P (XLSTREAM (instream), winsock) + ? get_winsock_stream_param (XLSTREAM (instream)) +#endif + : get_ntpipe_input_stream_param (XLSTREAM (instream))); + int out = (NILP(outstream) ? -1 + : filedesc_stream_fd (XLSTREAM (outstream))); + + if (in >= 0) + close (in); + if (out != in && out >= 0) + close (out); +#endif + + return (NILP (instream) + ? USID_DONTHASH +#if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT) + : LSTREAM_TYPE_P (XLSTREAM (instream), winsock) + ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream))) +#endif + : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream)))); +} + +#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) +{ +#if 0 /* #### */ + Lisp_Object instr, outstr; + + get_process_streams (p, &instr, &outstr); + /* if it still has fds, then it hasn't been killed yet. */ + assert (NILP(instr)); + assert (NILP(outstr)); + + /* #### More checks here */ +#endif +} +#endif + +/************************************************************************/ +/* initialization */ +/************************************************************************/ + +void +reinit_vars_of_event_mswindows (void) +{ + 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->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; + mswindows_event_stream->remove_timeout_cb = emacs_mswindows_remove_timeout; + mswindows_event_stream->quit_p_cb = emacs_mswindows_quit_p; + 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->create_stream_pair_cb = event_stream_unixoid_create_stream_pair; + mswindows_event_stream->delete_stream_pair_cb = event_stream_unixoid_delete_stream_pair; +#else + mswindows_event_stream->select_process_cb = emacs_mswindows_select_process; + mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process; + 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 +} + +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; + pdump_wire (&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; + pdump_wire (&mswindows_u_dispatch_event_queue_tail); + + mswindows_error_caught_in_modal_loop = Qnil; + staticpro (&mswindows_error_caught_in_modal_loop); + + DEFVAR_BOOL ("mswindows-meta-activates-menu", &mswindows_meta_activates_menu /* +*Controls whether pressing and releasing the Meta (Alt) key should +activate the menubar. +Default is t. +*/ ); + + 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 +MDI application background color. Note that this option only has effect +if "Show window contents while dragging" is on in system Display/Plus! +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 /* +*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. +If both mouse buttons are depressed within this interval, a middle mouse +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 /* +*Maximum horizontal distance in pixels between points in which left and +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 /* +*Maximum vertical distance in pixels between points in which left and +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. +*/ ); + + mswindows_mouse_button_max_skew_x = 0; + mswindows_mouse_button_max_skew_y = 0; + mswindows_mouse_button_tolerance = 0; + mswindows_meta_activates_menu = 1; +} + +void +syms_of_event_mswindows (void) +{ +} + +void +lstream_type_create_mswindows_selectable (void) +{ + init_slurp_stream (); + init_shove_stream (); +#if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT) + init_winsock_stream (); +#endif +} + +void +init_event_mswindows_late (void) +{ +#ifdef HAVE_MSG_SELECT + windows_fd = open("/dev/windows", O_RDONLY | O_NONBLOCK, 0); + assert (windows_fd>=0); + FD_SET (windows_fd, &input_wait_mask); + FD_ZERO(&zero_mask); +#endif + + event_stream = mswindows_event_stream; + + mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE); + mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS); +}