Mercurial > hg > xemacs-beta
annotate src/event-msw.c @ 5736:3192994c49ca
Convert C (un)signed long long values to bignums properly.
This patch also does the following:
- Uses make_fixnum instead of make_integer when the argument is guaranteed to
be in the fixnum range.
- Introduces make_unsigned_integer so that we handle unsigned values with the
high bit set correctly.
- Introduces conversions between bignums and (un)signed long long values.
- Uses mp_set_memory_functions with the BSD MP code, if it exists.
- Eliminates some unnecessary consing in the Lisp + and * implementations.
- Fixes a problem with check_valid_xbm_inline(). This function is called
during intialization. It calls Ftimes. When using pdump, this is a
problem, because (a) the bignum code is not initialized until *after*
dumping, so we don't try to dump any bignums, and (b) multiplication of
integers is done inside bignums so we handle fixnum overflow correctly. I
decided that an XBM file with dimensions that don't fit into fixnums is
probably not something we want to try to handle anyway, and did the
arithmetic with C values instead of Lisp values. Doing that broke one test,
which started getting a different error message from the one it expected, so
I adjusted the test to match the new reality.
- Fixes a few miscellaneous bugs in the BSD MP code.
See <CAHCOHQk0u0=eD1fUMHTNWi2Yh=1WgiYyCXdMbsGzHBNhdqYz4w@mail.gmail.com> in
xemacs-patches, as well as followup messages.
author | Jerry James <james@xemacs.org> |
---|---|
date | Mon, 17 Jun 2013 10:23:00 -0600 |
parents | 56144c8593a8 |
children | 0f2338afbabf |
rev | line source |
---|---|
442 | 1 /* The mswindows event_stream interface. |
428 | 2 Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. |
3 Copyright (C) 1995 Sun Microsystems, Inc. | |
5043 | 4 Copyright (C) 1996, 2000, 2001, 2002, 2003, 2005, 2010 Ben Wing. |
428 | 5 Copyright (C) 1997 Jonathan Harris. |
6 | |
7 This file is part of XEmacs. | |
8 | |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5176
diff
changeset
|
9 XEmacs is free software: you can redistribute it and/or modify it |
428 | 10 under the terms of the GNU General Public License as published by the |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5176
diff
changeset
|
11 Free Software Foundation, either version 3 of the License, or (at your |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5176
diff
changeset
|
12 option) any later version. |
428 | 13 |
14 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 for more details. | |
18 | |
19 You should have received a copy of the GNU General Public License | |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5176
diff
changeset
|
20 along with XEmacs. If not, see <http://www.gnu.org/licenses/>. */ |
428 | 21 |
22 /* Synched up with: Not in FSF. */ | |
23 | |
771 | 24 /* This file essentially Mule-ized (except perhaps some Unicode splitting). |
25 5-2000. */ | |
26 | |
428 | 27 /* Authorship: |
28 | |
29 Ultimately based on FSF. | |
30 Rewritten by Ben Wing. | |
31 Rewritten for mswindows by Jonathan Harris, November 1997 for 21.0. | |
32 Subprocess and modal loop support by Kirill M. Katsnelson. | |
33 */ | |
34 | |
771 | 35 #define NEED_MSWINDOWS_SHLOBJ /* for IShellLink */ |
36 | |
428 | 37 #include <config.h> |
38 #include "lisp.h" | |
39 | |
2286 | 40 #ifdef CYGWIN |
41 # define USED_IF_CYGWIN(decl) decl | |
42 # define UNUSED_IF_CYGWIN(decl) UNUSED (decl) | |
43 #else | |
44 # define USED_IF_CYGWIN(decl) UNUSED (decl) | |
45 # define UNUSED_IF_CYGWIN(decl) decl | |
46 #endif | |
47 | |
853 | 48 #if defined (CYGWIN) && !defined (HAVE_MSG_SELECT) |
49 #error We do not support non-select() versions (i.e. very old) of Cygwin. | |
50 #endif | |
51 | |
52 /* Acceptable are: | |
53 | |
54 WIN32_NATIVE and HAVE_WIN32_PROCESSES and nothing else | |
55 | |
56 CYGWIN and HAVE_MSG_SELECT and HAVE_UNIX_PROCESSES and nothing else | |
57 */ | |
58 #ifdef WIN32_NATIVE | |
856 | 59 # if !(defined (HAVE_WIN32_PROCESSES) && !defined (HAVE_UNIX_PROCESSES) && !defined (HAVE_MSG_SELECT) && !defined (CYGWIN)) |
853 | 60 # error Something is wrong with your process definitions for Windows native. |
61 # endif | |
62 #elif defined (CYGWIN) | |
63 # if !(defined (HAVE_UNIX_PROCESSES) && defined (HAVE_MSG_SELECT) && !defined (HAVE_WIN32_PROCESSES) && !defined (WIN32_NATIVE)) | |
64 # error Something is wrong with your process definitions for Cygwin. | |
65 # endif | |
66 #else | |
67 # error Something is wrong -- you are neither Windows native (possibly MinGW) nor Cygwin. | |
68 #endif | |
69 | |
800 | 70 #include "buffer.h" |
872 | 71 #include "device-impl.h" |
800 | 72 #include "events.h" |
73 #include "faces.h" | |
872 | 74 #include "frame-impl.h" |
800 | 75 #include "glyphs.h" |
76 #include "lstream.h" | |
77 #include "process.h" | |
78 #include "redisplay.h" | |
79 #include "sysdep.h" | |
80 #include "window.h" | |
81 | |
1204 | 82 #include "console-stream-impl.h" |
872 | 83 #include "console-msw-impl.h" |
5176
8b2f75cecb89
rename objects* (.c, .h and .el files) to fontcolor*
Ben Wing <ben@xemacs.org>
parents:
5043
diff
changeset
|
84 #include "fontcolor-msw-impl.h" |
428 | 85 |
86 #ifdef HAVE_SCROLLBARS | |
87 # include "scrollbar-msw.h" | |
88 #endif | |
89 | |
90 #ifdef HAVE_MENUBARS | |
442 | 91 # include "menubar.h" |
428 | 92 #endif |
93 | |
94 #ifdef HAVE_DRAGNDROP | |
95 # include "dragdrop.h" | |
96 #endif | |
97 | |
558 | 98 #include "sysfile.h" |
428 | 99 #include "sysproc.h" |
558 | 100 #include "systime.h" |
428 | 101 #include "syswait.h" |
102 | |
103 #ifdef HAVE_MENUBARS | |
104 #define ADJR_MENUFLAG TRUE | |
105 #else | |
106 #define ADJR_MENUFLAG FALSE | |
107 #endif | |
108 | |
109 /* Timer ID used for button2 emulation */ | |
110 #define BUTTON_2_TIMER_ID 1 | |
111 | |
112 static Lisp_Object mswindows_find_console (HWND hwnd); | |
113 static Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, int mods, | |
114 int extendedp); | |
771 | 115 static int mswindows_modifier_state (BYTE *keymap, DWORD fwKeys, |
442 | 116 int has_AltGr); |
428 | 117 static void mswindows_set_chord_timer (HWND hwnd); |
118 static int mswindows_button2_near_enough (POINTS p1, POINTS p2); | |
119 static int mswindows_current_layout_has_AltGr (void); | |
442 | 120 static int mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam, |
121 int downp, int keyp); | |
428 | 122 |
123 static struct event_stream *mswindows_event_stream; | |
124 | |
853 | 125 #ifdef CYGWIN |
126 | |
428 | 127 extern SELECT_TYPE input_wait_mask, non_fake_input_wait_mask; |
128 extern SELECT_TYPE process_only_mask, tty_only_mask; | |
129 SELECT_TYPE zero_mask; | |
130 extern int signal_event_pipe_initialized; | |
131 int windows_fd; | |
853 | 132 |
133 #else | |
134 | |
856 | 135 /* The number of things we can wait on */ |
136 #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1) | |
137 | |
853 | 138 /* List of mswindows waitable handles. */ |
139 static HANDLE mswindows_waitable_handles[MAX_WAITABLE]; | |
140 | |
141 /* Number of wait handles */ | |
142 static int mswindows_waitable_count = 0; | |
143 | |
428 | 144 #endif |
145 | |
146 /* | |
1204 | 147 * We use an additional queue, as well as the normal dispatch queue, for |
148 * efficiency, the normal one for user events, and another (_s_) for non-user | |
149 * ones. We always return events out of the first one until it is empty and | |
150 * only then proceed with the second one. | |
428 | 151 */ |
1204 | 152 static Lisp_Object mswindows_s_dispatch_event_queue; |
153 static Lisp_Object mswindows_s_dispatch_event_queue_tail; | |
428 | 154 |
155 /* Brush for painting widgets */ | |
156 static HBRUSH widget_brush = 0; | |
157 static LONG last_widget_brushed = 0; | |
158 | |
159 /* These are Lisp integers; see DEFVARS in this file for description. */ | |
160 int mswindows_dynamic_frame_resize; | |
442 | 161 int mswindows_alt_by_itself_activates_menu; |
458 | 162 Fixnum mswindows_num_mouse_buttons; |
163 Fixnum mswindows_mouse_button_max_skew_x; | |
164 Fixnum mswindows_mouse_button_max_skew_y; | |
165 Fixnum mswindows_mouse_button_tolerance; | |
428 | 166 |
442 | 167 #ifdef DEBUG_XEMACS |
458 | 168 Fixnum debug_mswindows_events; |
593 | 169 |
170 static void debug_output_mswin_message (HWND hwnd, UINT message_, | |
171 WPARAM wParam, LPARAM lParam); | |
442 | 172 #endif |
173 | |
428 | 174 /* This is the event signaled by the event pump. |
175 See mswindows_pump_outstanding_events for comments */ | |
853 | 176 static int mswindows_error_caught_in_modal_loop; |
428 | 177 |
178 /* Count of wound timers */ | |
179 static int mswindows_pending_timers_count; | |
442 | 180 |
181 static DWORD mswindows_last_mouse_button_state; | |
853 | 182 |
1292 | 183 extern int mswindows_is_blocking; |
184 | |
428 | 185 |
853 | 186 #ifndef CYGWIN /* Skips past slurp, shove, or winsock streams */ |
187 | |
428 | 188 /************************************************************************/ |
189 /* Pipe instream - reads process output */ | |
190 /************************************************************************/ | |
191 | |
192 #define PIPE_READ_DELAY 20 | |
193 | |
194 #define HANDLE_TO_USID(h) ((USID)(h)) | |
195 | |
196 #define NTPIPE_SLURP_STREAM_DATA(stream) \ | |
197 LSTREAM_TYPE_DATA (stream, ntpipe_slurp) | |
198 | |
199 /* This structure is allocated by the main thread, and is deallocated | |
200 in the thread upon exit. There are situations when a thread | |
201 remains blocked for a long time, much longer than the lstream | |
202 exists. For example, "start notepad" command is issued from the | |
203 shell, then the shell is closed by C-c C-d. Although the shell | |
204 process exits, its output pipe will not get closed until the | |
656 | 205 notepad process exits also, because it inherits the pipe from the |
428 | 206 shell. In this case, we abandon the thread, and let it live until |
207 all such processes exit. While struct ntpipe_slurp_stream is | |
208 deallocated in this case, ntpipe_slurp_stream_shared_data are not. */ | |
209 | |
210 struct ntpipe_slurp_stream_shared_data | |
211 { | |
212 HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */ | |
853 | 213 /* This is a manual-reset object. */ |
428 | 214 HANDLE hev_caller; /* Caller blocks on this, and we signal it */ |
853 | 215 /* This is a manual-reset object. */ |
428 | 216 HANDLE hev_unsleep; /* Pipe read delay is canceled if this is set */ |
853 | 217 /* This is a manual-reset object. */ |
428 | 218 HANDLE hpipe; /* Pipe read end handle. */ |
219 LONG die_p; /* Thread must exit ASAP if non-zero */ | |
220 BOOL eof_p : 1; /* Set when thread saw EOF */ | |
221 BOOL error_p : 1; /* Read error other than EOF/broken pipe */ | |
222 BOOL inuse_p : 1; /* this structure is in use */ | |
223 LONG lock_count; /* Client count of this struct, 0=safe to free */ | |
224 BYTE onebyte; /* One byte buffer read by thread */ | |
225 }; | |
226 | |
227 #define MAX_SLURP_STREAMS 32 | |
228 struct ntpipe_slurp_stream_shared_data | |
229 shared_data_block[MAX_SLURP_STREAMS]={{0}}; | |
230 | |
231 struct ntpipe_slurp_stream | |
232 { | |
233 LPARAM user_data; /* Any user data stored in the stream object */ | |
771 | 234 struct ntpipe_slurp_stream_shared_data *thread_data; |
428 | 235 }; |
236 | |
771 | 237 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-input", ntpipe_slurp); |
428 | 238 |
239 /* This function is thread-safe, and is called from either thread | |
240 context. It serializes freeing shared data structure */ | |
241 static void | |
771 | 242 slurper_free_shared_data_maybe (struct ntpipe_slurp_stream_shared_data *s) |
428 | 243 { |
244 if (InterlockedDecrement (&s->lock_count) == 0) | |
245 { | |
246 /* Destroy events */ | |
247 CloseHandle (s->hev_thread); | |
248 CloseHandle (s->hev_caller); | |
249 CloseHandle (s->hev_unsleep); | |
673 | 250 CloseHandle (s->hpipe); |
428 | 251 s->inuse_p = 0; |
252 } | |
253 } | |
254 | |
771 | 255 static struct ntpipe_slurp_stream_shared_data * |
442 | 256 slurper_allocate_shared_data (void) |
428 | 257 { |
258 int i=0; | |
771 | 259 for (i = 0; i < MAX_SLURP_STREAMS; i++) |
428 | 260 { |
261 if (!shared_data_block[i].inuse_p) | |
262 { | |
771 | 263 shared_data_block[i].inuse_p = 1; |
428 | 264 return &shared_data_block[i]; |
265 } | |
266 } | |
771 | 267 return (struct ntpipe_slurp_stream_shared_data *)0; |
428 | 268 } |
269 | |
270 static DWORD WINAPI | |
271 slurp_thread (LPVOID vparam) | |
272 { | |
273 struct ntpipe_slurp_stream_shared_data *s = | |
771 | 274 (struct ntpipe_slurp_stream_shared_data *)vparam; |
428 | 275 |
276 for (;;) | |
277 { | |
278 /* Read one byte from the pipe */ | |
279 DWORD actually_read; | |
280 if (!ReadFile (s->hpipe, &s->onebyte, 1, &actually_read, NULL)) | |
281 { | |
282 DWORD err = GetLastError (); | |
283 if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA) | |
284 s->eof_p = TRUE; | |
285 else | |
286 s->error_p = TRUE; | |
287 } | |
288 else if (actually_read == 0) | |
289 s->eof_p = TRUE; | |
290 | |
291 /* We must terminate on an error or eof */ | |
292 if (s->eof_p || s->error_p) | |
293 InterlockedIncrement (&s->die_p); | |
294 | |
295 /* Before we notify caller, we unsignal our event. */ | |
296 ResetEvent (s->hev_thread); | |
297 | |
298 /* Now we got something to notify caller, either a byte or an | |
299 error/eof indication. Before we do, allow internal pipe | |
300 buffer to accumulate little bit more data. | |
301 Reader function pulses this event before waiting for | |
302 a character, to avoid pipe delay, and to get the byte | |
303 immediately. */ | |
304 if (!s->die_p) | |
305 WaitForSingleObject (s->hev_unsleep, PIPE_READ_DELAY); | |
306 | |
307 /* Either make event loop generate a process event, or | |
308 inblock reader */ | |
309 SetEvent (s->hev_caller); | |
310 | |
311 /* Cleanup and exit if we're shot off */ | |
312 if (s->die_p) | |
313 break; | |
314 | |
315 /* Block until the client finishes with retrieving the rest of | |
316 pipe data */ | |
317 WaitForSingleObject (s->hev_thread, INFINITE); | |
318 } | |
319 | |
320 slurper_free_shared_data_maybe (s); | |
321 | |
322 return 0; | |
323 } | |
324 | |
325 static Lisp_Object | |
326 make_ntpipe_input_stream (HANDLE hpipe, LPARAM param) | |
327 { | |
328 Lstream *lstr = Lstream_new (lstream_ntpipe_slurp, "r"); | |
771 | 329 struct ntpipe_slurp_stream *s = NTPIPE_SLURP_STREAM_DATA (lstr); |
428 | 330 DWORD thread_id_unused; |
331 HANDLE hthread; | |
332 | |
333 /* We deal only with pipes, for we're using PeekNamedPipe api */ | |
334 assert (GetFileType (hpipe) == FILE_TYPE_PIPE); | |
335 | |
336 s->thread_data = slurper_allocate_shared_data(); | |
337 | |
338 /* Create reader thread. This could fail, so do not create events | |
339 until thread is created */ | |
340 hthread = CreateThread (NULL, 0, slurp_thread, (LPVOID)s->thread_data, | |
341 CREATE_SUSPENDED, &thread_id_unused); | |
342 if (hthread == NULL) | |
343 { | |
344 Lstream_delete (lstr); | |
345 s->thread_data->inuse_p=0; | |
346 return Qnil; | |
347 } | |
348 | |
349 /* Shared data are initially owned by both main and slurper | |
350 threads. */ | |
351 s->thread_data->lock_count = 2; | |
352 s->thread_data->die_p = 0; | |
353 s->thread_data->eof_p = FALSE; | |
354 s->thread_data->error_p = FALSE; | |
355 s->thread_data->hpipe = hpipe; | |
356 s->user_data = param; | |
357 | |
358 /* hev_thread is a manual-reset event, initially signaled */ | |
771 | 359 s->thread_data->hev_thread = qxeCreateEvent (NULL, TRUE, TRUE, NULL); |
428 | 360 /* hev_caller is a manual-reset event, initially nonsignaled */ |
771 | 361 s->thread_data->hev_caller = qxeCreateEvent (NULL, TRUE, FALSE, NULL); |
428 | 362 /* hev_unsleep is a manual-reset event, initially nonsignaled */ |
771 | 363 s->thread_data->hev_unsleep = qxeCreateEvent (NULL, TRUE, FALSE, NULL); |
428 | 364 |
365 /* Now let it go */ | |
366 ResumeThread (hthread); | |
367 CloseHandle (hthread); | |
368 | |
369 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE; | |
793 | 370 return wrap_lstream (lstr); |
428 | 371 } |
372 | |
373 static LPARAM | |
374 get_ntpipe_input_stream_param (Lstream *stream) | |
375 { | |
771 | 376 struct ntpipe_slurp_stream *s = NTPIPE_SLURP_STREAM_DATA(stream); |
428 | 377 return s->user_data; |
378 } | |
379 | |
380 static HANDLE | |
381 get_ntpipe_input_stream_waitable (Lstream *stream) | |
382 { | |
771 | 383 struct ntpipe_slurp_stream *s = NTPIPE_SLURP_STREAM_DATA(stream); |
428 | 384 return s->thread_data->hev_caller; |
385 } | |
386 | |
665 | 387 static Bytecount |
462 | 388 ntpipe_slurp_reader (Lstream *stream, unsigned char *data, |
665 | 389 Bytecount size) |
428 | 390 { |
391 /* This function must be called from the main thread only */ | |
771 | 392 struct ntpipe_slurp_stream_shared_data *s = |
428 | 393 NTPIPE_SLURP_STREAM_DATA(stream)->thread_data; |
394 | |
395 if (!s->die_p) | |
396 { | |
397 DWORD wait_result; | |
398 /* Disallow pipe read delay for the thread: we need a character | |
399 ASAP */ | |
400 SetEvent (s->hev_unsleep); | |
401 | |
402 /* Check if we have a character ready. Give it a short delay, | |
771 | 403 for the thread to awake from pipe delay, just ion case */ |
428 | 404 wait_result = WaitForSingleObject (s->hev_caller, 2); |
405 | |
406 /* Revert to the normal sleep behavior. */ | |
407 ResetEvent (s->hev_unsleep); | |
408 | |
409 /* If there's no byte buffered yet, give up */ | |
410 if (wait_result == WAIT_TIMEOUT) | |
411 { | |
412 errno = EAGAIN; | |
413 return -1; | |
414 } | |
415 } | |
416 | |
417 /* Reset caller unlock event now, as we've handled the pending | |
418 process output event */ | |
419 ResetEvent (s->hev_caller); | |
420 | |
421 /* It is now safe to do anything with contents of S, except for | |
422 changing s->die_p, which still should be interlocked */ | |
423 | |
424 if (s->eof_p) | |
425 return 0; | |
426 if (s->error_p || s->die_p) | |
427 return -1; | |
428 | |
429 /* Ok, there were no error neither eof - we've got a byte from the | |
430 pipe */ | |
431 *(data++) = s->onebyte; | |
432 --size; | |
433 | |
434 { | |
435 DWORD bytes_read = 0; | |
436 if (size > 0) | |
437 { | |
438 DWORD bytes_available; | |
439 | |
440 /* If the api call fails, return at least one byte already | |
441 read. ReadFile in thread will return error */ | |
442 if (PeekNamedPipe (s->hpipe, NULL, 0, NULL, &bytes_available, NULL)) | |
443 { | |
444 | |
445 /* Fetch available bytes. The same consideration applies, | |
446 so do not check for errors. ReadFile in the thread will | |
447 fail if the next call fails. */ | |
448 if (bytes_available) | |
647 | 449 ReadFile (s->hpipe, data, min (bytes_available, (DWORD) size), |
428 | 450 &bytes_read, NULL); |
451 } | |
452 | |
453 /* Now we can unblock thread, so it attempts to read more */ | |
454 SetEvent (s->hev_thread); | |
455 return bytes_read + 1; | |
456 } | |
457 } | |
458 return 0; | |
459 } | |
460 | |
461 static int | |
462 ntpipe_slurp_closer (Lstream *stream) | |
463 { | |
464 /* This function must be called from the main thread only */ | |
771 | 465 struct ntpipe_slurp_stream_shared_data *s = |
428 | 466 NTPIPE_SLURP_STREAM_DATA(stream)->thread_data; |
467 | |
468 /* Force thread to stop */ | |
469 InterlockedIncrement (&s->die_p); | |
470 | |
471 /* Set events which could possibly block slurper. Let it finish soon | |
472 or later. */ | |
473 SetEvent (s->hev_unsleep); | |
474 SetEvent (s->hev_thread); | |
475 | |
476 /* Unlock and maybe free shared data */ | |
477 slurper_free_shared_data_maybe (s); | |
478 | |
479 return 0; | |
480 } | |
481 | |
482 static void | |
483 init_slurp_stream (void) | |
484 { | |
485 LSTREAM_HAS_METHOD (ntpipe_slurp, reader); | |
486 LSTREAM_HAS_METHOD (ntpipe_slurp, closer); | |
487 } | |
853 | 488 |
428 | 489 |
490 /************************************************************************/ | |
491 /* Pipe outstream - writes process input */ | |
492 /************************************************************************/ | |
493 | |
494 #define NTPIPE_SHOVE_STREAM_DATA(stream) \ | |
495 LSTREAM_TYPE_DATA (stream, ntpipe_shove) | |
496 | |
442 | 497 #define MAX_SHOVE_BUFFER_SIZE 512 |
428 | 498 |
499 struct ntpipe_shove_stream | |
500 { | |
501 LPARAM user_data; /* Any user data stored in the stream object */ | |
502 HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */ | |
853 | 503 /* This is an auto-reset object. */ |
428 | 504 HANDLE hpipe; /* Pipe write end handle. */ |
505 HANDLE hthread; /* Reader thread handle. */ | |
506 char buffer[MAX_SHOVE_BUFFER_SIZE]; /* Buffer being written */ | |
507 DWORD size; /* Number of bytes to write */ | |
508 LONG die_p; /* Thread must exit ASAP if non-zero */ | |
509 LONG idle_p; /* Non-zero if thread is waiting for job */ | |
510 BOOL error_p : 1; /* Read error other than EOF/broken pipe */ | |
511 BOOL blocking_p : 1;/* Last write attempt would cause blocking */ | |
512 }; | |
513 | |
771 | 514 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-output", ntpipe_shove); |
428 | 515 |
516 static DWORD WINAPI | |
517 shove_thread (LPVOID vparam) | |
518 { | |
771 | 519 struct ntpipe_shove_stream *s = (struct ntpipe_shove_stream *) vparam; |
428 | 520 |
521 for (;;) | |
522 { | |
523 DWORD bytes_written; | |
524 | |
525 /* Block on event and wait for a job */ | |
526 InterlockedIncrement (&s->idle_p); | |
527 WaitForSingleObject (s->hev_thread, INFINITE); | |
528 | |
771 | 529 if (s->die_p) |
530 break; | |
531 | |
442 | 532 /* Write passed buffer if any */ |
533 if (s->size > 0) | |
428 | 534 { |
442 | 535 if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL) |
536 || bytes_written != s->size) | |
537 { | |
538 s->error_p = TRUE; | |
539 InterlockedIncrement (&s->die_p); | |
540 } | |
541 /* Set size to zero so we won't write it again if the closer sets | |
542 die_p and kicks us */ | |
543 s->size = 0; | |
428 | 544 } |
545 | |
546 if (s->die_p) | |
547 break; | |
548 } | |
549 | |
550 return 0; | |
551 } | |
552 | |
553 static Lisp_Object | |
554 make_ntpipe_output_stream (HANDLE hpipe, LPARAM param) | |
555 { | |
556 Lstream *lstr = Lstream_new (lstream_ntpipe_shove, "w"); | |
771 | 557 struct ntpipe_shove_stream *s = NTPIPE_SHOVE_STREAM_DATA (lstr); |
428 | 558 DWORD thread_id_unused; |
559 | |
560 s->die_p = 0; | |
561 s->error_p = FALSE; | |
562 s->hpipe = hpipe; | |
563 s->user_data = param; | |
564 | |
565 /* Create reader thread. This could fail, so do not | |
566 create the event until thread is created */ | |
567 s->hthread = CreateThread (NULL, 0, shove_thread, (LPVOID)s, | |
568 CREATE_SUSPENDED, &thread_id_unused); | |
569 if (s->hthread == NULL) | |
570 { | |
571 Lstream_delete (lstr); | |
572 return Qnil; | |
573 } | |
574 | |
442 | 575 /* Set the priority of the thread higher so we don't end up waiting |
576 on it to send things. */ | |
577 if (!SetThreadPriority (s->hthread, THREAD_PRIORITY_HIGHEST)) | |
578 { | |
579 CloseHandle (s->hthread); | |
580 Lstream_delete (lstr); | |
581 return Qnil; | |
582 } | |
583 | |
428 | 584 /* hev_thread is an auto-reset event, initially nonsignaled */ |
771 | 585 s->hev_thread = qxeCreateEvent (NULL, FALSE, FALSE, NULL); |
428 | 586 |
587 /* Now let it go */ | |
588 ResumeThread (s->hthread); | |
589 | |
590 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE; | |
793 | 591 return wrap_lstream (lstr); |
428 | 592 } |
593 | |
594 static LPARAM | |
595 get_ntpipe_output_stream_param (Lstream *stream) | |
596 { | |
771 | 597 struct ntpipe_shove_stream *s = NTPIPE_SHOVE_STREAM_DATA(stream); |
428 | 598 return s->user_data; |
599 } | |
600 | |
665 | 601 static Bytecount |
462 | 602 ntpipe_shove_writer (Lstream *stream, const unsigned char *data, |
665 | 603 Bytecount size) |
428 | 604 { |
771 | 605 struct ntpipe_shove_stream *s = NTPIPE_SHOVE_STREAM_DATA(stream); |
428 | 606 |
607 if (s->error_p) | |
608 return -1; | |
609 | |
610 s->blocking_p = !s->idle_p; | |
611 if (s->blocking_p) | |
612 return 0; | |
613 | |
614 if (size>MAX_SHOVE_BUFFER_SIZE) | |
615 return 0; | |
616 | |
617 memcpy (s->buffer, data, size); | |
618 s->size = size; | |
619 | |
620 /* Start output */ | |
621 InterlockedDecrement (&s->idle_p); | |
622 SetEvent (s->hev_thread); | |
442 | 623 /* Give it a chance to run -- this dramatically improves performance |
624 of things like crypt. */ | |
771 | 625 if (xSwitchToThread) /* not in Win9x */ |
442 | 626 (void) xSwitchToThread (); |
428 | 627 return size; |
628 } | |
629 | |
630 static int | |
631 ntpipe_shove_was_blocked_p (Lstream *stream) | |
632 { | |
771 | 633 struct ntpipe_shove_stream *s = NTPIPE_SHOVE_STREAM_DATA(stream); |
428 | 634 return s->blocking_p; |
635 } | |
636 | |
637 static int | |
638 ntpipe_shove_closer (Lstream *stream) | |
639 { | |
771 | 640 struct ntpipe_shove_stream *s = NTPIPE_SHOVE_STREAM_DATA(stream); |
428 | 641 |
642 /* Force thread stop */ | |
643 InterlockedIncrement (&s->die_p); | |
644 | |
771 | 645 /* Close pipe handle, possibly breaking it */ |
646 CloseHandle (s->hpipe); | |
647 | |
442 | 648 /* Thread will end upon unblocking. If it's already unblocked this will |
649 do nothing, but the thread won't look at die_p until it's written any | |
650 pending output. */ | |
428 | 651 SetEvent (s->hev_thread); |
652 | |
653 /* Wait while thread terminates */ | |
654 WaitForSingleObject (s->hthread, INFINITE); | |
442 | 655 |
656 /* Close the thread handle */ | |
428 | 657 CloseHandle (s->hthread); |
658 | |
659 /* Destroy the event */ | |
660 CloseHandle (s->hev_thread); | |
661 | |
662 return 0; | |
663 } | |
664 | |
665 static void | |
666 init_shove_stream (void) | |
667 { | |
668 LSTREAM_HAS_METHOD (ntpipe_shove, writer); | |
669 LSTREAM_HAS_METHOD (ntpipe_shove, was_blocked_p); | |
670 LSTREAM_HAS_METHOD (ntpipe_shove, closer); | |
671 } | |
672 | |
673 /************************************************************************/ | |
674 /* Winsock I/O stream */ | |
675 /************************************************************************/ | |
676 | |
677 #define WINSOCK_READ_BUFFER_SIZE 1024 | |
678 | |
679 struct winsock_stream | |
680 { | |
681 LPARAM user_data; /* Any user data stored in the stream object */ | |
682 SOCKET s; /* Socket handle (which is a Win32 handle) */ | |
683 OVERLAPPED ov; /* Overlapped I/O structure */ | |
647 | 684 void *buffer; /* Buffer. */ |
685 DWORD bufsize; /* Number of bytes last read */ | |
1204 | 686 DWORD charbpos; /* Position in buffer for next fetch */ |
428 | 687 unsigned int error_p :1; /* I/O Error seen */ |
688 unsigned int eof_p :1; /* EOF Error seen */ | |
689 unsigned int pending_p :1; /* There is a pending I/O operation */ | |
690 unsigned int blocking_p :1; /* Last write attempt would block */ | |
691 }; | |
692 | |
693 #define WINSOCK_STREAM_DATA(stream) LSTREAM_TYPE_DATA (stream, winsock) | |
694 | |
771 | 695 DEFINE_LSTREAM_IMPLEMENTATION ("winsock", winsock); |
428 | 696 |
697 static void | |
698 winsock_initiate_read (struct winsock_stream *str) | |
699 { | |
700 ResetEvent (str->ov.hEvent); | |
665 | 701 str->charbpos = 0; |
428 | 702 |
703 if (!ReadFile ((HANDLE)str->s, str->buffer, WINSOCK_READ_BUFFER_SIZE, | |
704 &str->bufsize, &str->ov)) | |
705 { | |
706 if (GetLastError () == ERROR_IO_PENDING) | |
707 str->pending_p = 1; | |
708 else if (GetLastError () == ERROR_HANDLE_EOF) | |
709 str->eof_p = 1; | |
710 else | |
711 str->error_p = 1; | |
712 } | |
713 else if (str->bufsize == 0) | |
714 str->eof_p = 1; | |
715 } | |
716 | |
665 | 717 static Bytecount |
718 winsock_reader (Lstream *stream, unsigned char *data, Bytecount size) | |
428 | 719 { |
720 struct winsock_stream *str = WINSOCK_STREAM_DATA (stream); | |
721 | |
722 /* If the current operation is not yet complete, there's nothing to | |
723 give back */ | |
724 if (str->pending_p) | |
725 { | |
726 if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT) | |
727 { | |
728 errno = EAGAIN; | |
729 return -1; | |
730 } | |
731 else | |
732 { | |
1204 | 733 if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &str->bufsize, |
734 TRUE)) | |
428 | 735 { |
736 if (GetLastError() == ERROR_HANDLE_EOF) | |
737 str->bufsize = 0; | |
738 else | |
739 str->error_p = 1; | |
740 } | |
741 if (str->bufsize == 0) | |
742 str->eof_p = 1; | |
743 str->pending_p = 0; | |
744 } | |
745 } | |
746 | |
747 if (str->eof_p) | |
748 return 0; | |
749 if (str->error_p) | |
750 return -1; | |
751 | |
752 /* Return as much of buffer as we have */ | |
665 | 753 size = min (size, (Bytecount) (str->bufsize - str->charbpos)); |
771 | 754 memcpy (data, (void *) ((BYTE *) str->buffer + str->charbpos), size); |
665 | 755 str->charbpos += size; |
428 | 756 |
757 /* Read more if buffer is exhausted */ | |
665 | 758 if (str->bufsize == str->charbpos) |
428 | 759 winsock_initiate_read (str); |
760 | |
761 return size; | |
762 } | |
763 | |
665 | 764 static Bytecount |
462 | 765 winsock_writer (Lstream *stream, const unsigned char *data, |
665 | 766 Bytecount size) |
428 | 767 { |
768 struct winsock_stream *str = WINSOCK_STREAM_DATA (stream); | |
769 | |
770 if (str->pending_p) | |
771 { | |
772 if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT) | |
773 { | |
774 str->blocking_p = 1; | |
775 return -1; | |
776 } | |
777 else | |
778 { | |
779 DWORD dw_unused; | |
1204 | 780 if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &dw_unused, |
781 TRUE)) | |
428 | 782 str->error_p = 1; |
783 str->pending_p = 0; | |
784 } | |
785 } | |
786 | |
787 str->blocking_p = 0; | |
788 | |
789 if (str->error_p) | |
790 return -1; | |
791 | |
792 if (size == 0) | |
793 return 0; | |
794 | |
558 | 795 ResetEvent (str->ov.hEvent); |
796 | |
797 /* According to WriteFile docs, we must hold onto the data we pass to it | |
798 and not make any changes until it finishes -- which may not be until | |
799 the next time we get here, since we use asynchronous I/O. We have | |
800 in fact seen data loss as a result of not doing this. */ | |
801 str->buffer = xrealloc (str->buffer, size); | |
802 memcpy (str->buffer, data, size); | |
803 | |
560 | 804 /* According to MSDN WriteFile docs, the fourth parameter cannot be NULL |
805 on Win95 even when doing an overlapped operation, as we are, where | |
806 the return value through that parameter is not meaningful. */ | |
807 if (WriteFile ((HANDLE)str->s, str->buffer, size, &str->bufsize, | |
558 | 808 &str->ov) |
809 || GetLastError() == ERROR_IO_PENDING) | |
810 str->pending_p = 1; | |
811 else | |
812 str->error_p = 1; | |
428 | 813 |
814 return str->error_p ? -1 : size; | |
815 } | |
816 | |
817 static int | |
818 winsock_closer (Lstream *lstr) | |
819 { | |
820 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr); | |
821 | |
822 if (lstr->flags & LSTREAM_FL_READ) | |
823 shutdown (str->s, 0); | |
824 else | |
825 shutdown (str->s, 1); | |
826 | |
986 | 827 closesocket (str->s); |
428 | 828 if (str->pending_p) |
829 WaitForSingleObject (str->ov.hEvent, INFINITE); | |
830 | |
558 | 831 if (str->buffer) |
560 | 832 { |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
833 xfree (str->buffer); |
560 | 834 str->buffer = 0; |
835 } | |
428 | 836 |
837 CloseHandle (str->ov.hEvent); | |
838 return 0; | |
839 } | |
840 | |
841 static int | |
842 winsock_was_blocked_p (Lstream *stream) | |
843 { | |
844 struct winsock_stream *str = WINSOCK_STREAM_DATA (stream); | |
845 return str->blocking_p; | |
846 } | |
847 | |
848 static Lisp_Object | |
442 | 849 make_winsock_stream_1 (SOCKET s, LPARAM param, const char *mode) |
428 | 850 { |
851 Lstream *lstr = Lstream_new (lstream_winsock, mode); | |
852 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr); | |
853 | |
558 | 854 xzero (*str); |
428 | 855 str->s = s; |
856 str->user_data = param; | |
857 | |
771 | 858 str->ov.hEvent = qxeCreateEvent (NULL, TRUE, FALSE, NULL); |
428 | 859 |
860 if (lstr->flags & LSTREAM_FL_READ) | |
861 { | |
862 str->buffer = xmalloc (WINSOCK_READ_BUFFER_SIZE); | |
863 winsock_initiate_read (str); | |
864 } | |
865 | |
866 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE; | |
793 | 867 return wrap_lstream (lstr); |
428 | 868 } |
869 | |
870 static Lisp_Object | |
871 make_winsock_input_stream (SOCKET s, LPARAM param) | |
872 { | |
873 return make_winsock_stream_1 (s, param, "r"); | |
874 } | |
875 | |
876 static Lisp_Object | |
877 make_winsock_output_stream (SOCKET s, LPARAM param) | |
878 { | |
879 return make_winsock_stream_1 (s, param, "w"); | |
880 } | |
881 | |
882 static HANDLE | |
883 get_winsock_stream_waitable (Lstream *lstr) | |
884 { | |
885 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr); | |
886 return str->ov.hEvent; | |
887 } | |
888 | |
889 static LPARAM | |
890 get_winsock_stream_param (Lstream *lstr) | |
891 { | |
892 struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr); | |
893 return str->user_data; | |
894 } | |
895 | |
896 static void | |
897 init_winsock_stream (void) | |
898 { | |
899 LSTREAM_HAS_METHOD (winsock, reader); | |
900 LSTREAM_HAS_METHOD (winsock, writer); | |
901 LSTREAM_HAS_METHOD (winsock, closer); | |
902 LSTREAM_HAS_METHOD (winsock, was_blocked_p); | |
903 } | |
853 | 904 #endif /* ! CYGWIN */ |
428 | 905 |
906 /************************************************************************/ | |
907 /* Dispatch queue management */ | |
908 /************************************************************************/ | |
909 | |
910 static int | |
771 | 911 mswindows_user_event_p (Lisp_Event *sevt) |
428 | 912 { |
913 return (sevt->event_type == key_press_event | |
914 || sevt->event_type == button_press_event | |
915 || sevt->event_type == button_release_event | |
916 || sevt->event_type == misc_user_event); | |
917 } | |
918 | |
919 /* | |
920 * Add an emacs event to the proper dispatch queue | |
921 */ | |
442 | 922 void |
428 | 923 mswindows_enqueue_dispatch_event (Lisp_Object event) |
924 { | |
1204 | 925 int user_p = mswindows_user_event_p (XEVENT (event)); |
926 if (user_p) | |
927 enqueue_dispatch_event (event); | |
928 else | |
929 enqueue_event (event, &mswindows_s_dispatch_event_queue, | |
930 &mswindows_s_dispatch_event_queue_tail); | |
428 | 931 |
932 /* Avoid blocking on WaitMessage */ | |
771 | 933 qxePostMessage (NULL, XM_BUMPQUEUE, 0, 0); |
428 | 934 } |
935 | |
936 /* | |
937 * Add a misc-user event to the dispatch queue. | |
938 */ | |
939 void | |
940 mswindows_enqueue_misc_user_event (Lisp_Object channel, Lisp_Object function, | |
941 Lisp_Object object) | |
942 { | |
943 Lisp_Object event = Fmake_event (Qnil, Qnil); | |
964 | 944 |
945 XSET_EVENT_TYPE (event, misc_user_event); | |
946 XSET_EVENT_CHANNEL (event, channel); | |
947 XSET_EVENT_TIMESTAMP (event, GetTickCount()); | |
1204 | 948 XSET_EVENT_MISC_USER_FUNCTION (event, function); |
949 XSET_EVENT_MISC_USER_OBJECT (event, object); | |
428 | 950 |
951 mswindows_enqueue_dispatch_event (event); | |
952 } | |
953 | |
954 void | |
440 | 955 mswindows_enqueue_magic_event (HWND hwnd, UINT msg) |
428 | 956 { |
957 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); | |
964 | 958 |
1204 | 959 XSET_EVENT_CHANNEL (emacs_event, hwnd ? mswindows_find_frame (hwnd) : Qnil); |
964 | 960 XSET_EVENT_TIMESTAMP (emacs_event, GetMessageTime ()); |
961 XSET_EVENT_TYPE (emacs_event, magic_event); | |
1204 | 962 XSET_EVENT_MAGIC_MSWINDOWS_EVENT (emacs_event, msg); |
428 | 963 |
964 mswindows_enqueue_dispatch_event (emacs_event); | |
965 } | |
966 | |
967 static void | |
771 | 968 mswindows_enqueue_process_event (Lisp_Process *p) |
428 | 969 { |
970 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); | |
793 | 971 Lisp_Object process = wrap_process (p); |
972 | |
964 | 973 XSET_EVENT_TYPE (emacs_event, process_event); |
1204 | 974 XSET_EVENT_TIMESTAMP (emacs_event, GetTickCount ()); |
975 XSET_EVENT_PROCESS_PROCESS (emacs_event, process); | |
428 | 976 |
977 mswindows_enqueue_dispatch_event (emacs_event); | |
978 } | |
979 | |
980 static void | |
442 | 981 mswindows_enqueue_mouse_button_event (HWND hwnd, UINT msg, POINTS where, |
982 int mods, DWORD when) | |
428 | 983 { |
442 | 984 int downp = (msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || |
985 msg == WM_RBUTTONDOWN); | |
428 | 986 |
1622 | 987 /* Wheel rotation amount: positive is away from user, negative towards user */ |
988 int delta = (short) HIWORD (mods); | |
989 | |
428 | 990 /* We always use last message time, because mouse button |
991 events may get delayed, and XEmacs double click | |
992 recognition will fail */ | |
993 | |
994 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); | |
442 | 995 |
996 mswindows_handle_sticky_modifiers (0, 0, downp, 0); | |
964 | 997 |
998 if (downp) | |
999 { | |
1000 XSET_EVENT_TYPE (emacs_event, button_press_event); | |
1001 } | |
1002 else | |
1003 { | |
1004 XSET_EVENT_TYPE (emacs_event, button_release_event); | |
1005 } | |
1006 | |
1007 XSET_EVENT_CHANNEL (emacs_event, mswindows_find_frame (hwnd)); | |
1008 XSET_EVENT_TIMESTAMP (emacs_event, when); | |
1204 | 1009 XSET_EVENT_BUTTON_BUTTON (emacs_event, |
1622 | 1010 (msg==WM_LBUTTONDOWN || msg==WM_LBUTTONUP) ? 1 : |
1011 (msg==WM_MBUTTONDOWN || msg==WM_MBUTTONUP) ? 2 : | |
1012 (msg==WM_RBUTTONDOWN || msg==WM_RBUTTONUP) ? 3 : | |
1013 (msg==WM_MOUSEWHEEL && delta>0) ? 4 : 5); | |
1204 | 1014 XSET_EVENT_BUTTON_X (emacs_event, where.x); |
1015 XSET_EVENT_BUTTON_Y (emacs_event, where.y); | |
1016 XSET_EVENT_BUTTON_MODIFIERS (emacs_event, | |
1017 mswindows_modifier_state (NULL, mods, 0)); | |
442 | 1018 |
1019 if (downp) | |
428 | 1020 { |
1021 SetCapture (hwnd); | |
1022 /* we need this to make sure the main window regains the focus | |
1023 from control subwindows */ | |
1024 if (GetFocus() != hwnd) | |
1025 { | |
1026 SetFocus (hwnd); | |
1027 mswindows_enqueue_magic_event (hwnd, WM_SETFOCUS); | |
1028 } | |
1029 } | |
1030 else | |
1031 { | |
1032 ReleaseCapture (); | |
1033 } | |
1034 | |
1035 mswindows_enqueue_dispatch_event (emacs_event); | |
1036 } | |
1037 | |
771 | 1038 static Lisp_Object |
428 | 1039 mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods) |
1040 { | |
1041 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); | |
964 | 1042 |
1043 XSET_EVENT_CHANNEL (emacs_event, mswindows_find_console(hwnd)); | |
1044 XSET_EVENT_TIMESTAMP (emacs_event, GetMessageTime()); | |
1045 XSET_EVENT_TYPE (emacs_event, key_press_event); | |
1204 | 1046 XSET_EVENT_KEY_KEYSYM (emacs_event, keysym); |
1047 XSET_EVENT_KEY_MODIFIERS (emacs_event, mods); | |
428 | 1048 mswindows_enqueue_dispatch_event (emacs_event); |
771 | 1049 return emacs_event; |
428 | 1050 } |
1051 | |
1052 /* | |
1053 * Remove and return the first emacs event on the dispatch queue. | |
1054 * Give a preference to user events over non-user ones. | |
1055 */ | |
1056 static Lisp_Object | |
442 | 1057 mswindows_dequeue_dispatch_event (void) |
428 | 1058 { |
1204 | 1059 assert (!NILP (dispatch_event_queue) || |
1060 !NILP (mswindows_s_dispatch_event_queue)); | |
1061 | |
1062 if (!NILP (dispatch_event_queue)) | |
1063 return dequeue_dispatch_event (); | |
1064 else | |
1065 return dequeue_event (&mswindows_s_dispatch_event_queue, | |
1066 &mswindows_s_dispatch_event_queue_tail); | |
428 | 1067 } |
1068 | |
853 | 1069 #ifndef CYGWIN |
428 | 1070 /************************************************************************/ |
1071 /* Waitable handles manipulation */ | |
1072 /************************************************************************/ | |
1073 static int | |
1074 find_waitable_handle (HANDLE h) | |
1075 { | |
1076 int i; | |
1077 for (i = 0; i < mswindows_waitable_count; ++i) | |
1078 if (mswindows_waitable_handles[i] == h) | |
1079 return i; | |
1080 | |
1081 return -1; | |
1082 } | |
1083 | |
1084 static BOOL | |
1085 add_waitable_handle (HANDLE h) | |
1086 { | |
1087 assert (find_waitable_handle (h) < 0); | |
1088 if (mswindows_waitable_count == MAX_WAITABLE) | |
1089 return FALSE; | |
1090 | |
1091 mswindows_waitable_handles [mswindows_waitable_count++] = h; | |
1092 return TRUE; | |
1093 } | |
1094 | |
1095 static void | |
1096 remove_waitable_handle (HANDLE h) | |
1097 { | |
1098 int ix = find_waitable_handle (h); | |
1099 if (ix < 0) | |
1100 return; | |
1101 | |
1102 mswindows_waitable_handles [ix] = | |
1103 mswindows_waitable_handles [--mswindows_waitable_count]; | |
1104 } | |
853 | 1105 |
1106 #endif /* CYGWIN */ | |
428 | 1107 |
791 | 1108 /* |
1109 * Given a lisp process pointer remove the corresponding process handle | |
1110 * from mswindows_waitable_handles if it is in it. Normally the handle is | |
1111 * removed when the process terminates, but if the lisp process structure | |
1112 * is deleted before the process terminates we must delete the process | |
1113 * handle since it will be invalid and will cause the wait to fail | |
1114 */ | |
1115 void | |
2286 | 1116 mswindows_unwait_process (Lisp_Process *UNUSED_IF_CYGWIN (p)) |
791 | 1117 { |
853 | 1118 #ifndef CYGWIN |
791 | 1119 remove_waitable_handle (get_nt_process_handle (p)); |
853 | 1120 #endif /* CYGWIN */ |
791 | 1121 } |
1122 | |
428 | 1123 |
1124 /************************************************************************/ | |
1125 /* Event pump */ | |
1126 /************************************************************************/ | |
1127 | |
771 | 1128 int |
1129 mswindows_window_is_xemacs (HWND hwnd) | |
1130 { | |
1131 /* GetClassName will truncate a longer class name. By adding one | |
1132 extra character, we are forcing textual comparison to fail | |
1133 if the name is longer than XEMACS_CLASS */ | |
1134 Extbyte class_name_buf[sizeof (XEMACS_CLASS) + 2]; | |
1135 | |
1136 /* Use GetClassNameA because XEMACS_CLASS is not in Unicode format. */ | |
1137 if (!GetClassNameA (hwnd, class_name_buf, sizeof (class_name_buf) - 1)) | |
1138 return 0; | |
1139 | |
1140 return !ascii_strcasecmp (class_name_buf, XEMACS_CLASS); | |
1141 } | |
1142 | |
428 | 1143 void |
1144 mswindows_unmodalize_signal_maybe (void) | |
1145 { | |
853 | 1146 mswindows_error_caught_in_modal_loop = 0; |
428 | 1147 } |
1148 | |
1149 /* | |
1150 * This is an unsafe part of event pump, guarded by | |
1151 * condition_case. See mswindows_pump_outstanding_events | |
1152 */ | |
1153 static Lisp_Object | |
2286 | 1154 mswindows_unsafe_pump_events (void *UNUSED (arg)) |
428 | 1155 { |
1156 /* This function can call lisp */ | |
1157 Lisp_Object event = Fmake_event (Qnil, Qnil); | |
1158 struct gcpro gcpro1; | |
1159 int do_redisplay = 0; | |
1160 GCPRO1 (event); | |
1161 | |
1268 | 1162 while (detect_input_pending (1)) |
428 | 1163 { |
1164 Fnext_event (event, Qnil); | |
1165 Fdispatch_event (event); | |
1166 do_redisplay = 1; | |
1167 } | |
1168 | |
1169 if (do_redisplay) | |
1170 redisplay (); | |
1171 | |
1172 Fdeallocate_event (event); | |
1173 UNGCPRO; | |
1174 | |
1175 /* Qt becomes return value of mswindows_pump_outstanding_events | |
1176 once we get here */ | |
1177 return Qt; | |
1178 } | |
1179 | |
1180 /* | |
1181 * This function pumps emacs events, while available, by using | |
1182 * next_message/dispatch_message loop. Errors are trapped around | |
1183 * the loop so the function always returns. | |
1184 * | |
1185 * Windows message queue is not looked into during the call, | |
1186 * neither are waitable handles checked. The function pumps | |
1187 * thus only dispatch events already queued, as well as those | |
1188 * resulted in dispatching thereof. This is done by setting | |
1268 | 1189 * in_modal_loop to nonzero. |
428 | 1190 * |
1191 * Return value is Qt if no errors was trapped, or Qunbound if | |
1192 * there was an error. | |
1193 * | |
853 | 1194 * In case of error, a warning is issued and the module local variable |
1195 * mswindows_error_caught_in_modal_loop is set to non-zero. Thus, | |
1196 * Windows internal modal loops are protected against throws, which | |
1197 * are proven to corrupt internal Windows structures. | |
428 | 1198 * |
1199 * In case of success, mswindows_error_caught_in_modal_loop is | |
853 | 1200 * assigned 0. |
428 | 1201 * |
1202 * If the value of mswindows_error_caught_in_modal_loop is not | |
853 | 1203 * zero already upon entry, the function just returns non-nil. |
428 | 1204 * This situation means that a new event has been queued while |
1205 * in cancel mode. The event will be dequeued on the next regular | |
1206 * call of next-event; the pump is off since error is caught. | |
1207 * The caller must *unconditionally* cancel modal loop if the | |
1208 * value returned by this function is nil. Otherwise, everything | |
1209 * will become frozen until the modal loop exits under normal | |
853 | 1210 * condition (scrollbar drag is released, menu closed etc.) */ |
428 | 1211 Lisp_Object |
1212 mswindows_pump_outstanding_events (void) | |
1213 { | |
1214 /* This function can call lisp */ | |
1215 | |
1216 Lisp_Object result = Qt; | |
1217 struct gcpro gcpro1; | |
1218 GCPRO1 (result); | |
1219 | |
853 | 1220 if (!mswindows_error_caught_in_modal_loop) |
1268 | 1221 result = event_stream_protect_modal_loop |
1222 ("Error during event handling", mswindows_unsafe_pump_events, 0, 0); | |
428 | 1223 UNGCPRO; |
1268 | 1224 if (UNBOUNDP (result)) |
1225 mswindows_error_caught_in_modal_loop = 1; | |
428 | 1226 return result; |
1227 } | |
1228 | |
440 | 1229 /* |
428 | 1230 * This is a special flavor of the mswindows_need_event function, |
1231 * used while in event pump. Actually, there is only kind of events | |
1232 * allowed while in event pump: a timer. An attempt to fetch any | |
1233 * other event leads to a deadlock, as there's no source of user input | |
1234 * ('cause event pump mirrors windows modal loop, which is a sole | |
1235 * owner of thread message queue). | |
1236 * | |
1237 * To detect this, we use a counter of active timers, and allow | |
1238 * fetching WM_TIMER messages. Instead of trying to fetch a WM_TIMER | |
1239 * which will never come when there are no pending timers, which leads | |
1240 * to deadlock, we simply signal an error. | |
487 | 1241 * |
1242 * It might be possible to combine this with mswindows_drain_windows_queue | |
1243 * which fetches events when not in a modal loop. It's not clear | |
1244 * whether the result would be more complex than is justified. | |
428 | 1245 */ |
1246 static void | |
1247 mswindows_need_event_in_modal_loop (int badly_p) | |
1248 { | |
1249 MSG msg; | |
1250 | |
1251 /* Check if already have one */ | |
1204 | 1252 if (!NILP (dispatch_event_queue) |
428 | 1253 || !NILP (mswindows_s_dispatch_event_queue)) |
1254 return; | |
1255 | |
1256 /* No event is ok */ | |
1257 if (!badly_p) | |
1258 return; | |
1259 | |
1204 | 1260 /* We do not check the user queue, because timers go to _s_ */ |
428 | 1261 while (NILP (mswindows_s_dispatch_event_queue)) |
1262 { | |
1263 /* We'll deadlock if go waiting */ | |
1264 if (mswindows_pending_timers_count == 0) | |
1204 | 1265 invalid_operation |
1266 ("Deadlock due to an attempt to call next-event in a wrong context", | |
1267 Qunbound); | |
428 | 1268 |
1269 /* Fetch and dispatch any pending timers */ | |
771 | 1270 if (qxeGetMessage (&msg, NULL, WM_TIMER, WM_TIMER) > 0) |
1271 qxeDispatchMessage (&msg); | |
428 | 1272 } |
1273 } | |
1274 | |
1268 | 1275 /* BADLY_P non-zero means we were called from mswindows_need_event(1). It |
1276 only matters when we are in a modal loop, and causes us to fetch timer | |
1277 events (the only kinds we can fetch in such a case). | |
1278 */ | |
1279 static void | |
1280 mswindows_drain_windows_queue (int badly_p) | |
1281 { | |
1282 MSG msg; | |
1283 | |
1284 if (in_modal_loop) | |
1285 mswindows_need_event_in_modal_loop (badly_p); | |
1286 else | |
1287 while (qxePeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) | |
1288 { | |
1289 #ifdef HAVE_DIALOGS | |
1290 /* Don't translate messages destined for a dialog box, this | |
1291 makes keyboard traversal work. I think?? */ | |
1292 if (mswindows_is_dialog_msg (&msg)) | |
1293 { | |
1294 mswindows_unmodalize_signal_maybe (); | |
1295 continue; | |
1296 } | |
1297 #endif /* HAVE_DIALOGS */ | |
1298 | |
1299 /* We have to translate messages that are not sent to an XEmacs | |
1300 frame. This is so that key presses work ok in things like | |
1301 edit fields. However, we *musn't* translate message for XEmacs | |
1302 frames as this is handled in the wnd proc. | |
1303 We also have to avoid generating paint magic events for windows | |
1304 that aren't XEmacs frames */ | |
1305 | |
1306 if (!mswindows_window_is_xemacs (msg.hwnd)) | |
1307 TranslateMessage (&msg); | |
1308 else if (msg.message == WM_PAINT) | |
1309 { | |
1310 struct mswindows_frame *msframe; | |
1311 | |
1312 /* hdc will be NULL unless this is a subwindow - in which case we | |
1313 shouldn't have received a paint message for it here. */ | |
1314 assert (msg.wParam == 0); | |
1315 | |
1316 /* Queue a magic event for handling when safe */ | |
1317 msframe = | |
1318 FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (msg.hwnd))); | |
1319 if (!msframe->paint_pending) | |
1320 { | |
1321 msframe->paint_pending = 1; | |
1322 mswindows_enqueue_magic_event (msg.hwnd, WM_PAINT); | |
1323 } | |
1324 /* Don't dispatch. WM_PAINT is always the last message in the | |
1325 queue so it's OK to just return. */ | |
1326 return; | |
1327 } | |
1328 qxeDispatchMessage (&msg); | |
1329 mswindows_unmodalize_signal_maybe (); | |
1330 } | |
1331 } | |
1332 | |
1333 static void | |
1334 emacs_mswindows_drain_queue (void) | |
1335 { | |
1318 | 1336 /* This can call Lisp */ |
1268 | 1337 mswindows_drain_windows_queue (0); |
1338 #ifdef HAVE_TTY | |
1339 drain_tty_devices (); | |
1340 #endif | |
1341 } | |
1342 | |
428 | 1343 /* |
1344 * This drains the event queue and fills up two internal queues until | |
1345 * an event of a type specified by USER_P is retrieved. | |
1346 * | |
1347 * | |
1348 * Used by emacs_mswindows_event_pending_p and emacs_mswindows_next_event | |
1349 */ | |
1350 static void | |
1351 mswindows_need_event (int badly_p) | |
1352 { | |
1204 | 1353 while (NILP (dispatch_event_queue) |
428 | 1354 && NILP (mswindows_s_dispatch_event_queue)) |
1355 { | |
853 | 1356 #ifdef CYGWIN |
428 | 1357 int i; |
647 | 1358 int active; |
428 | 1359 SELECT_TYPE temp_mask = input_wait_mask; |
1360 EMACS_TIME sometime; | |
1361 EMACS_SELECT_TIME select_time_to_block, *pointer_to_this; | |
1362 | |
1363 if (badly_p) | |
1364 pointer_to_this = 0; | |
1365 else | |
1366 { | |
1367 EMACS_SET_SECS_USECS (sometime, 0, 0); | |
1368 EMACS_TIME_TO_SELECT_TIME (sometime, select_time_to_block); | |
1369 pointer_to_this = &select_time_to_block; | |
1268 | 1370 if (in_modal_loop) |
534 | 1371 /* In modal loop with badly_p false, don't care about |
1372 Windows events. */ | |
1373 FD_CLR (windows_fd, &temp_mask); | |
428 | 1374 } |
1375 | |
1292 | 1376 mswindows_is_blocking = 1; |
428 | 1377 active = select (MAXDESC, &temp_mask, 0, 0, pointer_to_this); |
1292 | 1378 mswindows_is_blocking = 0; |
428 | 1379 |
1380 if (active == 0) | |
1381 { | |
1382 assert (!badly_p); | |
1383 return; /* timeout */ | |
1384 } | |
1385 else if (active > 0) | |
1386 { | |
1387 if (FD_ISSET (windows_fd, &temp_mask)) | |
1268 | 1388 mswindows_drain_windows_queue (badly_p); |
442 | 1389 else |
428 | 1390 { |
442 | 1391 #ifdef HAVE_TTY |
1392 /* Look for a TTY event */ | |
1204 | 1393 for (i = 0; i < MAXDESC; i++) |
428 | 1394 { |
442 | 1395 /* To avoid race conditions (among other things, an infinite |
1396 loop when called from Fdiscard_input()), we must return | |
1397 user events ahead of process events. */ | |
1398 if (FD_ISSET (i, &temp_mask) && FD_ISSET (i, &tty_only_mask)) | |
428 | 1399 { |
1204 | 1400 struct console *c = |
1401 find_tty_or_stream_console_from_fd (i); | |
442 | 1402 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); |
771 | 1403 Lisp_Event *event = XEVENT (emacs_event); |
1404 | |
442 | 1405 assert (c); |
771 | 1406 if (read_event_from_tty_or_stream_desc (event, c)) |
442 | 1407 { |
1408 mswindows_enqueue_dispatch_event (emacs_event); | |
1409 return; | |
1410 } | |
428 | 1411 } |
1412 } | |
1413 #endif | |
442 | 1414 /* Look for a process event */ |
1204 | 1415 for (i = 0; i < MAXDESC; i++) |
428 | 1416 { |
442 | 1417 if (FD_ISSET (i, &temp_mask)) |
428 | 1418 { |
442 | 1419 if (FD_ISSET (i, &process_only_mask)) |
1420 { | |
1421 Lisp_Process *p = | |
1204 | 1422 get_process_from_usid (FD_TO_USID (i)); |
442 | 1423 |
1424 mswindows_enqueue_process_event (p); | |
1425 } | |
1426 else | |
1427 { | |
1428 /* We might get here when a fake event came | |
1429 through a signal. Return a dummy event, so | |
1430 that a cycle of the command loop will | |
1431 occur. */ | |
1432 drain_signal_event_pipe (); | |
1433 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); | |
1434 } | |
428 | 1435 } |
1436 } | |
1437 } | |
1438 } | |
771 | 1439 else if (active == -1) |
428 | 1440 { |
1441 if (errno != EINTR) | |
1442 { | |
1443 /* something bad happened */ | |
1204 | 1444 assert (0); |
428 | 1445 } |
1446 } | |
1447 else | |
1448 { | |
1204 | 1449 assert (0); |
428 | 1450 } |
853 | 1451 #else /* not CYGWIN */ |
428 | 1452 /* Now try getting a message or process event */ |
647 | 1453 DWORD active; |
487 | 1454 DWORD what_events; |
1268 | 1455 if (in_modal_loop) |
534 | 1456 /* In a modal loop, only look for timer events, and only if |
1457 we really need one. */ | |
1458 { | |
1459 if (badly_p) | |
1460 what_events = QS_TIMER; | |
1461 else | |
1462 what_events = 0; | |
1463 } | |
487 | 1464 else |
534 | 1465 /* Look for any event */ |
1466 what_events = QS_ALLINPUT; | |
487 | 1467 |
771 | 1468 /* |
1469 #### YUCK YUCK YUCK!!!! | |
1470 | |
1471 When running under a debugger, every time I hit F12 (which for me | |
1472 is mapped to right-brace) I hit a breakpoint inside of Windows! | |
1473 | |
1474 NTDLL! DbgBreakPoint@0 address 0x77f9eea9 | |
1475 KERNEL32! BaseAttachComplete@4 + 41 bytes | |
1476 KERNEL32! BaseAttachCompleteThunk@0 + 19 bytes | |
1477 USER32! MsgWaitForMultipleObjectsEx@20 + 224 bytes | |
1478 USER32! MsgWaitForMultipleObjects@20 + 30 bytes | |
1479 | |
1480 Microsoft says: | |
1481 | |
1482 (Knowledge Base Q130667, PRB: F12 Causes Hard-Coded Breakpoint | |
1483 Exception When Debugging) | |
1484 | |
1485 CAUSE | |
1486 | |
1487 When the F12 key is pressed and the application in focus is being | |
1488 debugged, Windows NT calls a function similar to DebugBreak(), | |
1489 which executes a hard coded breakpoint instruction. The integrated | |
1490 debugger then traps the exception generated by this instruction. | |
1491 | |
1492 This behavior is intentional and occurs with other debuggers such | |
1493 as WinDbg from the Windows 32-bit SDK. | |
1494 | |
1495 RESOLUTION | |
1496 | |
1497 While there is no way to disable this functionality, it doesn't | |
1498 affect the application that's being debugged other than to pause | |
1499 debugging and change focus. You can continue debugging by pressing | |
1500 the F5 key. | |
1501 | |
1502 This can be annoying if you have an application that heavily uses | |
1503 the F12 key, so you may want to temporarily assign another key to | |
1504 handle the F12 key functionality in your program when debugging. | |
1505 | |
1506 STATUS | |
1507 | |
1508 This behavior is by design. | |
1509 | |
1510 | |
1511 However, elsewhere I found this: | |
1512 | |
1513 UserDebuggerHotKey | |
1514 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug | |
1515 | |
1516 Data type Range Default value | |
1517 REG_DWORD 0x0 - 0xFF 0x0 | |
1518 | |
1519 Description | |
1520 | |
1521 Specifies the key that, when pressed, establishes a breakpoint in | |
1522 code being debugged. | |
1523 | |
1524 The debugger interrupts code processing at the breakpoint so the | |
1525 programmer can examine a suspected problem. | |
1526 | |
1527 The key specified in this value only sets a breakpoint. It does | |
1528 not invoke the debugger (the debugger must be running before the | |
1529 key is pressed) and it does not switch the debugger to single-step | |
1530 mode. | |
1531 | |
1532 The value of this entry is a keyboard scan code. The default | |
1533 value, 0x0, represents the F12 key on a 101-key keyboard or the - | |
1534 (hyphen, VK_SUBTRACT) key on an 82-key keyboard. | |
1535 */ | |
1536 | |
853 | 1537 __try |
1538 { | |
923 | 1539 /* This fixes a long outstanding bug, where XEmacs would occasionally |
1540 * not redraw its window (or process other events) until "something | |
1541 * happened" - usually the mouse moving over a frame. | |
1542 * | |
1543 * The problem is that MsgWaitForMultipleObjects only checks to see | |
1544 * if NEW messages have been placed into the thread queue. So we | |
1545 * specifically check to see if the queue is empty (using PeekMessage | |
1546 * with the PM_NOREMOVE flag) before we wait. | |
1547 */ | |
1548 MSG msg; | |
1549 if (what_events == QS_ALLINPUT && badly_p && | |
1550 qxePeekMessage (&msg, 0, 0, 0, PM_NOREMOVE)) | |
1551 active = WAIT_OBJECT_0 + mswindows_waitable_count; | |
1552 else | |
1292 | 1553 { |
1554 mswindows_is_blocking = 1; | |
1555 active = MsgWaitForMultipleObjects (mswindows_waitable_count, | |
1556 mswindows_waitable_handles, | |
1557 FALSE, | |
1558 badly_p ? INFINITE : 0, | |
1559 what_events); | |
1560 mswindows_is_blocking = 0; | |
1561 } | |
853 | 1562 } |
1563 __except (GetExceptionCode () == EXCEPTION_BREAKPOINT ? | |
1564 EXCEPTION_CONTINUE_EXECUTION : | |
1565 EXCEPTION_CONTINUE_SEARCH) | |
1566 { | |
1567 } | |
442 | 1568 |
1569 /* This will assert if handle being waited for becomes abandoned. | |
1570 Not the case currently tho */ | |
1571 assert ((!badly_p && active == WAIT_TIMEOUT) || | |
1572 (active >= WAIT_OBJECT_0 && | |
1573 active <= WAIT_OBJECT_0 + mswindows_waitable_count)); | |
1574 | |
1575 if (active == WAIT_TIMEOUT) | |
1576 { | |
1577 /* No luck trying - just return what we've already got */ | |
1578 return; | |
1579 } | |
1580 else if (active == WAIT_OBJECT_0 + mswindows_waitable_count) | |
1268 | 1581 mswindows_drain_windows_queue (badly_p); |
442 | 1582 else |
1583 { | |
1584 int ix = active - WAIT_OBJECT_0; | |
1204 | 1585 |
1586 /* look for a stream console event; see | |
1587 emacs_mswindows_select_console below. */ | |
1588 LIST_LOOP_3 (porca_troia, Vconsole_list, vcontail) | |
442 | 1589 { |
1204 | 1590 struct console *con = XCONSOLE (porca_troia); |
1591 | |
1592 if (CONSOLE_STREAM_P (con)) | |
1593 { | |
1594 Lisp_Object instr = CONSOLE_STREAM_DATA (con)->instream; | |
1595 if (!NILP (instr) && !UNBOUNDP (instr) && | |
1596 get_ntpipe_input_stream_waitable (XLSTREAM (instr)) == | |
1597 mswindows_waitable_handles [ix]) | |
1598 { | |
1599 Ichar ch = Lstream_get_ichar (XLSTREAM (instr)); | |
1600 if (ch < 0) | |
1601 { | |
1602 /* deleting the console might not be safe right now | |
1603 ... */ | |
1604 enqueue_magic_eval_event (io_error_delete_console, | |
1605 porca_troia); | |
1606 /* but we definitely need to unselect it to avoid | |
1607 infinite loops reading EOF's */ | |
1608 Fconsole_disable_input (porca_troia); | |
1609 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); | |
1610 } | |
1611 else | |
1612 { | |
1613 Lisp_Object event = Fmake_event (Qnil, Qnil); | |
1614 /* Here we really do want to set the | |
1615 use_console_meta_flag because the char is from the | |
1616 TTY. */ | |
4780
2fd201d73a92
Call character_to_event on characters received from XIM, event-Xt.c
Aidan Kehoe <kehoea@parhasard.net>
parents:
4677
diff
changeset
|
1617 character_to_event (ch, XEVENT (event), con, |
2fd201d73a92
Call character_to_event on characters received from XIM, event-Xt.c
Aidan Kehoe <kehoea@parhasard.net>
parents:
4677
diff
changeset
|
1618 use_console_meta_flag, 1); |
1204 | 1619 XSET_EVENT_CHANNEL (event, porca_troia); |
1620 enqueue_dispatch_event (event); | |
1621 } | |
1622 break; | |
1623 } | |
1624 } | |
1625 } | |
1626 | |
1627 if (NILP (vcontail)) | |
1628 { /* no stream console event, look for process event */ | |
1629 /* First, try to find which process' output has signaled */ | |
1630 Lisp_Process *p = | |
1631 get_process_from_usid (HANDLE_TO_USID | |
1632 (mswindows_waitable_handles[ix])); | |
1633 if (p != NULL) | |
1634 /* Found a signaled process input handle */ | |
1635 mswindows_enqueue_process_event (p); | |
853 | 1636 else |
442 | 1637 { |
1204 | 1638 /* None. This means that the process handle itself has |
1639 signaled. Remove the handle from the wait vector, and | |
1640 make status_notify note the exited process. First | |
1641 find the process object if possible. */ | |
1642 LIST_LOOP_3 (vaffanculo, Vprocess_list, vproctail) | |
1643 if (get_nt_process_handle (XPROCESS (vaffanculo)) == | |
1644 mswindows_waitable_handles [ix]) | |
1645 break; | |
1646 mswindows_waitable_handles [ix] = | |
1647 mswindows_waitable_handles [--mswindows_waitable_count]; | |
1648 kick_status_notify (); | |
1649 /* We need to return a process event here so that (1) | |
1650 accept-process-output will return when called on this | |
1651 process, and (2) status notifications will happen in | |
1652 accept-process-output, sleep-for, and sit-for. */ | |
1653 if (!NILP (vproctail)) | |
1654 mswindows_enqueue_process_event (XPROCESS (vaffanculo)); | |
1655 else | |
1656 { | |
2500 | 1657 /* ABORT (); */ |
1204 | 1658 /* #### FUCKME! When can this happen? I hit this |
2500 | 1659 ABORT() when I tried enabling it. */ |
1204 | 1660 /* Have to return something: there may be no |
1661 accompanying process event */ | |
1662 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); | |
1663 } | |
442 | 1664 } |
1665 } | |
1666 } | |
853 | 1667 #endif /* not CYGWIN */ |
442 | 1668 } /* while */ |
428 | 1669 } |
1670 | |
1671 /************************************************************************/ | |
1672 /* Event generators */ | |
1673 /************************************************************************/ | |
1674 | |
1675 /* | |
1676 * Callback procedure for synchronous timer messages | |
1677 */ | |
1678 static void CALLBACK | |
2286 | 1679 mswindows_wm_timer_callback (HWND UNUSED (hwnd), UINT UNUSED (umsg), |
1680 UINT id_timer, DWORD dwtime) | |
428 | 1681 { |
1682 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); | |
1683 | |
1684 if (KillTimer (NULL, id_timer)) | |
1685 --mswindows_pending_timers_count; | |
1686 | |
964 | 1687 XSET_EVENT_CHANNEL (emacs_event, Qnil); |
1688 XSET_EVENT_TIMESTAMP (emacs_event, dwtime); | |
1689 XSET_EVENT_TYPE (emacs_event, timeout_event); | |
1204 | 1690 XSET_EVENT_TIMEOUT_INTERVAL_ID (emacs_event, id_timer); |
1691 XSET_EVENT_TIMEOUT_FUNCTION (emacs_event, Qnil); | |
1692 XSET_EVENT_TIMEOUT_OBJECT (emacs_event, Qnil); | |
428 | 1693 |
1694 mswindows_enqueue_dispatch_event (emacs_event); | |
1695 } | |
1696 | |
1697 /* | |
1698 * Callback procedure for dde messages | |
1699 * | |
1700 * We execute a dde Open("file") by simulating a file drop, so dde support | |
1701 * depends on dnd support. | |
1702 */ | |
1703 #ifdef HAVE_DRAGNDROP | |
657 | 1704 extern int mswindows_dde_enable; |
1705 | |
903 | 1706 EXFUN(Fread_from_string, 3); |
1707 | |
1708 /* The following variables are used to maintain consistency of result and | |
1709 * error reporting to the client. | |
1710 * The basic protocol is to Execute a lisp form, and then Request one or | |
1711 * more of the following items: Status (1 = OK, 0 = Error), Result, or Error. | |
1712 * When the lisp form is queued, the dde_eval_pending flag is set to 1, | |
1713 * to indicate that the items are not yet available. The dde_eval_pending | |
1714 * flag is set to 0 when the evaluation is complete. Requests for the result | |
1715 * items will block while the dde_eval_pending flag is 1, to avoid clients | |
1716 * getting inconsistent results. | |
1717 */ | |
1718 static int dde_eval_pending; | |
1719 static Lisp_Object dde_eval_result; | |
1720 static Lisp_Object dde_eval_error; | |
1721 | |
1722 static Lisp_Object | |
2286 | 1723 dde_error (Lisp_Object err, Lisp_Object UNUSED (obj)) |
903 | 1724 { |
1725 dde_eval_error = err; | |
1726 return Qnil; | |
1727 } | |
1728 | |
1729 /* Read lisp forms from a string. Evaluate the forms as if they were | |
1730 * wrapped in a progn form. Return the result of the form. | |
1731 */ | |
1732 static Lisp_Object | |
1733 dde_eval_string (Lisp_Object str) | |
1734 { | |
1735 struct gcpro gcpro1, gcpro2; | |
1736 Lisp_Object args[3]; | |
1737 Lisp_Object obj; | |
1738 | |
1739 /* Heavy handed GCPROing, on the principle of it's better to be safe than | |
1740 * sorry... | |
1741 */ | |
1742 args[0] = Qnil; | |
1743 args[1] = Qnil; | |
1744 args[2] = Qnil; | |
1745 GCPRO2 (args[0], str); | |
1746 gcpro1.nvars = 3; | |
1747 | |
1748 /* Wrap the user supplied string in string "(progn ...)". | |
1749 * We can now just read-from-string a single form. If we | |
1750 * get an error, or finish before the end of the string, | |
1751 * we know the original string had syntax errors. | |
1752 */ | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1753 args[0] = build_ascstring ("(progn "); |
903 | 1754 args[1] = str; |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1755 args[2] = build_ascstring (")"); |
903 | 1756 str = Fconcat (3, args); |
1757 | |
1758 obj = Fread_from_string (str, Qnil, Qnil); | |
1759 UNGCPRO; | |
1760 | |
1761 /* The following doesn't check that the length fits in an EMACS_INT. | |
1762 * This won't be a problem in reality...? | |
1763 * | |
1764 * If the read didn't get to the end of the string, we have a syntax | |
1765 * error in the string supplied by the user. | |
1766 */ | |
5581
56144c8593a8
Mechanically change INT to FIXNUM in our sources.
Aidan Kehoe <kehoea@parhasard.net>
parents:
5402
diff
changeset
|
1767 if (XFIXNUM (XCDR (obj)) != XSTRING_LENGTH (str)) |
903 | 1768 return Qnil; |
1769 | |
1770 GCPRO1 (obj); | |
4677
8f1ee2d15784
Support full Common Lisp multiple values in C.
Aidan Kehoe <kehoea@parhasard.net>
parents:
3263
diff
changeset
|
1771 obj = IGNORE_MULTIPLE_VALUES (Feval (XCAR (obj))); |
903 | 1772 |
1204 | 1773 RETURN_UNGCPRO (obj); |
903 | 1774 } |
1775 | |
1776 /* Evaluate the supplied string as a sequence of Lisp forms, wrapped in | |
1777 * a progn. Catch any evaluation errors. Set the evaluation status and | |
1778 * result variables. | |
1779 */ | |
1780 static void | |
1781 dde_eval (Lisp_Object str) | |
1782 { | |
1783 dde_eval_error = Qnil; | |
1784 dde_eval_result = condition_case_1 (Qt, dde_eval_string, str, | |
1785 dde_error, Qnil); | |
1786 dde_eval_pending = 0; | |
1787 | |
1788 /* Re-enable callbacks in case the client is waiting on a request */ | |
1789 DdeEnableCallback (mswindows_dde_mlid, NULL, EC_ENABLEALL); | |
1790 | |
1791 /* Post advise notifications on the result item */ | |
1792 DdePostAdvise (mswindows_dde_mlid, mswindows_dde_topic_eval, | |
1793 mswindows_dde_item_result); | |
1794 } | |
1795 | |
1796 /* A list of DDE advise tokens. Each token is an uninterned symbol, | |
1797 * whose value is the DDE string handle for its name (stored as a float, | |
1798 * as a Lisp int cannot hold a full C int). | |
3025 | 1799 * The token's `dde-data' property is used to store data for a dde-advise. |
903 | 1800 */ |
1801 Lisp_Object Vdde_advise_items; | |
1802 | |
3025 | 1803 /* The symbol `HSZ' */ |
903 | 1804 Lisp_Object QHSZ; |
1805 | |
1806 DEFUN("dde-alloc-advise-item", Fdde_alloc_advise_item, 0, 1, 0, /* | |
1807 Allocate an advise item, and return its token. | |
1808 */ | |
1809 (name)) | |
1810 { | |
1811 Lisp_Object token; | |
1812 HSZ hsz; | |
1813 struct gcpro gcpro1, gcpro2; | |
1814 | |
1815 if (!NILP (name)) | |
1816 CHECK_STRING (name); | |
1817 else | |
1818 { | |
1819 static int num = 0; | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1820 Ascbyte buf[20]; |
903 | 1821 sprintf (buf, "Tok%d", num); |
1822 ++num; | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1823 name = build_ascstring (buf); |
903 | 1824 } |
1825 | |
1826 token = Qnil; | |
1827 GCPRO2 (name, token); | |
1828 token = Fmake_symbol (name); | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
1829 hsz = qxeDdeCreateStringHandle (mswindows_dde_mlid, |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
1830 LISP_STRING_TO_TSTR (name), |
903 | 1831 XEUNICODE_P ? CP_WINUNICODE : CP_WINANSI); |
1832 | |
1833 Fput(token, QHSZ, make_float ((int)hsz)); | |
1834 Vdde_advise_items = Fcons (token, Vdde_advise_items); | |
1835 | |
1204 | 1836 RETURN_UNGCPRO (token); |
903 | 1837 } |
1838 | |
1839 DEFUN("dde-free-advise-item", Fdde_free_advise_item, 1, 1, 0, /* | |
1840 Free the resources associated with advise item ITEM. | |
1841 | |
1842 Frees all resources allocated to allow clients to set up advise loops | |
1843 on ITEM. It is assumed that no active advise loops remain. However, no | |
1844 problems should arise if they do - it's just that we won't ever send any | |
1845 notifications again. | |
1846 | |
1847 If the user does not free an advise item, resources will be leaked. | |
1848 */ | |
1849 (item)) | |
1850 { | |
1851 HSZ hsz; | |
1852 Lisp_Object val; | |
1853 | |
1854 CHECK_SYMBOL (item); | |
1855 val = Fget (item, QHSZ, Qnil); | |
1856 if (!FLOATP (val)) | |
1857 return Qnil; | |
1858 hsz = (HSZ)(int)XFLOAT_DATA (val); | |
1859 DdeFreeStringHandle (mswindows_dde_mlid, hsz); | |
1860 Vdde_advise_items = delq_no_quit (item, Vdde_advise_items); | |
1861 return Qnil; | |
1862 } | |
1863 | |
1864 DEFUN("dde-advise", Fdde_advise, 2, 2, 0, /* | |
1865 Post a DDE advise for ITEM with associated data DATA. | |
1866 | |
1867 Records the value DATA for sending back to clients waiting for | |
1868 notifications on DDE item ITEM in the system topic, and posts | |
1869 the advise transaction. | |
1870 | |
1871 ITEM must be an advise token allocated using dde-alloc-advise-item. | |
1872 */ | |
1873 (item, data)) | |
1874 { | |
1875 HSZ hsz; | |
1876 Lisp_Object val; | |
1877 | |
1878 CHECK_SYMBOL (item); | |
1879 val = Fget (item, QHSZ, Qnil); | |
1880 if (!FLOATP (val)) | |
1881 return Qnil; | |
1882 hsz = (HSZ)(int)XFLOAT_DATA (val); | |
1883 | |
1884 Fset (item, data); | |
1885 DdePostAdvise (mswindows_dde_mlid, mswindows_dde_topic_eval, hsz); | |
1886 return Qnil; | |
1887 } | |
1888 | |
428 | 1889 HDDEDATA CALLBACK |
2286 | 1890 mswindows_dde_callback (UINT uType, UINT uFmt, HCONV UNUSED (hconv), |
428 | 1891 HSZ hszTopic, HSZ hszItem, HDDEDATA hdata, |
2286 | 1892 DWORD UNUSED (dwData1), DWORD UNUSED (dwData2)) |
428 | 1893 { |
1894 switch (uType) | |
1895 { | |
1896 case XTYP_CONNECT: | |
903 | 1897 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system) |
1898 || !DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval)) | |
853 | 1899 return (HDDEDATA) TRUE; |
1900 return (HDDEDATA) FALSE; | |
428 | 1901 |
1902 case XTYP_WILDCONNECT: | |
1903 { | |
903 | 1904 /* We support two {service,topic} pairs */ |
1905 HSZPAIR pairs[3] = | |
771 | 1906 { |
903 | 1907 { mswindows_dde_service, mswindows_dde_topic_system }, |
1908 { mswindows_dde_service, mswindows_dde_topic_eval }, | |
1909 { 0, 0 } | |
1910 }; | |
1911 | |
1912 if ((!hszItem | |
1913 || !DdeCmpStringHandles (hszItem, mswindows_dde_service)) && | |
1914 (!hszTopic | |
1915 || !DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system) | |
1916 || !DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval))) | |
853 | 1917 return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE) pairs, |
428 | 1918 sizeof (pairs), 0L, 0, uFmt, 0)); |
1919 } | |
853 | 1920 return (HDDEDATA) NULL; |
428 | 1921 |
903 | 1922 case XTYP_ADVSTART: |
1923 if (!mswindows_dde_enable) | |
1924 return (HDDEDATA) FALSE; | |
1925 | |
1926 /* We only support advise loops on the eval topic for text data */ | |
1927 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval) | |
1928 && (uFmt == CF_TEXT || uFmt == CF_UNICODETEXT)) | |
1929 { | |
1930 /* Only allocated items or Result, are allowed */ | |
1931 if (!DdeCmpStringHandles (hszItem, mswindows_dde_item_result)) | |
1932 return (HDDEDATA) TRUE; | |
1933 | |
1934 { | |
1935 EXTERNAL_LIST_LOOP_2 (elt, Vdde_advise_items) | |
1936 { | |
1937 Lisp_Object val; | |
1938 HSZ hsz; | |
1939 if (!SYMBOLP (elt)) | |
1940 continue; | |
1941 val = Fget (elt, QHSZ, Qnil); | |
1942 if (!FLOATP (val)) | |
1943 continue; | |
1944 hsz = (HSZ) (int) XFLOAT_DATA (val); | |
1945 if (!DdeCmpStringHandles (hszItem, hsz)) | |
1946 return (HDDEDATA) TRUE; | |
1947 } | |
1948 } | |
1949 } | |
1950 return (HDDEDATA) FALSE; | |
1951 | |
1952 /* Both advise requests and normal requests work the same */ | |
1953 case XTYP_ADVREQ: | |
1954 case XTYP_REQUEST: | |
1955 if (!mswindows_dde_enable) | |
1956 return (HDDEDATA) NULL; | |
1957 | |
1958 if (DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval) != 0) | |
1959 return (HDDEDATA) NULL; | |
1960 | |
1961 /* If this is a normal request and we're in the middle of | |
1962 * an Execute, block until the Execute completes. | |
1963 */ | |
1964 if (dde_eval_pending && uType == XTYP_REQUEST) | |
1965 return (HDDEDATA) CBR_BLOCK; | |
1966 | |
1967 /* We can only support requests for ANSI or Unicode text */ | |
1968 if (uFmt != CF_TEXT && uFmt != CF_UNICODETEXT) | |
1969 return (HDDEDATA) NULL; | |
1970 | |
1971 { | |
1972 Lisp_Object args[2]; | |
1973 struct gcpro gcpro1; | |
1974 Lisp_Object res; | |
1975 Extbyte *result; | |
1976 DWORD bytes; | |
1977 | |
1978 args[0] = Qnil; | |
1979 args[1] = Qnil; | |
1980 GCPRO1 (args[0]); | |
1981 gcpro1.nvars = 2; | |
1982 | |
1983 | |
1984 if (!DdeCmpStringHandles (hszItem, mswindows_dde_item_result)) | |
1985 { | |
1986 if (NILP (dde_eval_error)) | |
1987 { | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1988 args[0] = build_ascstring ("OK: %s"); |
903 | 1989 args[1] = dde_eval_result; |
1990 } | |
1991 else | |
1992 { | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1993 args[0] = build_ascstring ("ERR: %s"); |
903 | 1994 args[1] = dde_eval_error; |
1995 } | |
1996 } | |
1997 else | |
1998 { | |
1999 EXTERNAL_LIST_LOOP_2 (elt, Vdde_advise_items) | |
2000 { | |
2001 Lisp_Object val; | |
2002 HSZ hsz; | |
2003 if (!SYMBOLP (elt)) | |
2004 continue; | |
2005 val = Fget (elt, QHSZ, Qnil); | |
2006 if (!FLOATP (val)) | |
2007 continue; | |
2008 hsz = (HSZ) (int) XFLOAT_DATA (val); | |
2009 if (!DdeCmpStringHandles (hszItem, hsz)) | |
2010 args[1] = Fsymbol_value (elt); | |
2011 } | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
2012 args[0] = build_ascstring ("%s"); |
903 | 2013 } |
2014 | |
2015 res = Fformat (2, args); | |
2016 UNGCPRO; | |
2017 | |
2018 bytes = (uFmt == CF_TEXT ? 1 : 2) * (XSTRING_LENGTH (res) + 1); | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2019 result = |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2020 LISP_STRING_TO_EXTERNAL (res, |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2021 uFmt == CF_TEXT ? Qmswindows_multibyte |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2022 : Qmswindows_unicode); |
903 | 2023 |
2024 /* If we cannot create the data handle, this passes the null | |
2025 * return back to the client, which signals an error as we wish. | |
2026 */ | |
2027 return DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)result, | |
2028 bytes, 0L, hszItem, uFmt, 0); | |
2029 } | |
2030 | |
428 | 2031 case XTYP_EXECUTE: |
657 | 2032 if (!mswindows_dde_enable) |
2033 return (HDDEDATA) DDE_FBUSY; | |
2034 | |
903 | 2035 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval)) |
2036 { | |
2037 DWORD len; | |
2038 LPBYTE extcmd; | |
2039 Lisp_Object tmp; | |
2040 | |
2041 /* Grab a pointer to the raw data supplied */ | |
2042 extcmd = DdeAccessData (hdata, &len); | |
2043 | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2044 tmp = make_extstring ((Extbyte *) extcmd, len, Qmswindows_tstr); |
903 | 2045 |
2046 /* Release and free the data handle */ | |
2047 DdeUnaccessData (hdata); | |
2048 DdeFreeDataHandle (hdata); | |
2049 | |
2050 /* Set a flag to say that the evaluation isn't yet complete, | |
2051 * enqueue the evaluation, send a dummy event to trigger the | |
2052 * event loop (I've no idea why this is needed, but it works...) | |
2053 * and return success to the client. | |
2054 */ | |
2055 dde_eval_pending = 1; | |
2056 enqueue_magic_eval_event (dde_eval, tmp); | |
2057 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); | |
2058 return (HDDEDATA) DDE_FACK; | |
2059 } | |
2060 else if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)) | |
428 | 2061 { |
2062 DWORD len = DdeGetData (hdata, NULL, 0, 0); | |
2367 | 2063 Extbyte *extcmd = alloca_extbytes (len + 1); |
867 | 2064 Ibyte *cmd; |
2065 Ibyte *end; | |
428 | 2066 struct gcpro gcpro1, gcpro2; |
657 | 2067 Lisp_Object l_dndlist = Qnil; |
428 | 2068 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); |
2069 Lisp_Object frmcons, devcons, concons; | |
440 | 2070 Lisp_Event *event = XEVENT (emacs_event); |
428 | 2071 |
2367 | 2072 DdeGetData (hdata, (LPBYTE) extcmd, len, 0); |
428 | 2073 DdeFreeDataHandle (hdata); |
2074 | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2075 cmd = SIZED_EXTERNAL_TO_ITEXT (extcmd, len, Qmswindows_tstr); |
771 | 2076 |
428 | 2077 /* Check syntax & that it's an [Open("foo")] command, which we |
2078 * treat like a file drop */ | |
2079 if (*cmd == '[') | |
2080 cmd++; | |
2367 | 2081 if (qxestrncasecmp_ascii (cmd, MSWINDOWS_DDE_ITEM_OPEN, |
771 | 2082 strlen (MSWINDOWS_DDE_ITEM_OPEN))) |
428 | 2083 return DDE_FNOTPROCESSED; |
2084 cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN); | |
771 | 2085 while (*cmd == ' ') |
428 | 2086 cmd++; |
771 | 2087 if (*cmd != '(' || *(cmd + 1) != '\"') |
428 | 2088 return DDE_FNOTPROCESSED; |
771 | 2089 end = (cmd += 2); |
2090 while (*end && *end != '\"') | |
428 | 2091 end++; |
2092 if (!*end) | |
2093 return DDE_FNOTPROCESSED; | |
2094 *end = '\0'; | |
771 | 2095 if (*++end != ')') |
428 | 2096 return DDE_FNOTPROCESSED; |
771 | 2097 if (*++end == ']') |
428 | 2098 end++; |
2099 if (*end) | |
2100 return DDE_FNOTPROCESSED; | |
2101 | |
771 | 2102 { |
2103 /* The drag-n-drop code in dragdrop.el expects pseudo-URL's, | |
2104 consisting of just file: followed by the filename. This | |
2105 should maybe work, but both Netscape and IE complain | |
2106 whenever they're not given the full file spec, like | |
2107 | |
2108 file:///C|/foo/bar/ or equivalently | |
2109 file:///C:/foo/bar/ (less portably) | |
2110 | |
2111 they don't allow relative paths at all! this is way bogus. */ | |
2112 cmd = urlify_filename (cmd); | |
4953
304aebb79cd3
function renamings to track names of char typedefs
Ben Wing <ben@xemacs.org>
parents:
4952
diff
changeset
|
2113 l_dndlist = build_istring (cmd); |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2114 xfree (cmd); |
771 | 2115 } |
428 | 2116 GCPRO2 (emacs_event, l_dndlist); |
2117 | |
2118 /* Find a mswindows frame */ | |
2119 event->channel = Qnil; | |
2120 FRAME_LOOP_NO_BREAK (frmcons, devcons, concons) | |
2121 { | |
2122 Lisp_Object frame = XCAR (frmcons); | |
2123 if (FRAME_TYPE_P (XFRAME (frame), mswindows)) | |
2124 event->channel = frame; | |
2125 }; | |
2126 assert (!NILP (event->channel)); | |
2127 | |
964 | 2128 SET_EVENT_TIMESTAMP (event, GetTickCount()); |
2129 SET_EVENT_TYPE (event, misc_user_event); | |
1204 | 2130 SET_EVENT_MISC_USER_BUTTON (event, 1); |
2131 SET_EVENT_MISC_USER_MODIFIERS (event, 0); | |
2132 SET_EVENT_MISC_USER_X (event, -1); | |
2133 SET_EVENT_MISC_USER_Y (event, -1); | |
2134 SET_EVENT_MISC_USER_FUNCTION (event, | |
964 | 2135 Qdragdrop_drop_dispatch); |
1204 | 2136 SET_EVENT_MISC_USER_OBJECT (event, |
964 | 2137 Fcons (Qdragdrop_URL, |
2138 Fcons (l_dndlist, Qnil))); | |
428 | 2139 mswindows_enqueue_dispatch_event (emacs_event); |
2140 UNGCPRO; | |
2141 return (HDDEDATA) DDE_FACK; | |
2142 } | |
2143 DdeFreeDataHandle (hdata); | |
2144 return (HDDEDATA) DDE_FNOTPROCESSED; | |
2145 | |
2146 default: | |
2147 return (HDDEDATA) NULL; | |
2148 } | |
2149 } | |
2150 #endif | |
2151 | |
2152 /* | |
442 | 2153 * Helper to do repainting - repaints can happen both from the windows |
2154 * procedure and from magic events | |
2155 */ | |
2156 static void | |
2157 mswindows_handle_paint (struct frame *frame) | |
2158 { | |
2159 HWND hwnd = FRAME_MSWINDOWS_HANDLE (frame); | |
2160 | |
2161 /* According to the docs we need to check GetUpdateRect() before | |
2162 actually doing a WM_PAINT */ | |
2163 if (GetUpdateRect (hwnd, NULL, FALSE)) | |
2164 { | |
2165 PAINTSTRUCT paintStruct; | |
2166 int x, y, width, height; | |
2167 | |
2168 BeginPaint (hwnd, &paintStruct); | |
2169 x = paintStruct.rcPaint.left; | |
2170 y = paintStruct.rcPaint.top; | |
2171 width = paintStruct.rcPaint.right - paintStruct.rcPaint.left; | |
2172 height = paintStruct.rcPaint.bottom - paintStruct.rcPaint.top; | |
2173 /* Normally we want to ignore expose events when child | |
2174 windows are unmapped, however once we are in the guts of | |
2175 WM_PAINT we need to make sure that we don't register | |
2176 unmaps then because they will not actually occur. */ | |
2177 /* #### commenting out the next line seems to fix some problems | |
2178 but not all. only andy currently understands this stuff and | |
2179 he needs to review it more carefully. --ben */ | |
2180 if (!check_for_ignored_expose (frame, x, y, width, height)) | |
2181 { | |
2182 hold_ignored_expose_registration = 1; | |
1318 | 2183 redisplay_redraw_exposed_area (frame, x, y, width, height); |
442 | 2184 hold_ignored_expose_registration = 0; |
2185 } | |
2186 EndPaint (hwnd, &paintStruct); | |
2187 } | |
2188 } | |
2189 | |
2190 /* | |
2191 * Returns 1 if a key is a real modifier or special key, which | |
440 | 2192 * is better handled by DefWindowProc |
2193 */ | |
2194 static int | |
2195 key_needs_default_processing_p (UINT vkey) | |
2196 { | |
442 | 2197 if (mswindows_alt_by_itself_activates_menu && vkey == VK_MENU |
2198 /* if we let ALT activate the menu like this, then sticky ALT-modified | |
2199 keystrokes become impossible. */ | |
2200 && !modifier_keys_are_sticky) | |
440 | 2201 return 1; |
2202 | |
2203 return 0; | |
2204 } | |
2205 | |
442 | 2206 /* key-handling code is always ugly. It just ends up working out |
2207 that way. | |
2208 | |
2209 #### Most of the sticky-modifier code below is copied from similar | |
2210 code in event-Xt.c. They should somehow or other be merged. | |
2211 | |
2212 Here are some pointers: | |
2213 | |
2214 -- DOWN_MASK indicates which modifiers should be treated as "down" | |
2215 when the corresponding upstroke happens. It gets reset for | |
2216 a particular modifier when that modifier goes up, and reset | |
2217 for all modifiers when a non-modifier key is pressed. Example: | |
2218 | |
2219 I press Control-A-Shift and then release Control-A-Shift. | |
2220 I want the Shift key to be sticky but not the Control key. | |
2221 | |
2222 -- If a modifier key is sticky, I can unstick it by pressing | |
2223 the modifier key again. */ | |
2224 | |
2225 static WPARAM last_downkey; | |
2226 static int need_to_add_mask, down_mask; | |
2227 | |
2228 #define XEMSW_LCONTROL (1<<0) | |
2229 #define XEMSW_RCONTROL (1<<1) | |
2230 #define XEMSW_LSHIFT (1<<2) | |
2231 #define XEMSW_RSHIFT (1<<3) | |
2232 #define XEMSW_LMENU (1<<4) | |
2233 #define XEMSW_RMENU (1<<5) | |
2234 | |
2235 static int | |
2236 mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam, | |
2237 int downp, int keyp) | |
2238 { | |
2239 int mods = 0; | |
2240 | |
2241 if (!modifier_keys_are_sticky) /* Optimize for non-sticky modifiers */ | |
2242 return 0; | |
2243 | |
2244 if (! (keyp && | |
2245 (wParam == VK_CONTROL || wParam == VK_LCONTROL || | |
2246 wParam == VK_RCONTROL || | |
2247 wParam == VK_MENU || wParam == VK_LMENU || | |
2248 wParam == VK_RMENU || | |
2249 wParam == VK_SHIFT || wParam == VK_LSHIFT || | |
2250 wParam == VK_RSHIFT))) | |
2251 { /* Not a modifier key */ | |
2252 if (downp && keyp && !last_downkey) | |
2253 last_downkey = wParam; | |
2254 /* If I hold press-and-release the Control key and then press | |
2255 and hold down the right arrow, I want it to auto-repeat | |
2256 Control-Right. On the other hand, if I do the same but | |
2257 manually press the Right arrow a bunch of times, I want | |
2258 to see one Control-Right and then a bunch of Rights. | |
2259 This means that we need to distinguish between an | |
2260 auto-repeated key and a key pressed and released a bunch | |
2261 of times. */ | |
2262 else if ((downp && !keyp) || | |
2263 (downp && keyp && last_downkey && | |
2264 (wParam != last_downkey || | |
2265 /* the "previous key state" bit indicates autorepeat */ | |
2266 ! (lParam & (1 << 30))))) | |
2267 { | |
2268 need_to_add_mask = 0; | |
2269 last_downkey = 0; | |
2270 } | |
2271 if (downp) | |
2272 down_mask = 0; | |
2273 | |
2274 mods = need_to_add_mask; | |
2275 } | |
2276 else /* Modifier key pressed */ | |
2277 { | |
2278 /* If a non-modifier key was pressed in the middle of a bunch | |
2279 of modifiers, then it unsticks all the modifiers that were | |
2280 previously pressed. We cannot unstick the modifiers until | |
2281 now because we want to check for auto-repeat of the | |
2282 non-modifier key. */ | |
2283 | |
2284 if (last_downkey) | |
2285 { | |
2286 last_downkey = 0; | |
2287 need_to_add_mask = 0; | |
2288 } | |
2289 | |
2290 #define FROB(mask) \ | |
2291 do { \ | |
2292 if (downp && keyp) \ | |
2293 { \ | |
2294 /* If modifier key is already sticky, \ | |
2295 then unstick it. Note that we do \ | |
2296 not test down_mask to deal with the \ | |
2297 unlikely but possible case that the \ | |
2298 modifier key auto-repeats. */ \ | |
2299 if (need_to_add_mask & mask) \ | |
2300 { \ | |
2301 need_to_add_mask &= ~mask; \ | |
2302 down_mask &= ~mask; \ | |
2303 } \ | |
2304 else \ | |
2305 down_mask |= mask; \ | |
2306 } \ | |
2307 else \ | |
2308 { \ | |
2309 if (down_mask & mask) \ | |
2310 { \ | |
2311 down_mask &= ~mask; \ | |
2312 need_to_add_mask |= mask; \ | |
2313 } \ | |
2314 } \ | |
2315 } while (0) | |
2316 | |
2317 if ((wParam == VK_CONTROL && (lParam & 0x1000000)) | |
2318 || wParam == VK_RCONTROL) | |
2319 FROB (XEMSW_RCONTROL); | |
2320 if ((wParam == VK_CONTROL && !(lParam & 0x1000000)) | |
2321 || wParam == VK_LCONTROL) | |
2322 FROB (XEMSW_LCONTROL); | |
2323 | |
2324 if ((wParam == VK_SHIFT && (lParam & 0x1000000)) | |
2325 || wParam == VK_RSHIFT) | |
2326 FROB (XEMSW_RSHIFT); | |
2327 if ((wParam == VK_SHIFT && !(lParam & 0x1000000)) | |
2328 || wParam == VK_LSHIFT) | |
2329 FROB (XEMSW_LSHIFT); | |
2330 | |
2331 if ((wParam == VK_MENU && (lParam & 0x1000000)) | |
2332 || wParam == VK_RMENU) | |
2333 FROB (XEMSW_RMENU); | |
2334 if ((wParam == VK_MENU && !(lParam & 0x1000000)) | |
2335 || wParam == VK_LMENU) | |
2336 FROB (XEMSW_LMENU); | |
2337 } | |
2338 #undef FROB | |
2339 | |
2340 if (mods && downp) | |
2341 { | |
2342 BYTE keymap[256]; | |
2343 | |
2344 GetKeyboardState (keymap); | |
2345 | |
2346 if (mods & XEMSW_LCONTROL) | |
2347 { | |
2348 keymap [VK_CONTROL] |= 0x80; | |
2349 keymap [VK_LCONTROL] |= 0x80; | |
2350 } | |
2351 if (mods & XEMSW_RCONTROL) | |
2352 { | |
2353 keymap [VK_CONTROL] |= 0x80; | |
2354 keymap [VK_RCONTROL] |= 0x80; | |
2355 } | |
2356 | |
2357 if (mods & XEMSW_LSHIFT) | |
2358 { | |
2359 keymap [VK_SHIFT] |= 0x80; | |
2360 keymap [VK_LSHIFT] |= 0x80; | |
2361 } | |
2362 if (mods & XEMSW_RSHIFT) | |
2363 { | |
2364 keymap [VK_SHIFT] |= 0x80; | |
2365 keymap [VK_RSHIFT] |= 0x80; | |
2366 } | |
2367 | |
2368 if (mods & XEMSW_LMENU) | |
2369 { | |
2370 keymap [VK_MENU] |= 0x80; | |
2371 keymap [VK_LMENU] |= 0x80; | |
2372 } | |
2373 if (mods & XEMSW_RMENU) | |
2374 { | |
2375 keymap [VK_MENU] |= 0x80; | |
2376 keymap [VK_RMENU] |= 0x80; | |
2377 } | |
2378 | |
2379 SetKeyboardState (keymap); | |
2380 return 1; | |
2381 } | |
2382 | |
2383 return 0; | |
2384 } | |
2385 | |
2386 static void | |
2387 clear_sticky_modifiers (void) | |
2388 { | |
2389 need_to_add_mask = 0; | |
2390 last_downkey = 0; | |
2391 down_mask = 0; | |
2392 } | |
2393 | |
2394 #ifdef DEBUG_XEMACS | |
2395 | |
2396 #if 0 | |
2397 | |
2398 static void | |
2399 output_modifier_keyboard_state (void) | |
2400 { | |
2401 BYTE keymap[256]; | |
2402 | |
2403 GetKeyboardState (keymap); | |
2404 | |
2405 stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", | |
2406 keymap[VK_MENU] & 0x80 ? 1 : 0, | |
2407 keymap[VK_MENU] & 0x1 ? 1 : 0, | |
2408 keymap[VK_LMENU] & 0x80 ? 1 : 0, | |
2409 keymap[VK_LMENU] & 0x1 ? 1 : 0, | |
2410 keymap[VK_RMENU] & 0x80 ? 1 : 0, | |
2411 keymap[VK_RMENU] & 0x1 ? 1 : 0); | |
2412 stderr_out ("GetKeyboardState VK_CONTROL %d %d VK_LCONTROL %d %d VK_RCONTROL %d %d\n", | |
2413 keymap[VK_CONTROL] & 0x80 ? 1 : 0, | |
2414 keymap[VK_CONTROL] & 0x1 ? 1 : 0, | |
2415 keymap[VK_LCONTROL] & 0x80 ? 1 : 0, | |
2416 keymap[VK_LCONTROL] & 0x1 ? 1 : 0, | |
2417 keymap[VK_RCONTROL] & 0x80 ? 1 : 0, | |
2418 keymap[VK_RCONTROL] & 0x1 ? 1 : 0); | |
2419 stderr_out ("GetKeyboardState VK_SHIFT %d %d VK_LSHIFT %d %d VK_RSHIFT %d %d\n", | |
2420 keymap[VK_SHIFT] & 0x80 ? 1 : 0, | |
2421 keymap[VK_SHIFT] & 0x1 ? 1 : 0, | |
2422 keymap[VK_LSHIFT] & 0x80 ? 1 : 0, | |
2423 keymap[VK_LSHIFT] & 0x1 ? 1 : 0, | |
2424 keymap[VK_RSHIFT] & 0x80 ? 1 : 0, | |
2425 keymap[VK_RSHIFT] & 0x1 ? 1 : 0); | |
2426 } | |
2427 | |
2428 #endif | |
2429 | |
2430 /* try to debug the stuck-alt-key problem. | |
2431 | |
2432 #### this happens only inconsistently, and may only happen when using | |
2433 StickyKeys in the Win2000 accessibility section of the control panel, | |
2434 which is extremely broken for other reasons. */ | |
2435 | |
2436 static void | |
2437 output_alt_keyboard_state (void) | |
2438 { | |
2439 BYTE keymap[256]; | |
2440 SHORT keystate[3]; | |
1242 | 2441 /* SHORT asyncstate[3]; */ |
442 | 2442 |
2443 GetKeyboardState (keymap); | |
2444 keystate[0] = GetKeyState (VK_MENU); | |
2445 keystate[1] = GetKeyState (VK_LMENU); | |
2446 keystate[2] = GetKeyState (VK_RMENU); | |
2447 /* Doing this interferes with key processing. */ | |
2448 /* asyncstate[0] = GetAsyncKeyState (VK_MENU); */ | |
2449 /* asyncstate[1] = GetAsyncKeyState (VK_LMENU); */ | |
2450 /* asyncstate[2] = GetAsyncKeyState (VK_RMENU); */ | |
2451 | |
2452 stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", | |
2453 keymap[VK_MENU] & 0x80 ? 1 : 0, | |
2454 keymap[VK_MENU] & 0x1 ? 1 : 0, | |
2455 keymap[VK_LMENU] & 0x80 ? 1 : 0, | |
2456 keymap[VK_LMENU] & 0x1 ? 1 : 0, | |
2457 keymap[VK_RMENU] & 0x80 ? 1 : 0, | |
2458 keymap[VK_RMENU] & 0x1 ? 1 : 0); | |
2459 stderr_out ("GetKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", | |
2460 keystate[0] & 0x8000 ? 1 : 0, | |
2461 keystate[0] & 0x1 ? 1 : 0, | |
2462 keystate[1] & 0x8000 ? 1 : 0, | |
2463 keystate[1] & 0x1 ? 1 : 0, | |
2464 keystate[2] & 0x8000 ? 1 : 0, | |
2465 keystate[2] & 0x1 ? 1 : 0); | |
2466 /* stderr_out ("GetAsyncKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", */ | |
2467 /* asyncstate[0] & 0x8000 ? 1 : 0, */ | |
2468 /* asyncstate[0] & 0x1 ? 1 : 0, */ | |
2469 /* asyncstate[1] & 0x8000 ? 1 : 0, */ | |
2470 /* asyncstate[1] & 0x1 ? 1 : 0, */ | |
2471 /* asyncstate[2] & 0x8000 ? 1 : 0, */ | |
2472 /* asyncstate[2] & 0x1 ? 1 : 0); */ | |
2473 } | |
2474 | |
2475 #endif /* DEBUG_XEMACS */ | |
2476 | |
2477 | |
440 | 2478 /* |
428 | 2479 * The windows procedure for the window class XEMACS_CLASS |
2480 */ | |
2481 LRESULT WINAPI | |
442 | 2482 mswindows_wnd_proc (HWND hwnd, UINT message_, WPARAM wParam, LPARAM lParam) |
428 | 2483 { |
1204 | 2484 /* Note: Remember to initialize emacs_event and event before use. This |
2485 code calls code that can GC. You must GCPRO before calling such | |
2486 code. */ | |
428 | 2487 Lisp_Object emacs_event = Qnil; |
2488 Lisp_Object fobj = Qnil; | |
2489 | |
440 | 2490 Lisp_Event *event; |
428 | 2491 struct frame *frame; |
647 | 2492 struct mswindows_frame *msframe; |
428 | 2493 |
3092 | 2494 #ifndef NEW_GC |
611 | 2495 /* If you hit this, rewrite the offending API call to occur after GC, |
2496 using register_post_gc_action(). */ | |
2497 assert (!gc_in_progress); | |
3263 | 2498 #endif /* not NEW_GC */ |
593 | 2499 |
2500 #ifdef DEBUG_XEMACS | |
2501 if (debug_mswindows_events) | |
2502 debug_output_mswin_message (hwnd, message_, wParam, lParam); | |
2503 #endif /* DEBUG_XEMACS */ | |
442 | 2504 |
771 | 2505 assert (!qxeGetWindowLong (hwnd, GWL_USERDATA)); |
442 | 2506 switch (message_) |
428 | 2507 { |
442 | 2508 case WM_DESTROYCLIPBOARD: |
771 | 2509 mswindows_handle_destroyclipboard (); |
442 | 2510 break; |
2511 | |
2512 case WM_ERASEBKGND: | |
2513 /* Erase background only during non-dynamic sizing */ | |
771 | 2514 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 2515 if (msframe->sizing && !mswindows_dynamic_frame_resize) |
2516 goto defproc; | |
2517 return 1; | |
2518 | |
2519 case WM_CLOSE: | |
2520 fobj = mswindows_find_frame (hwnd); | |
853 | 2521 mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, |
2522 Qt)); | |
440 | 2523 break; |
428 | 2524 |
442 | 2525 case WM_KEYUP: |
2526 case WM_SYSKEYUP: | |
2527 | |
2528 /* See Win95 comment under WM_KEYDOWN */ | |
2529 { | |
2530 BYTE keymap[256]; | |
2531 int should_set_keymap = 0; | |
2532 | |
2533 #ifdef DEBUG_XEMACS | |
593 | 2534 if (debug_mswindows_events > 2) |
2535 output_alt_keyboard_state (); | |
442 | 2536 #endif /* DEBUG_XEMACS */ |
2537 | |
2538 mswindows_handle_sticky_modifiers (wParam, lParam, 0, 1); | |
2539 if (wParam == VK_CONTROL) | |
2540 { | |
2541 GetKeyboardState (keymap); | |
2542 keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80; | |
2543 should_set_keymap = 1; | |
2544 } | |
2545 else if (wParam == VK_MENU) | |
2546 { | |
2547 GetKeyboardState (keymap); | |
2548 keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80; | |
2549 should_set_keymap = 1; | |
2550 } | |
2551 | |
2552 if (should_set_keymap) | |
1242 | 2553 /* && (message_ != WM_SYSKEYUP */ |
2554 /* || NILP (Vmenu_accelerator_enabled))) */ | |
428 | 2555 SetKeyboardState (keymap); |
2556 | |
2557 } | |
442 | 2558 |
2559 if (key_needs_default_processing_p (wParam)) | |
2560 goto defproc; | |
2561 else | |
2562 break; | |
2563 | |
2564 case WM_KEYDOWN: | |
2565 case WM_SYSKEYDOWN: | |
2566 | |
2567 /* In some locales the right-hand Alt key is labelled AltGr. This key | |
2568 * should produce alternative characters when combined with another key. | |
2569 * eg on a German keyboard pressing AltGr+q should produce '@'. | |
2570 * AltGr generates exactly the same keystrokes as LCtrl+RAlt. But if | |
2571 * TranslateMessage() is called with *any* combination of Ctrl+Alt down, | |
2572 * it translates as if AltGr were down. | |
2573 * We get round this by removing all modifiers from the keymap before | |
2574 * calling TranslateMessage() unless AltGr is *really* down. */ | |
428 | 2575 { |
442 | 2576 BYTE keymap_trans[256]; |
2577 BYTE keymap_orig[256]; | |
2578 BYTE keymap_sticky[256]; | |
771 | 2579 /* WARNING: XEmacs code paths are far more subtle than you |
2580 think. In particular, QUIT checking will query and remove | |
2581 events, including keyboard events, from the queue. (QUIT is | |
2582 definitely invoked from TO_INTERNAL_FORMAT().) If we do | |
2583 this recursively anywhere in the following code, it will | |
2584 mess certain things up -- in particular, the OS-provided | |
2585 sticky modifier code available as part of the accessibility | |
2586 package. | |
2587 | |
2588 (Academic question: If QUIT checking is supposed to be | |
2589 triggered only every 1/4 second, why is it getting | |
2590 consistently triggered here? I saw the problem | |
2591 consistently. Answer: It appears that, currently, | |
2592 sometimes the code to pump messages is wrapped with | |
2593 begin_dont_check_for_quit() and sometimes it isn't. (#### | |
2594 FIX THIS SHIT!) cmdloop.c, for example, has it, but not | |
2595 everywhere. The current games with avoiding QUIT mean that | |
2596 the 1/4-second timer consistently fires while | |
2597 dont_check_for_quit is set [which causes the quit check to | |
2598 get deferred but the flag is still on], and so the next | |
2599 time it's unset and we call QUIT is *right here*. | |
2600 | |
2601 In my stderr-proc ws I majorly cleaned up the whole shit by | |
2602 just wrapping all the entry points in dont_check_for_quit. | |
2603 This fixed the remaining bugs with C-g getting interpreted | |
2604 wrong.) | |
2605 | |
2606 #### We should probably wrap this whole function in | |
2607 begin_dont_check_for_quit(); but then we should set this | |
2608 back to 0 when handling a menu callback, which gets invoked | |
2609 from within this function, specifically from | |
2610 DefWindowProc(). (We already do the latter in my new | |
2611 stderr-proc ws, because in that ws next_event_internal() | |
2612 calls begin_dont_check_for_quit(). */ | |
2613 | |
2614 int count = begin_dont_check_for_quit (); | |
442 | 2615 int has_AltGr = mswindows_current_layout_has_AltGr (); |
502 | 2616 int mods = 0, mods_with_shift = 0; |
442 | 2617 int extendedp = lParam & 0x1000000; |
2618 Lisp_Object keysym; | |
2619 int sticky_changed; | |
2620 | |
2621 #ifdef DEBUG_XEMACS | |
593 | 2622 if (debug_mswindows_events > 2) |
2623 output_alt_keyboard_state (); | |
442 | 2624 #endif /* DEBUG_XEMACS */ |
2625 | |
2626 GetKeyboardState (keymap_orig); | |
2627 frame = XFRAME (mswindows_find_frame (hwnd)); | |
2628 if ((sticky_changed = | |
2629 mswindows_handle_sticky_modifiers (wParam, lParam, 1, 1))) | |
428 | 2630 { |
442 | 2631 GetKeyboardState (keymap_sticky); |
2632 if (keymap_sticky[VK_MENU] & 0x80) | |
2633 { | |
2634 message_ = WM_SYSKEYDOWN; | |
2635 /* We have to set the "context bit" so that the | |
2636 TranslateMessage() call below that generates the | |
2637 SYSCHAR message does its thing; see the documentation | |
2638 on WM_SYSKEYDOWN */ | |
2639 lParam |= 1 << 29; | |
2640 } | |
428 | 2641 } |
2642 else | |
442 | 2643 memcpy (keymap_sticky, keymap_orig, 256); |
2644 | |
2645 mods = mswindows_modifier_state (keymap_sticky, (DWORD) -1, has_AltGr); | |
502 | 2646 mods_with_shift = mods; |
442 | 2647 |
2648 /* Handle non-printables */ | |
2649 if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods, | |
2650 extendedp))) | |
428 | 2651 { |
442 | 2652 mswindows_enqueue_keypress_event (hwnd, keysym, mods); |
2653 if (sticky_changed) | |
2654 SetKeyboardState (keymap_orig); | |
428 | 2655 } |
442 | 2656 else /* Normal keys & modifiers */ |
428 | 2657 { |
442 | 2658 POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) }; |
2659 MSG msg, tranmsg; | |
1204 | 2660 #ifdef HAVE_MENUBARS |
442 | 2661 int potential_accelerator = 0; |
1204 | 2662 #endif |
442 | 2663 int got_accelerator = 0; |
771 | 2664 /* No need to gcpro because the event is already on a |
2665 queue when we retrieve it. */ | |
2666 Lisp_Object lastev = Qnil; | |
442 | 2667 |
2668 msg.hwnd = hwnd; | |
2669 msg.message = message_; | |
2670 msg.wParam = wParam; | |
2671 msg.lParam = lParam; | |
2672 msg.time = GetMessageTime(); | |
2673 msg.pt = pnt; | |
2674 | |
2675 /* GetKeyboardState() does not work as documented on Win95. We have | |
2676 * to loosely track Left and Right modifiers on behalf of the OS, | |
2677 * without screwing up Windows NT which tracks them properly. */ | |
2678 if (wParam == VK_CONTROL) | |
2679 { | |
2680 keymap_orig[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80; | |
2681 keymap_sticky[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80; | |
2682 } | |
2683 else if (wParam == VK_MENU) | |
2684 { | |
2685 keymap_orig[extendedp ? VK_RMENU : VK_LMENU] |= 0x80; | |
2686 keymap_sticky[extendedp ? VK_RMENU : VK_LMENU] |= 0x80; | |
2687 } | |
2688 | |
827 | 2689 #ifdef HAVE_MENUBARS |
442 | 2690 if (!NILP (Vmenu_accelerator_enabled) && |
2691 !(mods & XEMACS_MOD_SHIFT) && message_ == WM_SYSKEYDOWN) | |
2692 potential_accelerator = 1; | |
827 | 2693 #endif |
442 | 2694 |
2695 /* Remove shift modifier from an ascii character */ | |
2696 mods &= ~XEMACS_MOD_SHIFT; | |
2697 | |
2698 memcpy (keymap_trans, keymap_sticky, 256); | |
2699 | |
2700 /* Clear control and alt modifiers unless AltGr is pressed */ | |
2701 keymap_trans[VK_RCONTROL] = 0; | |
2702 keymap_trans[VK_LMENU] = 0; | |
2703 if (!has_AltGr || !(keymap_trans[VK_LCONTROL] & 0x80) | |
2704 || !(keymap_trans[VK_RMENU] & 0x80)) | |
2705 { | |
2706 keymap_trans[VK_LCONTROL] = 0; | |
2707 keymap_trans[VK_CONTROL] = 0; | |
2708 keymap_trans[VK_RMENU] = 0; | |
2709 keymap_trans[VK_MENU] = 0; | |
2710 } | |
2711 SetKeyboardState (keymap_trans); | |
2712 | |
2713 /* Maybe generate some WM_[SYS]CHARs in the queue */ | |
2714 TranslateMessage (&msg); | |
2715 | |
771 | 2716 while (qxePeekMessage (&tranmsg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE) |
2717 || qxePeekMessage (&tranmsg, hwnd, WM_SYSCHAR, WM_SYSCHAR, | |
2718 PM_REMOVE)) | |
442 | 2719 { |
502 | 2720 int mods_with_quit = mods; |
771 | 2721 int length; |
2722 Extbyte extchar[4]; | |
867 | 2723 Ibyte *intchar; |
2724 Ichar ch; | |
771 | 2725 |
2726 if (XEUNICODE_P) | |
2727 { | |
2728 length = unicode_char_to_text (tranmsg.wParam, extchar); | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2729 intchar = SIZED_EXTERNAL_TO_ITEXT (extchar, length, |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2730 Qmswindows_unicode); |
867 | 2731 ch = itext_ichar (intchar); |
771 | 2732 } |
2733 else | |
2734 { | |
2735 length = ansi_char_to_text (tranmsg.wParam, extchar); | |
2736 intchar = (convert_multibyte_to_internal_malloc | |
2737 (extchar, length, | |
2738 mswindows_locale_to_code_page | |
2739 /* See intl-win32.c for an explanation of | |
2740 the following */ | |
2741 ((LCID) GetKeyboardLayout (0) & 0xFFFF), | |
2742 NULL)); | |
867 | 2743 ch = itext_ichar (intchar); |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2744 xfree (intchar); |
771 | 2745 } |
442 | 2746 |
593 | 2747 #ifdef DEBUG_XEMACS |
2748 if (debug_mswindows_events) | |
2749 { | |
2750 stderr_out ("-> "); | |
2751 debug_output_mswin_message (tranmsg.hwnd, tranmsg.message, | |
2752 tranmsg.wParam, | |
2753 tranmsg.lParam); | |
2754 } | |
2755 #endif /* DEBUG_XEMACS */ | |
2756 | |
827 | 2757 #ifdef HAVE_MENUBARS |
1204 | 2758 if (potential_accelerator && !got_accelerator && |
2759 mswindows_char_is_accelerator (frame, ch)) | |
442 | 2760 { |
2761 got_accelerator = 1; | |
2762 break; | |
2763 } | |
827 | 2764 #endif /* HAVE_MENUBARS */ |
2765 | |
771 | 2766 lastev = mswindows_enqueue_keypress_event (hwnd, |
2767 make_char (ch), | |
2768 mods_with_quit); | |
442 | 2769 } /* while */ |
2770 | |
771 | 2771 /* Also figure out what the character would be in other |
2772 possible keyboard layouts, in this order: | |
2773 | |
2774 -- current language environment | |
2775 -- user default language environment | |
2776 -- system default language environment | |
2777 -- same three, but checking the underlying virtual key, | |
2778 and only paying attention if it's alphabetic | |
2779 -- US ASCII | |
2780 | |
2781 See events.h, struct key_data, for why we do this. | |
2782 */ | |
2783 | |
2784 if (!NILP (lastev)) | |
2785 { | |
2786 int i; | |
2787 int scan = (lParam >> 16) && 0xFF; | |
2788 | |
2789 for (i = 0; i < KEYCHAR_LAST; i++) | |
2790 { | |
2791 int vk_only = 0; | |
2792 LCID lcid; | |
2793 int virtual_key; | |
2794 | |
2795 switch (i) | |
2796 { | |
2797 case KEYCHAR_UNDERLYING_VIRTUAL_KEY_CURRENT_LANGENV: | |
2798 vk_only = 1; | |
2799 case KEYCHAR_CURRENT_LANGENV: | |
2800 lcid = mswindows_current_locale (); | |
2801 break; | |
2802 | |
2803 case KEYCHAR_UNDERLYING_VIRTUAL_KEY_DEFAULT_USER: | |
2804 vk_only = 1; | |
2805 case KEYCHAR_DEFAULT_USER: | |
2806 lcid = GetUserDefaultLCID (); | |
2807 break; | |
2808 | |
2809 case KEYCHAR_UNDERLYING_VIRTUAL_KEY_DEFAULT_SYSTEM: | |
2810 vk_only = 1; | |
2811 case KEYCHAR_DEFAULT_SYSTEM: | |
2812 lcid = GetSystemDefaultLCID (); | |
2813 break; | |
2814 | |
2815 case KEYCHAR_QWERTY: | |
2816 lcid = MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US); | |
2817 break; | |
2818 | |
2500 | 2819 default: ABORT (); lcid = 0; |
771 | 2820 } |
2821 | |
2822 /* VERY CONFUSING! See intl-win32.c. */ | |
2823 lcid = lcid & 0xFFFF; | |
2824 | |
800 | 2825 virtual_key = qxeMapVirtualKeyEx (scan, 1, (HKL) lcid); |
771 | 2826 if (!vk_only) |
2827 { | |
2828 if (XEUNICODE_P) | |
2829 { | |
2830 Extbyte received_keys[32]; | |
2831 int tounret = | |
2832 ToUnicodeEx | |
2833 (virtual_key, scan, keymap_trans, | |
2834 (LPWSTR) received_keys, | |
2835 sizeof (received_keys) / XETCHAR_SIZE, | |
2836 0, /* #### what about this flag? "if | |
2837 bit 0 is set, a menu is | |
2838 active???" */ | |
2839 (HKL) lcid); | |
2840 if (tounret > 0) | |
2841 { | |
867 | 2842 Ibyte *intchar; |
771 | 2843 |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2844 intchar = |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2845 SIZED_EXTERNAL_TO_ITEXT |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2846 (received_keys + (tounret - 1) * 2, 2, |
771 | 2847 Qmswindows_unicode); |
1204 | 2848 XSET_EVENT_KEY_ALT_KEYCHARS |
2849 (lastev, i, itext_ichar (intchar)); | |
771 | 2850 } |
2851 } | |
2852 else | |
2853 { | |
2854 WORD received_keys[32]; | |
2855 int tounret = | |
2856 ToAsciiEx (virtual_key, scan, keymap_trans, | |
2857 received_keys, | |
2858 0, /* #### what about this | |
2859 flag? "if bit 0 is set, a | |
2860 menu is active???" */ | |
2861 (HKL) lcid); | |
2862 if (tounret > 0) | |
2863 { | |
2864 /* #### I cannot find proper | |
2865 documentation on what format the | |
2866 return value is in. I'm assuming | |
2867 it's like WM_IME_CHAR: DBCS chars | |
2868 have the lead byte in bits 8-15 of | |
2869 the short. */ | |
867 | 2870 Ibyte *intchar; |
771 | 2871 Extbyte mbstuff[2]; |
2872 Bytecount mblength = 0; | |
2873 WORD thechar = received_keys[tounret - 1]; | |
2874 | |
2875 mbstuff[mblength++] = | |
2876 (Extbyte) (thechar & 0xFF); | |
2877 if (thechar > 0xFF) | |
2878 mbstuff[mblength++] = | |
2879 (Extbyte) ((thechar >> 8) & 0xFF); | |
2880 | |
2881 intchar = convert_multibyte_to_internal_malloc | |
2882 (mbstuff, mblength, | |
2883 mswindows_locale_to_code_page (lcid), | |
2884 NULL); | |
2885 | |
1204 | 2886 XSET_EVENT_KEY_ALT_KEYCHARS |
2887 (lastev, i, itext_ichar (intchar)); | |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2888 xfree (intchar); |
771 | 2889 } |
2890 } | |
2891 } | |
2892 else | |
2893 { | |
867 | 2894 Ichar altch; |
771 | 2895 |
2896 if (virtual_key >= 'A' && virtual_key <= 'Z') | |
2897 altch = | |
2898 virtual_key + (mods_with_shift & XEMACS_MOD_SHIFT ? | |
2899 'a' - 'A' : 0); | |
2900 else | |
2901 altch = 0; | |
2902 | |
1204 | 2903 XSET_EVENT_KEY_ALT_KEYCHARS (lastev, i, altch); |
771 | 2904 } |
2905 } | |
2906 } | |
2907 | |
442 | 2908 /* This generates WM_SYSCHAR messages, which are interpreted |
2909 by DefWindowProc as the menu selections. */ | |
2910 if (got_accelerator) | |
2911 { | |
2912 SetKeyboardState (keymap_sticky); | |
2913 TranslateMessage (&msg); | |
2914 SetKeyboardState (keymap_orig); | |
771 | 2915 unbind_to (count); |
442 | 2916 goto defproc; |
2917 } | |
2918 | |
2919 SetKeyboardState (keymap_orig); | |
2920 } /* else */ | |
771 | 2921 |
2922 if (key_needs_default_processing_p (wParam)) | |
2923 { | |
2924 unbind_to (count); | |
2925 goto defproc; | |
2926 } | |
2927 else | |
2928 { | |
2929 unbind_to (count); | |
2930 break; | |
2931 } | |
428 | 2932 } |
442 | 2933 |
2934 case WM_MBUTTONDOWN: | |
2935 case WM_MBUTTONUP: | |
2936 /* Real middle mouse button has nothing to do with emulated one: | |
2937 if one wants to exercise fingers playing chords on the mouse, | |
2938 he is allowed to do that! */ | |
2939 mswindows_enqueue_mouse_button_event (hwnd, message_, | |
2367 | 2940 XE_MAKEPOINTS (lParam), |
442 | 2941 wParam &~ MK_MBUTTON, |
2942 GetMessageTime()); | |
2943 break; | |
2944 | |
2945 case WM_LBUTTONUP: | |
771 | 2946 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
2947 msframe->last_click_time = GetMessageTime(); | |
442 | 2948 |
2949 KillTimer (hwnd, BUTTON_2_TIMER_ID); | |
2950 msframe->button2_need_lbutton = 0; | |
2951 if (msframe->ignore_next_lbutton_up) | |
2952 { | |
2953 msframe->ignore_next_lbutton_up = 0; | |
2954 } | |
2955 else if (msframe->button2_is_down) | |
2956 { | |
2957 msframe->button2_is_down = 0; | |
2958 msframe->ignore_next_rbutton_up = 1; | |
2959 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, | |
2367 | 2960 XE_MAKEPOINTS (lParam), |
442 | 2961 wParam |
2962 &~ (MK_LBUTTON | MK_MBUTTON | |
2963 | MK_RBUTTON), | |
2964 GetMessageTime()); | |
2965 } | |
2966 else | |
2967 { | |
2968 if (msframe->button2_need_rbutton) | |
2969 { | |
2970 msframe->button2_need_rbutton = 0; | |
2971 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, | |
2367 | 2972 XE_MAKEPOINTS (lParam), |
442 | 2973 wParam &~ MK_LBUTTON, |
2974 GetMessageTime()); | |
2975 } | |
2976 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP, | |
2367 | 2977 XE_MAKEPOINTS (lParam), |
442 | 2978 wParam &~ MK_LBUTTON, |
2979 GetMessageTime()); | |
2980 } | |
2981 break; | |
2982 | |
2983 case WM_RBUTTONUP: | |
771 | 2984 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
2985 msframe->last_click_time = GetMessageTime(); | |
442 | 2986 |
2987 KillTimer (hwnd, BUTTON_2_TIMER_ID); | |
2988 msframe->button2_need_rbutton = 0; | |
2989 if (msframe->ignore_next_rbutton_up) | |
2990 { | |
2991 msframe->ignore_next_rbutton_up = 0; | |
2992 } | |
2993 else if (msframe->button2_is_down) | |
2994 { | |
2995 msframe->button2_is_down = 0; | |
2996 msframe->ignore_next_lbutton_up = 1; | |
2997 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, | |
2367 | 2998 XE_MAKEPOINTS (lParam), |
442 | 2999 wParam |
3000 &~ (MK_LBUTTON | MK_MBUTTON | |
3001 | MK_RBUTTON), | |
3002 GetMessageTime()); | |
3003 } | |
3004 else | |
3005 { | |
3006 if (msframe->button2_need_lbutton) | |
3007 { | |
3008 msframe->button2_need_lbutton = 0; | |
3009 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, | |
2367 | 3010 XE_MAKEPOINTS (lParam), |
442 | 3011 wParam &~ MK_RBUTTON, |
3012 GetMessageTime()); | |
3013 } | |
3014 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP, | |
2367 | 3015 XE_MAKEPOINTS (lParam), |
442 | 3016 wParam &~ MK_RBUTTON, |
3017 GetMessageTime()); | |
3018 } | |
3019 break; | |
3020 | |
3021 case WM_LBUTTONDOWN: | |
771 | 3022 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3023 |
3024 if (msframe->button2_need_lbutton) | |
428 | 3025 { |
3026 KillTimer (hwnd, BUTTON_2_TIMER_ID); | |
442 | 3027 msframe->button2_need_lbutton = 0; |
3028 msframe->button2_need_rbutton = 0; | |
3029 if (mswindows_button2_near_enough (msframe->last_click_point, | |
2367 | 3030 XE_MAKEPOINTS (lParam))) |
428 | 3031 { |
442 | 3032 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, |
2367 | 3033 XE_MAKEPOINTS (lParam), |
442 | 3034 wParam |
3035 &~ (MK_LBUTTON | MK_MBUTTON | |
3036 | MK_RBUTTON), | |
3037 GetMessageTime()); | |
3038 msframe->button2_is_down = 1; | |
428 | 3039 } |
442 | 3040 else |
428 | 3041 { |
442 | 3042 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, |
3043 msframe->last_click_point, | |
3044 msframe->last_click_mods | |
3045 &~ MK_RBUTTON, | |
3046 msframe->last_click_time); | |
3047 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, | |
2367 | 3048 XE_MAKEPOINTS (lParam), |
442 | 3049 wParam &~ MK_LBUTTON, |
3050 GetMessageTime()); | |
428 | 3051 } |
3052 } | |
3053 else | |
442 | 3054 { |
3055 mswindows_set_chord_timer (hwnd); | |
3056 msframe->button2_need_rbutton = 1; | |
2367 | 3057 msframe->last_click_point = XE_MAKEPOINTS (lParam); |
442 | 3058 msframe->last_click_mods = wParam; |
3059 } | |
771 | 3060 msframe->last_click_time = GetMessageTime(); |
442 | 3061 break; |
3062 | |
3063 case WM_RBUTTONDOWN: | |
771 | 3064 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3065 |
3066 if (msframe->button2_need_rbutton) | |
428 | 3067 { |
442 | 3068 KillTimer (hwnd, BUTTON_2_TIMER_ID); |
3069 msframe->button2_need_lbutton = 0; | |
3070 msframe->button2_need_rbutton = 0; | |
3071 if (mswindows_button2_near_enough (msframe->last_click_point, | |
2367 | 3072 XE_MAKEPOINTS (lParam))) |
442 | 3073 { |
3074 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, | |
2367 | 3075 XE_MAKEPOINTS (lParam), |
442 | 3076 wParam |
3077 &~ (MK_LBUTTON | MK_MBUTTON | |
3078 | MK_RBUTTON), | |
3079 GetMessageTime()); | |
3080 msframe->button2_is_down = 1; | |
3081 } | |
3082 else | |
3083 { | |
3084 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, | |
3085 msframe->last_click_point, | |
3086 msframe->last_click_mods | |
3087 &~ MK_LBUTTON, | |
3088 msframe->last_click_time); | |
3089 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, | |
2367 | 3090 XE_MAKEPOINTS (lParam), |
442 | 3091 wParam &~ MK_RBUTTON, |
3092 GetMessageTime()); | |
3093 } | |
428 | 3094 } |
3095 else | |
3096 { | |
442 | 3097 mswindows_set_chord_timer (hwnd); |
3098 msframe->button2_need_lbutton = 1; | |
2367 | 3099 msframe->last_click_point = XE_MAKEPOINTS (lParam); |
442 | 3100 msframe->last_click_mods = wParam; |
3101 } | |
771 | 3102 msframe->last_click_time = GetMessageTime(); |
442 | 3103 break; |
3104 | |
3105 case WM_TIMER: | |
3106 if (wParam == BUTTON_2_TIMER_ID) | |
3107 { | |
771 | 3108 msframe = |
3109 FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); | |
442 | 3110 KillTimer (hwnd, BUTTON_2_TIMER_ID); |
3111 | |
3112 if (msframe->button2_need_lbutton) | |
3113 { | |
3114 msframe->button2_need_lbutton = 0; | |
3115 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, | |
3116 msframe->last_click_point, | |
3117 msframe->last_click_mods | |
3118 &~ MK_RBUTTON, | |
3119 msframe->last_click_time); | |
3120 } | |
3121 else if (msframe->button2_need_rbutton) | |
3122 { | |
3123 msframe->button2_need_rbutton = 0; | |
3124 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, | |
3125 msframe->last_click_point, | |
3126 msframe->last_click_mods | |
3127 &~ MK_LBUTTON, | |
3128 msframe->last_click_time); | |
3129 } | |
3130 } | |
3131 else | |
3132 assert ("Spurious timer fired" == 0); | |
3133 break; | |
3134 | |
3135 case WM_MOUSEMOVE: | |
3136 /* Optimization: don't report mouse movement while size is changing */ | |
771 | 3137 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3138 if (!msframe->sizing) |
3139 { | |
3140 /* When waiting for the second mouse button to finish | |
3141 button2 emulation, and have moved too far, just pretend | |
3142 as if timer has expired. This improves drag-select feedback */ | |
3143 if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton) | |
3144 && !mswindows_button2_near_enough (msframe->last_click_point, | |
2367 | 3145 XE_MAKEPOINTS (lParam))) |
428 | 3146 { |
442 | 3147 KillTimer (hwnd, BUTTON_2_TIMER_ID); |
771 | 3148 qxeSendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0); |
442 | 3149 } |
3150 | |
3151 emacs_event = Fmake_event (Qnil, Qnil); | |
3152 event = XEVENT(emacs_event); | |
3153 | |
964 | 3154 XSET_EVENT_CHANNEL (emacs_event, mswindows_find_frame(hwnd)); |
3155 XSET_EVENT_TIMESTAMP (emacs_event, GetMessageTime()); | |
3156 XSET_EVENT_TYPE (emacs_event, pointer_motion_event); | |
2367 | 3157 XSET_EVENT_MOTION_X (emacs_event, XE_MAKEPOINTS (lParam).x); |
3158 XSET_EVENT_MOTION_Y (emacs_event, XE_MAKEPOINTS (lParam).y); | |
1204 | 3159 XSET_EVENT_MOTION_MODIFIERS (emacs_event, |
964 | 3160 mswindows_modifier_state (NULL, wParam, 0)); |
442 | 3161 |
3162 mswindows_enqueue_dispatch_event (emacs_event); | |
3163 } | |
3164 break; | |
3165 | |
3166 case WM_CANCELMODE: | |
3167 ReleaseCapture (); | |
3168 /* Queue a `cancel-mode-internal' misc user event, so mouse | |
3169 selection would be canceled if any */ | |
3170 mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd), | |
3171 Qcancel_mode_internal, Qnil); | |
3172 break; | |
3173 | |
3174 case WM_NOTIFY: | |
3175 { | |
647 | 3176 LPNMHDR nmhdr = (LPNMHDR) lParam; |
3177 | |
1111 | 3178 if (nmhdr->code == TTN_NEEDTEXT) |
442 | 3179 { |
3180 #ifdef HAVE_TOOLBARS | |
771 | 3181 LPTOOLTIPTEXTW tttextw = (LPTOOLTIPTEXTW) lParam; |
442 | 3182 Lisp_Object btext; |
771 | 3183 Extbyte *btextext = 0; |
442 | 3184 |
3185 /* find out which toolbar */ | |
3186 frame = XFRAME (mswindows_find_frame (hwnd)); | |
647 | 3187 btext = mswindows_get_toolbar_button_text (frame, nmhdr->idFrom); |
442 | 3188 |
771 | 3189 tttextw->hinst = NULL; |
3190 | |
3191 if (!NILP (btext)) | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
3192 btextext = LISP_STRING_TO_TSTR (btext); |
771 | 3193 |
3194 if (btextext) | |
442 | 3195 { |
771 | 3196 /* WARNING: We can't just write a '\0' into the 79th |
3197 "character" because tttextw->szText is in WCHAR's but we | |
3198 may be copying an ANSI string into it. Easiest to just | |
3199 zero the whole thing. */ | |
3200 xzero (*tttextw->szText); | |
2421 | 3201 qxetcsncpy ((Extbyte *) tttextw->szText, btextext, 79); |
442 | 3202 } |
771 | 3203 else |
3204 tttextw->lpszText = NULL; | |
442 | 3205 #endif |
3206 } | |
3207 /* handle tree view callbacks */ | |
1111 | 3208 else if (nmhdr->code == TVN_SELCHANGED) |
442 | 3209 { |
647 | 3210 NM_TREEVIEW *ptree = (NM_TREEVIEW *) lParam; |
442 | 3211 frame = XFRAME (mswindows_find_frame (hwnd)); |
3212 mswindows_handle_gui_wm_command (frame, 0, ptree->itemNew.lParam); | |
3213 } | |
3214 /* handle tab control callbacks */ | |
1111 | 3215 else if (nmhdr->code == TCN_SELCHANGE) |
442 | 3216 { |
3217 TC_ITEM item; | |
771 | 3218 int idx = qxeSendMessage (nmhdr->hwndFrom, TCM_GETCURSEL, 0, 0); |
442 | 3219 frame = XFRAME (mswindows_find_frame (hwnd)); |
3220 | |
3221 item.mask = TCIF_PARAM; | |
771 | 3222 qxeSendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM) idx, |
3223 (LPARAM) &item); | |
442 | 3224 |
3225 mswindows_handle_gui_wm_command (frame, 0, item.lParam); | |
3226 } | |
3227 } | |
3228 break; | |
3229 | |
3230 case WM_PAINT: | |
3231 /* hdc will be NULL unless this is a subwindow - in which case we | |
3232 shouldn't have received a paint message for it here. */ | |
3233 assert (wParam == 0); | |
3234 | |
3235 /* Can't queue a magic event because windows goes modal and sends paint | |
3236 messages directly to the windows procedure when doing solid drags | |
3237 and the message queue doesn't get processed. */ | |
3238 mswindows_handle_paint (XFRAME (mswindows_find_frame (hwnd))); | |
3239 break; | |
3240 | |
903 | 3241 case WM_ACTIVATE: |
3242 { | |
3243 /* | |
3244 * If we receive a WM_ACTIVATE message that indicates that our frame | |
3245 * is being activated, make sure that the frame is marked visible | |
3246 * if the window itself is visible. This seems to fix the problem | |
3247 * where XEmacs appears to lock-up after switching desktops with | |
3248 * some virtual window managers. | |
3249 */ | |
3250 int state = (int)(short) LOWORD(wParam); | |
3251 #ifdef DEBUG_XEMACS | |
3252 if (debug_mswindows_events) | |
3253 stderr_out("state = %d\n", state); | |
3254 #endif /* DEBUG_XEMACS */ | |
3255 if (state == WA_ACTIVE || state == WA_CLICKACTIVE) | |
3256 { | |
3257 #ifdef DEBUG_XEMACS | |
3258 if (debug_mswindows_events) | |
3259 stderr_out(" activating\n"); | |
3260 #endif /* DEBUG_XEMACS */ | |
3261 | |
3262 fobj = mswindows_find_frame (hwnd); | |
3263 frame = XFRAME (fobj); | |
3264 if (IsWindowVisible (hwnd)) | |
3265 { | |
3266 #ifdef DEBUG_XEMACS | |
3267 if (debug_mswindows_events) | |
3268 stderr_out(" window is visible\n"); | |
3269 #endif /* DEBUG_XEMACS */ | |
3270 if (!FRAME_VISIBLE_P (frame)) | |
3271 { | |
3272 #ifdef DEBUG_XEMACS | |
3273 if (debug_mswindows_events) | |
3274 stderr_out(" frame is not visible\n"); | |
3275 #endif /* DEBUG_XEMACS */ | |
3276 /* | |
3277 * It seems that we have to enqueue the XM_MAPFRAME event | |
3278 * prior to setting the frame visible so that | |
3279 * suspend-or-iconify-emacs works properly. | |
3280 */ | |
3281 mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME); | |
3282 FRAME_VISIBLE_P (frame) = 1; | |
3283 FRAME_ICONIFIED_P (frame) = 0; | |
3284 } | |
3285 #ifdef DEBUG_XEMACS | |
3286 else | |
3287 { | |
3288 if (debug_mswindows_events) | |
3289 stderr_out(" frame is visible\n"); | |
3290 } | |
3291 #endif /* DEBUG_XEMACS */ | |
3292 } | |
3293 #ifdef DEBUG_XEMACS | |
3294 else | |
3295 { | |
3296 if (debug_mswindows_events) | |
3297 stderr_out(" window is not visible\n"); | |
3298 } | |
3299 #endif /* DEBUG_XEMACS */ | |
3300 } | |
3301 return qxeDefWindowProc (hwnd, message_, wParam, lParam); | |
3302 } | |
3303 break; | |
3304 | |
593 | 3305 case WM_WINDOWPOSCHANGED: |
3306 /* This is sent before WM_SIZE; in fact, the processing of this | |
3307 by DefWindowProc() sends WM_SIZE. But WM_SIZE is not sent when | |
3308 a window is hidden (make-frame-invisible), so we need to process | |
3309 this and update the state flags. */ | |
3310 { | |
3311 fobj = mswindows_find_frame (hwnd); | |
3312 frame = XFRAME (fobj); | |
3313 if (IsIconic (hwnd)) | |
3314 { | |
3315 FRAME_VISIBLE_P (frame) = 0; | |
3316 FRAME_ICONIFIED_P (frame) = 1; | |
3317 } | |
3318 else if (IsWindowVisible (hwnd)) | |
3319 { | |
707 | 3320 /* APA: It's too early here to set the frame visible. |
3321 * Let's do this later, in WM_SIZE processing, after the | |
3322 * magic XM_MAPFRAME event has been sent (just like 21.1 | |
3323 * did). */ | |
3324 /* FRAME_VISIBLE_P (frame) = 1; */ | |
593 | 3325 FRAME_ICONIFIED_P (frame) = 0; |
3326 } | |
3327 else | |
3328 { | |
3329 FRAME_VISIBLE_P (frame) = 0; | |
3330 FRAME_ICONIFIED_P (frame) = 0; | |
3331 } | |
3332 | |
771 | 3333 goto defproc; |
593 | 3334 } |
3335 | |
731 | 3336 case WM_SHOWWINDOW: |
3337 /* | |
3338 The WM_SHOWWINDOW message is sent to a window when the window | |
3339 is about to be hidden or shown. | |
3340 APA: This message is also sent when switching to a virtual | |
3341 desktop under the virtuawin virtual window manager. | |
3342 | |
3343 */ | |
3344 { | |
3345 fobj = mswindows_find_frame (hwnd); | |
3346 frame = XFRAME (fobj); | |
3347 if (wParam == TRUE) | |
3348 { | |
3349 mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME); | |
3350 FRAME_VISIBLE_P (frame) = 1; | |
3351 } | |
3352 else | |
3353 { | |
3354 mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME); | |
3355 FRAME_VISIBLE_P (frame) = 0; | |
3356 } | |
3357 } | |
3358 break; | |
3359 | |
442 | 3360 case WM_SIZE: |
3361 /* We only care about this message if our size has really changed */ | |
771 | 3362 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || |
3363 wParam == SIZE_MINIMIZED) | |
442 | 3364 { |
3365 RECT rect; | |
3366 int columns, rows; | |
3367 | |
3368 fobj = mswindows_find_frame (hwnd); | |
3369 frame = XFRAME (fobj); | |
771 | 3370 msframe = FRAME_MSWINDOWS_DATA (frame); |
442 | 3371 |
3372 /* We cannot handle frame map and unmap hooks right in | |
3373 this routine, because these may throw. We queue | |
3374 magic events to run these hooks instead - kkm */ | |
3375 | |
771 | 3376 if (wParam == SIZE_MINIMIZED) |
442 | 3377 { |
3378 /* Iconified */ | |
3379 mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME); | |
428 | 3380 } |
3381 else | |
3382 { | |
1279 | 3383 GetClientRect (hwnd, &rect); |
3384 FRAME_PIXWIDTH (frame) = rect.right; | |
3385 FRAME_PIXHEIGHT (frame) = rect.bottom; | |
442 | 3386 |
5043 | 3387 pixel_to_frame_unit_size (frame, rect.right, rect.bottom, &columns, |
771 | 3388 &rows); |
5043 | 3389 change_frame_size (frame, columns, rows, 1); |
442 | 3390 |
3391 /* If we are inside frame creation, we have to apply geometric | |
3392 properties now. */ | |
3393 if (FRAME_MSWINDOWS_TARGET_RECT (frame)) | |
3394 { | |
3395 /* Yes, we have to size again */ | |
771 | 3396 mswindows_size_frame_internal (frame, |
3397 FRAME_MSWINDOWS_TARGET_RECT | |
3398 (frame)); | |
3399 /* Reset so we do not get here again. The SetWindowPos | |
3400 * call in mswindows_size_frame_internal can cause | |
3401 * recursion here. */ | |
442 | 3402 if (FRAME_MSWINDOWS_TARGET_RECT (frame)) |
3403 { | |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
3404 xfree (FRAME_MSWINDOWS_TARGET_RECT (frame)); |
442 | 3405 FRAME_MSWINDOWS_TARGET_RECT (frame) = 0; |
3406 } | |
3407 } | |
3408 else | |
3409 { | |
903 | 3410 if (!msframe->sizing && !FRAME_VISIBLE_P (frame)) |
3411 { | |
3412 mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME); | |
3413 /* APA: Now that the magic XM_MAPFRAME event has | |
3414 * been sent we can mark the frame as visible (just | |
3415 * like 21.1 did). */ | |
3416 FRAME_VISIBLE_P (frame) = 1; | |
3417 } | |
442 | 3418 |
1279 | 3419 if (frame->init_finished && |
3420 (!msframe->sizing || mswindows_dynamic_frame_resize)) | |
442 | 3421 redisplay (); |
3422 } | |
428 | 3423 } |
3424 } | |
442 | 3425 break; |
3426 | |
3427 case WM_DISPLAYCHANGE: | |
3428 { | |
3429 struct device *d; | |
3430 DWORD message_tick = GetMessageTime (); | |
3431 | |
3432 fobj = mswindows_find_frame (hwnd); | |
3433 frame = XFRAME (fobj); | |
3434 d = XDEVICE (FRAME_DEVICE (frame)); | |
3435 | |
3436 /* Do this only once per message. XEmacs can receive this message | |
3437 through as many frames as it currently has open. Message time | |
3438 will be the same for all these messages. Despite extreme | |
3439 efficiency, the code below has about one in 4 billion | |
3440 probability that the HDC is not recreated, provided that | |
3441 XEmacs is running sufficiently longer than 52 days. */ | |
1279 | 3442 if (DEVICE_MSWINDOWS_UPDATE_TICK (d) != message_tick) |
442 | 3443 { |
1279 | 3444 DEVICE_MSWINDOWS_UPDATE_TICK (d) = message_tick; |
3445 DeleteDC (DEVICE_MSWINDOWS_HCDC (d)); | |
3446 DEVICE_MSWINDOWS_HCDC (d) = CreateCompatibleDC (NULL); | |
442 | 3447 } |
3448 } | |
3449 break; | |
3450 | |
3451 /* Misc magic events which only require that the frame be identified */ | |
3452 case WM_SETFOCUS: | |
3453 case WM_KILLFOCUS: | |
3454 mswindows_enqueue_magic_event (hwnd, message_); | |
3455 break; | |
3456 | |
3457 case WM_WINDOWPOSCHANGING: | |
428 | 3458 { |
442 | 3459 WINDOWPOS *wp = (LPWINDOWPOS) lParam; |
3460 WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) }; | |
3461 GetWindowPlacement(hwnd, &wpl); | |
3462 | |
3463 /* Only interested if size is changing and we're not being iconified */ | |
3464 if (wpl.showCmd != SW_SHOWMINIMIZED | |
3465 && wpl.showCmd != SW_SHOWMAXIMIZED | |
3466 && !(wp->flags & SWP_NOSIZE)) | |
428 | 3467 { |
442 | 3468 RECT ncsize = { 0, 0, 0, 0 }; |
3469 int pixwidth, pixheight; | |
771 | 3470 AdjustWindowRectEx (&ncsize, qxeGetWindowLong (hwnd, GWL_STYLE), |
442 | 3471 GetMenu(hwnd) != NULL, |
771 | 3472 qxeGetWindowLong (hwnd, GWL_EXSTYLE)); |
442 | 3473 |
5043 | 3474 round_size_to_char (XFRAME (mswindows_find_frame (hwnd)), |
442 | 3475 wp->cx - (ncsize.right - ncsize.left), |
3476 wp->cy - (ncsize.bottom - ncsize.top), | |
3477 &pixwidth, &pixheight); | |
3478 | |
3479 /* Convert client sizes to window sizes */ | |
3480 pixwidth += (ncsize.right - ncsize.left); | |
3481 pixheight += (ncsize.bottom - ncsize.top); | |
3482 | |
3483 if (wpl.showCmd != SW_SHOWMAXIMIZED) | |
3484 { | |
3485 /* Adjust so that the bottom or right doesn't move if it's | |
3486 * the top or left that's being changed */ | |
3487 RECT rect; | |
3488 GetWindowRect (hwnd, &rect); | |
3489 | |
3490 if (rect.left != wp->x) | |
3491 wp->x += wp->cx - pixwidth; | |
3492 if (rect.top != wp->y) | |
3493 wp->y += wp->cy - pixheight; | |
3494 } | |
3495 | |
3496 wp->cx = pixwidth; | |
3497 wp->cy = pixheight; | |
428 | 3498 } |
442 | 3499 /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts |
3500 window position if the user tries to track window too small */ | |
428 | 3501 } |
442 | 3502 goto defproc; |
3503 | |
3504 case WM_ENTERSIZEMOVE: | |
771 | 3505 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3506 msframe->sizing = 1; |
3507 return 0; | |
3508 | |
3509 case WM_EXITSIZEMOVE: | |
771 | 3510 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3511 msframe->sizing = 0; |
3512 /* Queue noop event */ | |
3513 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); | |
3514 return 0; | |
428 | 3515 |
3516 #ifdef HAVE_SCROLLBARS | |
442 | 3517 case WM_VSCROLL: |
3518 case WM_HSCROLL: | |
3519 { | |
3520 /* Direction of scroll is determined by scrollbar instance. */ | |
1279 | 3521 int code = (int) LOWORD (wParam); |
3522 int pos = (short int) HIWORD (wParam); | |
442 | 3523 HWND hwndScrollBar = (HWND) lParam; |
3524 struct gcpro gcpro1, gcpro2; | |
3525 | |
3526 mswindows_handle_scrollbar_event (hwndScrollBar, code, pos); | |
3527 GCPRO2 (emacs_event, fobj); | |
853 | 3528 if (UNBOUNDP (mswindows_pump_outstanding_events ())) /* Can GC */ |
442 | 3529 { |
3530 /* Error during event pumping - cancel scroll */ | |
771 | 3531 qxeSendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0); |
442 | 3532 } |
3533 UNGCPRO; | |
3534 break; | |
3535 } | |
3536 | |
3537 case WM_MOUSEWHEEL: | |
3538 { | |
3539 int keys = LOWORD (wParam); /* Modifier key flags */ | |
3540 int delta = (short) HIWORD (wParam); /* Wheel rotation amount */ | |
3541 | |
1622 | 3542 /* enqueue button4/5 events if mswindows_handle_mousewheel_event |
3543 doesn't handle the event, such as when the scrollbars are not | |
3544 displayed */ | |
3545 if (!mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd), | |
464 | 3546 keys, delta, |
2367 | 3547 XE_MAKEPOINTS (lParam))) |
1622 | 3548 mswindows_enqueue_mouse_button_event (hwnd, message_, |
2367 | 3549 XE_MAKEPOINTS (lParam), |
1622 | 3550 wParam, |
3551 GetMessageTime()); | |
3552 /* We are not in a modal loop so no pumping is necessary. */ | |
3553 break; | |
442 | 3554 } |
428 | 3555 #endif |
3556 | |
3557 #ifdef HAVE_MENUBARS | |
442 | 3558 case WM_INITMENU: |
771 | 3559 if (UNBOUNDP (mswindows_handle_wm_initmenu |
3560 ((HMENU) wParam, | |
3561 XFRAME (mswindows_find_frame (hwnd))))) | |
3562 qxeSendMessage (hwnd, WM_CANCELMODE, 0, 0); | |
442 | 3563 break; |
3564 | |
3565 case WM_INITMENUPOPUP: | |
3566 if (!HIWORD(lParam)) | |
3567 { | |
771 | 3568 if (UNBOUNDP (mswindows_handle_wm_initmenupopup |
3569 ((HMENU) wParam, | |
3570 XFRAME (mswindows_find_frame (hwnd))))) | |
3571 qxeSendMessage (hwnd, WM_CANCELMODE, 0, 0); | |
442 | 3572 } |
3573 break; | |
428 | 3574 |
3575 #endif /* HAVE_MENUBARS */ | |
3576 | |
442 | 3577 case WM_COMMAND: |
3578 { | |
3579 WORD id = LOWORD (wParam); | |
3580 WORD nid = HIWORD (wParam); | |
3581 HWND cid = (HWND)lParam; | |
3582 frame = XFRAME (mswindows_find_frame (hwnd)); | |
428 | 3583 |
3584 #ifdef HAVE_TOOLBARS | |
442 | 3585 if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id))) |
3586 break; | |
428 | 3587 #endif |
771 | 3588 /* widgets in a buffer only eval a callback for suitable events. */ |
442 | 3589 switch (nid) |
3590 { | |
3591 case BN_CLICKED: | |
3592 case EN_CHANGE: | |
3593 case CBN_EDITCHANGE: | |
3594 case CBN_SELCHANGE: | |
3595 if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id))) | |
3596 return 0; | |
3597 } | |
3598 /* menubars always must come last since the hashtables do not | |
771 | 3599 always exist */ |
428 | 3600 #ifdef HAVE_MENUBARS |
442 | 3601 if (!NILP (mswindows_handle_wm_command (frame, id))) |
3602 break; | |
428 | 3603 #endif |
3604 | |
771 | 3605 goto defproc; |
3606 /* Bite me - a spurious command. This used to not be able to | |
3607 happen but with the introduction of widgets it's now | |
3608 possible. #### Andy, fix the god-damn widget code! It has | |
3609 more bugs than a termite's nest! */ | |
442 | 3610 } |
3611 break; | |
3612 | |
3613 case WM_CTLCOLORBTN: | |
3614 case WM_CTLCOLORLISTBOX: | |
3615 case WM_CTLCOLOREDIT: | |
3616 case WM_CTLCOLORSTATIC: | |
3617 case WM_CTLCOLORSCROLLBAR: | |
3618 { | |
3619 /* if we get an opportunity to paint a widget then do so if | |
3620 there is an appropriate face */ | |
771 | 3621 HWND crtlwnd = (HWND) lParam; |
3622 LONG ii = qxeGetWindowLong (crtlwnd, GWL_USERDATA); | |
442 | 3623 if (ii) |
3624 { | |
3625 Lisp_Object image_instance; | |
5013 | 3626 image_instance = GET_LISP_FROM_VOID ((void *) ii); |
442 | 3627 if (IMAGE_INSTANCEP (image_instance) |
3628 && | |
3629 IMAGE_INSTANCE_TYPE_P (image_instance, IMAGE_WIDGET)) | |
3630 { | |
3631 /* set colors for the buttons */ | |
771 | 3632 HDC hdc = (HDC) wParam; |
442 | 3633 if (last_widget_brushed != ii) |
3634 { | |
3635 if (widget_brush) | |
3636 DeleteObject (widget_brush); | |
3637 widget_brush = CreateSolidBrush | |
3638 (COLOR_INSTANCE_MSWINDOWS_COLOR | |
3639 (XCOLOR_INSTANCE | |
3640 (FACE_BACKGROUND | |
3641 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), | |
3642 XIMAGE_INSTANCE_FRAME (image_instance))))); | |
3643 } | |
3644 last_widget_brushed = ii; | |
3645 SetTextColor | |
3646 (hdc, | |
3647 COLOR_INSTANCE_MSWINDOWS_COLOR | |
3648 (XCOLOR_INSTANCE | |
3649 (FACE_FOREGROUND | |
3650 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), | |
3651 XIMAGE_INSTANCE_FRAME (image_instance))))); | |
3652 SetBkMode (hdc, OPAQUE); | |
3653 SetBkColor | |
3654 (hdc, | |
3655 COLOR_INSTANCE_MSWINDOWS_COLOR | |
3656 (XCOLOR_INSTANCE | |
3657 (FACE_BACKGROUND | |
3658 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), | |
3659 XIMAGE_INSTANCE_FRAME (image_instance))))); | |
3660 return (LRESULT)widget_brush; | |
3661 } | |
3662 } | |
3663 } | |
3664 goto defproc; | |
428 | 3665 |
3666 #ifdef HAVE_DRAGNDROP | |
853 | 3667 case WM_DROPFILES: /* implementation ripped-off from event-Xt.c */ |
442 | 3668 { |
771 | 3669 UINT filecount, i; |
442 | 3670 POINT point; |
3671 | |
3672 Lisp_Object l_dndlist = Qnil, l_item = Qnil; | |
3673 struct gcpro gcpro1, gcpro2, gcpro3; | |
3674 | |
3675 emacs_event = Fmake_event (Qnil, Qnil); | |
771 | 3676 event = XEVENT (emacs_event); |
442 | 3677 |
3678 GCPRO3 (emacs_event, l_dndlist, l_item); | |
3679 | |
3680 if (!DragQueryPoint ((HDROP) wParam, &point)) | |
853 | 3681 point.x = point.y = -1; /* outside client area */ |
442 | 3682 |
964 | 3683 XSET_EVENT_TYPE (emacs_event, misc_user_event); |
3684 XSET_EVENT_CHANNEL (emacs_event, mswindows_find_frame(hwnd)); | |
3685 XSET_EVENT_TIMESTAMP (emacs_event, GetMessageTime()); | |
1204 | 3686 XSET_EVENT_MISC_USER_BUTTON (emacs_event, 1); |
3687 XSET_EVENT_MISC_USER_MODIFIERS (emacs_event, | |
964 | 3688 mswindows_modifier_state (NULL, (DWORD) -1, 0)); |
1204 | 3689 XSET_EVENT_MISC_USER_X (emacs_event, point.x); |
3690 XSET_EVENT_MISC_USER_Y (emacs_event, point.y); | |
3691 XSET_EVENT_MISC_USER_FUNCTION (emacs_event, | |
964 | 3692 Qdragdrop_drop_dispatch); |
442 | 3693 |
771 | 3694 filecount = qxeDragQueryFile ((HDROP) wParam, 0xffffffff, NULL, 0); |
3695 for (i = 0; i < filecount; i++) | |
442 | 3696 { |
867 | 3697 Ibyte *fname; |
771 | 3698 Extbyte *fname_ext; |
3699 Bytecount fnamelen; | |
3700 Charcount len = qxeDragQueryFile ((HDROP) wParam, i, NULL, 0); | |
2526 | 3701 int freeme = 0; |
442 | 3702 /* The URLs that we make here aren't correct according to section |
3703 * 3.10 of rfc1738 because they're missing the //<host>/ part and | |
3704 * because they may contain reserved characters. But that's OK - | |
3705 * they just need to be good enough to keep dragdrop.el happy. */ | |
2367 | 3706 fname_ext = alloca_extbytes ((len + 1) * XETCHAR_SIZE); |
771 | 3707 qxeDragQueryFile ((HDROP) wParam, i, fname_ext, len + 1); |
3708 | |
3709 TO_INTERNAL_FORMAT (DATA, (fname_ext, len * XETCHAR_SIZE), | |
3710 ALLOCA, (fname, fnamelen), | |
3711 Qmswindows_tstr); | |
442 | 3712 |
2526 | 3713 |
442 | 3714 /* May be a shell link aka "shortcut" - replace fname if so */ |
2367 | 3715 if (!qxestrcasecmp_ascii (fname + fnamelen - 4, ".LNK")) |
442 | 3716 { |
2526 | 3717 fname = mswindows_read_link (fname); |
3718 freeme = 1; | |
442 | 3719 } |
2526 | 3720 |
771 | 3721 { |
2526 | 3722 Ibyte *fname2 = urlify_filename (fname); |
4953
304aebb79cd3
function renamings to track names of char typedefs
Ben Wing <ben@xemacs.org>
parents:
4952
diff
changeset
|
3723 l_item = build_istring (fname2); |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
3724 xfree (fname2); |
2526 | 3725 if (freeme) |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
3726 xfree (fname); |
771 | 3727 l_dndlist = Fcons (l_item, l_dndlist); |
3728 } | |
442 | 3729 } |
771 | 3730 |
442 | 3731 DragFinish ((HDROP) wParam); |
3732 | |
1204 | 3733 SET_EVENT_MISC_USER_OBJECT (event, |
964 | 3734 Fcons (Qdragdrop_URL, l_dndlist)); |
442 | 3735 mswindows_enqueue_dispatch_event (emacs_event); |
3736 UNGCPRO; | |
3737 } | |
3738 break; | |
771 | 3739 #endif /* HAVE_DRAGNDROP */ |
3740 | |
3741 #ifdef MULE | |
3742 case WM_IME_CHAR: | |
3743 | |
3744 case WM_IME_STARTCOMPOSITION: | |
3745 mswindows_start_ime_composition (XFRAME (mswindows_find_frame (hwnd))); | |
3746 goto defproc; | |
3747 | |
3748 case WM_IME_COMPOSITION: | |
3749 if (lParam & GCS_RESULTSTR) | |
3750 { | |
3751 HIMC imc = ImmGetContext (hwnd); | |
3752 Extbyte *result; | |
3753 Bytecount len; | |
867 | 3754 Ibyte *resultint, *endptr; |
771 | 3755 Bytecount lenint; |
3756 int speccount; | |
3757 | |
3758 if (!imc) | |
3759 break; | |
3760 | |
3761 /* See WM_KEYDOWN above. */ | |
3762 speccount = begin_dont_check_for_quit (); | |
3763 | |
3764 /* Sizes always in bytes, even for unicode. | |
3765 ImmGetCompositionStringW is supported even on Windows 9x, and | |
3766 allows us to handle multiple languages. */ | |
3767 len = ImmGetCompositionStringW (imc, GCS_RESULTSTR, NULL, 0); | |
2367 | 3768 result = alloca_extbytes (len); |
771 | 3769 ImmGetCompositionStringW (imc, GCS_RESULTSTR, (WCHAR *) result, len); |
3770 ImmReleaseContext (hwnd, imc); | |
3771 | |
3772 TO_INTERNAL_FORMAT (DATA, (result, len), | |
3773 ALLOCA, (resultint, lenint), | |
3774 Qmswindows_tstr); | |
3775 | |
3776 endptr = resultint + lenint; | |
3777 | |
3778 while (resultint < endptr) | |
3779 { | |
867 | 3780 Ichar ch = itext_ichar (resultint); |
771 | 3781 if (ch == ' ') |
3782 mswindows_enqueue_keypress_event (hwnd, QKspace, 0); | |
3783 else | |
3784 mswindows_enqueue_keypress_event (hwnd, make_char (ch), 0); | |
867 | 3785 INC_IBYTEPTR (resultint); |
771 | 3786 } |
3787 | |
3788 unbind_to (speccount); | |
3789 } | |
3790 goto defproc; | |
3791 #endif /* MULE */ | |
442 | 3792 |
3793 defproc: | |
3794 default: | |
771 | 3795 return qxeDefWindowProc (hwnd, message_, wParam, lParam); |
428 | 3796 } |
3797 return (0); | |
3798 } | |
3799 | |
3800 | |
3801 /************************************************************************/ | |
3802 /* keyboard, mouse & other helpers for the windows procedure */ | |
3803 /************************************************************************/ | |
3804 static void | |
3805 mswindows_set_chord_timer (HWND hwnd) | |
3806 { | |
3807 int interval; | |
3808 | |
3809 /* We get one third half system double click threshold */ | |
3810 if (mswindows_mouse_button_tolerance <= 0) | |
3811 interval = GetDoubleClickTime () / 3; | |
3812 else | |
3813 interval = mswindows_mouse_button_tolerance; | |
3814 | |
3815 SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0); | |
3816 } | |
3817 | |
3818 static int | |
3819 mswindows_button2_near_enough (POINTS p1, POINTS p2) | |
3820 { | |
3821 int dx, dy; | |
3822 if (mswindows_mouse_button_max_skew_x <= 0) | |
3823 dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2; | |
3824 else | |
3825 dx = mswindows_mouse_button_max_skew_x; | |
3826 | |
3827 if (mswindows_mouse_button_max_skew_y <= 0) | |
3828 dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2; | |
3829 else | |
3830 dy = mswindows_mouse_button_max_skew_y; | |
3831 | |
3832 return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy; | |
3833 } | |
3834 | |
3835 static int | |
3836 mswindows_current_layout_has_AltGr (void) | |
3837 { | |
3838 /* This simple caching mechanism saves 10% of CPU | |
3839 time when a key typed at autorepeat rate of 30 cps! */ | |
3840 static HKL last_hkl = 0; | |
3841 static int last_hkl_has_AltGr; | |
771 | 3842 HKL current_hkl = GetKeyboardLayout (0); |
3843 | |
428 | 3844 if (current_hkl != last_hkl) |
3845 { | |
647 | 3846 int c; |
428 | 3847 last_hkl_has_AltGr = 0; |
3848 /* In this loop, we query whether a character requires | |
3849 AltGr to be down to generate it. If at least such one | |
3850 found, this means that the layout does regard AltGr */ | |
647 | 3851 for (c = ' '; c <= 255 && !last_hkl_has_AltGr; ++c) |
3852 /* #### This is not really such a good check. What about under | |
3853 CJK locales? It may not matter there, though. We always | |
3854 call VkKeyScanA so that we check the locale-specific characters | |
3855 in non-Latin-1 locales, instead of just the Latin-1 characters. */ | |
3856 if (HIBYTE (VkKeyScanA ((char) c)) == 6) | |
428 | 3857 last_hkl_has_AltGr = 1; |
3858 last_hkl = current_hkl; | |
3859 } | |
3860 return last_hkl_has_AltGr; | |
3861 } | |
3862 | |
3863 | |
3864 /* Returns the state of the modifier keys in the format expected by the | |
3865 * Lisp_Event key_data, button_data and motion_data modifiers member */ | |
442 | 3866 static int |
771 | 3867 mswindows_modifier_state (BYTE *keymap, DWORD fwKeys, int has_AltGr) |
428 | 3868 { |
3869 int mods = 0; | |
442 | 3870 int keys_is_real = 0; |
3871 BYTE keymap2[256]; | |
3872 | |
3873 if (fwKeys == (DWORD) -1) | |
3874 fwKeys = mswindows_last_mouse_button_state; | |
3875 else | |
3876 { | |
3877 keys_is_real = 1; | |
3878 mswindows_last_mouse_button_state = fwKeys; | |
3879 } | |
428 | 3880 |
3881 if (keymap == NULL) | |
3882 { | |
442 | 3883 keymap = keymap2; |
428 | 3884 GetKeyboardState (keymap); |
3885 has_AltGr = mswindows_current_layout_has_AltGr (); | |
3886 } | |
3887 | |
442 | 3888 /* #### should look at fwKeys for MK_CONTROL. I don't understand how |
3889 AltGr works. */ | |
428 | 3890 if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80)) |
3891 { | |
442 | 3892 mods |= (keymap [VK_LMENU] & 0x80) ? XEMACS_MOD_META : 0; |
3893 mods |= (keymap [VK_RCONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0; | |
428 | 3894 } |
3895 else | |
3896 { | |
442 | 3897 mods |= (keymap [VK_MENU] & 0x80) ? XEMACS_MOD_META : 0; |
3898 mods |= (keymap [VK_CONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0; | |
428 | 3899 } |
3900 | |
1111 | 3901 mods |= (keys_is_real ? (int) (fwKeys & MK_SHIFT) : |
3902 (keymap [VK_SHIFT] & 0x80)) ? XEMACS_MOD_SHIFT : 0; | |
442 | 3903 mods |= fwKeys & MK_LBUTTON ? XEMACS_MOD_BUTTON1 : 0; |
3904 mods |= fwKeys & MK_MBUTTON ? XEMACS_MOD_BUTTON2 : 0; | |
3905 mods |= fwKeys & MK_RBUTTON ? XEMACS_MOD_BUTTON3 : 0; | |
428 | 3906 |
3907 return mods; | |
3908 } | |
3909 | |
3910 /* | |
3911 * Translate a mswindows virtual key to a keysym. | |
3912 * Only returns non-Qnil for keys that don't generate WM_CHAR messages | |
3913 * or whose ASCII codes (like space) xemacs doesn't like. | |
3914 */ | |
2286 | 3915 Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, |
3916 int UNUSED (mods), int extendedp) | |
428 | 3917 { |
3918 if (extendedp) /* Keys not present on a 82 key keyboard */ | |
3919 { | |
3920 switch (mswindows_key) | |
3921 { | |
442 | 3922 case VK_CANCEL: return KEYSYM ("pause"); |
428 | 3923 case VK_RETURN: return KEYSYM ("kp-enter"); |
3924 case VK_PRIOR: return KEYSYM ("prior"); | |
3925 case VK_NEXT: return KEYSYM ("next"); | |
3926 case VK_END: return KEYSYM ("end"); | |
3927 case VK_HOME: return KEYSYM ("home"); | |
3928 case VK_LEFT: return KEYSYM ("left"); | |
3929 case VK_UP: return KEYSYM ("up"); | |
3930 case VK_RIGHT: return KEYSYM ("right"); | |
3931 case VK_DOWN: return KEYSYM ("down"); | |
3932 case VK_INSERT: return KEYSYM ("insert"); | |
3933 case VK_DELETE: return QKdelete; | |
442 | 3934 #if 0 /* FSF Emacs allows these to return configurable syms/mods */ |
3935 case VK_LWIN return KEYSYM (""); | |
3936 case VK_RWIN return KEYSYM (""); | |
3937 #endif | |
3938 case VK_APPS: return KEYSYM ("menu"); | |
428 | 3939 } |
3940 } | |
3941 else | |
3942 { | |
3943 switch (mswindows_key) | |
3944 { | |
771 | 3945 |
3946 #if 0 | |
3947 VK_LBUTTON: | |
3948 VK_RBUTTON: | |
3949 VK_CANCEL: | |
3950 VK_MBUTTON: | |
3951 VK_XBUTTON1: | |
3952 VK_XBUTTON2: | |
3953 #endif /* 0 */ | |
3954 | |
428 | 3955 case VK_BACK: return QKbackspace; |
3956 case VK_TAB: return QKtab; | |
771 | 3957 /* #### Officially 0A (and 0B too) are "reserved". */ |
428 | 3958 case '\n': return QKlinefeed; |
3959 case VK_CLEAR: return KEYSYM ("clear"); | |
3960 case VK_RETURN: return QKreturn; | |
771 | 3961 |
3962 #if 0 | |
3963 VK_SHIFT: "shift" | |
3964 VK_CONTROL: "control" | |
3965 VK_MENU: "alt" | |
3966 #endif /* 0 */ | |
3967 | |
442 | 3968 case VK_PAUSE: return KEYSYM ("pause"); |
771 | 3969 |
3970 #if 0 | |
3971 VK_CAPITAL: "caps-lock" | |
3972 VK_KANA: IME Kana mode | |
3973 VK_HANGUEL: IME Hanguel mode (maintained for compatibility; use VK_HANGUL) | |
3974 VK_HANGUL: IME Hangul mode | |
3975 VK_JUNJA: IME Junja mode | |
3976 VK_FINAL: IME final mode | |
3977 VK_HANJA: IME Hanja mode | |
3978 VK_KANJI: IME Kanji mode | |
3979 #endif /* 0 */ | |
3980 | |
428 | 3981 case VK_ESCAPE: return QKescape; |
771 | 3982 |
3983 #if 0 | |
3984 VK_CONVERT: IME convert | |
3985 VK_NONCONVERT: IME nonconvert | |
3986 VK_ACCEPT: IME accept | |
3987 VK_MODECHANGE: IME mode change request | |
3988 #endif /* 0 */ | |
3989 | |
428 | 3990 case VK_SPACE: return QKspace; |
3991 case VK_PRIOR: return KEYSYM ("kp-prior"); | |
3992 case VK_NEXT: return KEYSYM ("kp-next"); | |
3993 case VK_END: return KEYSYM ("kp-end"); | |
3994 case VK_HOME: return KEYSYM ("kp-home"); | |
3995 case VK_LEFT: return KEYSYM ("kp-left"); | |
3996 case VK_UP: return KEYSYM ("kp-up"); | |
3997 case VK_RIGHT: return KEYSYM ("kp-right"); | |
3998 case VK_DOWN: return KEYSYM ("kp-down"); | |
3999 case VK_SELECT: return KEYSYM ("select"); | |
4000 case VK_PRINT: return KEYSYM ("print"); | |
4001 case VK_EXECUTE: return KEYSYM ("execute"); | |
4002 case VK_SNAPSHOT: return KEYSYM ("print"); | |
4003 case VK_INSERT: return KEYSYM ("kp-insert"); | |
4004 case VK_DELETE: return KEYSYM ("kp-delete"); | |
4005 case VK_HELP: return KEYSYM ("help"); | |
771 | 4006 #if 0 |
4007 '0' through '9': numeric keys | |
4008 'A' through 'Z': alphabetic keys | |
4009 VK_LWIN: "lwin" | |
4010 VK_RWIN: "rwin" | |
4011 VK_APPS: "apps" | |
4012 VK_SLEEP: "sleep" | |
4013 #endif /* 0 */ | |
428 | 4014 case VK_NUMPAD0: return KEYSYM ("kp-0"); |
4015 case VK_NUMPAD1: return KEYSYM ("kp-1"); | |
4016 case VK_NUMPAD2: return KEYSYM ("kp-2"); | |
4017 case VK_NUMPAD3: return KEYSYM ("kp-3"); | |
4018 case VK_NUMPAD4: return KEYSYM ("kp-4"); | |
4019 case VK_NUMPAD5: return KEYSYM ("kp-5"); | |
4020 case VK_NUMPAD6: return KEYSYM ("kp-6"); | |
4021 case VK_NUMPAD7: return KEYSYM ("kp-7"); | |
4022 case VK_NUMPAD8: return KEYSYM ("kp-8"); | |
4023 case VK_NUMPAD9: return KEYSYM ("kp-9"); | |
4024 case VK_MULTIPLY: return KEYSYM ("kp-multiply"); | |
4025 case VK_ADD: return KEYSYM ("kp-add"); | |
4026 case VK_SEPARATOR: return KEYSYM ("kp-separator"); | |
4027 case VK_SUBTRACT: return KEYSYM ("kp-subtract"); | |
4028 case VK_DECIMAL: return KEYSYM ("kp-decimal"); | |
4029 case VK_DIVIDE: return KEYSYM ("kp-divide"); | |
4030 case VK_F1: return KEYSYM ("f1"); | |
4031 case VK_F2: return KEYSYM ("f2"); | |
4032 case VK_F3: return KEYSYM ("f3"); | |
4033 case VK_F4: return KEYSYM ("f4"); | |
4034 case VK_F5: return KEYSYM ("f5"); | |
4035 case VK_F6: return KEYSYM ("f6"); | |
4036 case VK_F7: return KEYSYM ("f7"); | |
4037 case VK_F8: return KEYSYM ("f8"); | |
4038 case VK_F9: return KEYSYM ("f9"); | |
4039 case VK_F10: return KEYSYM ("f10"); | |
4040 case VK_F11: return KEYSYM ("f11"); | |
4041 case VK_F12: return KEYSYM ("f12"); | |
4042 case VK_F13: return KEYSYM ("f13"); | |
4043 case VK_F14: return KEYSYM ("f14"); | |
4044 case VK_F15: return KEYSYM ("f15"); | |
4045 case VK_F16: return KEYSYM ("f16"); | |
4046 case VK_F17: return KEYSYM ("f17"); | |
4047 case VK_F18: return KEYSYM ("f18"); | |
4048 case VK_F19: return KEYSYM ("f19"); | |
4049 case VK_F20: return KEYSYM ("f20"); | |
4050 case VK_F21: return KEYSYM ("f21"); | |
4051 case VK_F22: return KEYSYM ("f22"); | |
4052 case VK_F23: return KEYSYM ("f23"); | |
4053 case VK_F24: return KEYSYM ("f24"); | |
771 | 4054 |
4055 #if 0 | |
4056 VK_NUMLOCK: 90 NUM LOCK key | |
4057 VK_SCROLL: 91 SCROLL LOCK key | |
4058 92~96 OEM specific; | |
4059 VK_LSHIFT: | |
4060 VK_RSHIFT: | |
4061 VK_LCONTROL: | |
4062 VK_RCONTROL: | |
4063 VK_LMENU: | |
4064 VK_RMENU: | |
4065 | |
4066 #ifdef VK_BROWSER_BACK /* Windows 2000 only */ | |
4067 VK_BROWSER_BACK: Browser Back key | |
4068 VK_BROWSER_FORWARD: Browser Forward key | |
4069 VK_BROWSER_REFRESH: Browser Refresh key | |
4070 VK_BROWSER_STOP: Browser Stop key | |
4071 VK_BROWSER_SEARCH: Browser Search key | |
4072 VK_BROWSER_FAVORITES: Browser Favorites key | |
4073 VK_BROWSER_HOME: Browser Start and Home key | |
4074 VK_VOLUME_MUTE: Volume Mute key | |
4075 VK_VOLUME_DOWN: Volume Down key | |
4076 VK_VOLUME_UP: Volume Up key | |
4077 VK_MEDIA_NEXT_TRACK: Next Track key | |
4078 VK_MEDIA_PREV_TRACK: Previous Track key | |
4079 VK_MEDIA_STOP: Stop Media key | |
4080 VK_MEDIA_PLAY_PAUSE: Play/Pause Media key | |
4081 VK_LAUNCH_MAIL: Start Mail key | |
4082 VK_LAUNCH_MEDIA_SELECT: Select Media key | |
4083 VK_LAUNCH_APP1: Start Application 1 key | |
4084 VK_LAUNCH_APP2: Start Application 2 key | |
4085 B8-B9 Reserved; | |
4086 VK_OEM_1: For the US standard keyboard, the ';:' key | |
4087 VK_OEM_PLUS: For any country/region, the '+' key | |
4088 VK_OEM_COMMA: For any country/region, the ',' key | |
4089 VK_OEM_MINUS: For any country/region, the '-' key | |
4090 VK_OEM_PERIOD: For any country/region, the '.' key | |
4091 VK_OEM_2: For the US standard keyboard, the '/?' key | |
4092 VK_OEM_3: For the US standard keyboard, the '`~' key | |
4093 C1~D7 Reserved; | |
4094 D8~DA Unassigned; | |
4095 VK_OEM_4: For the US standard keyboard, the '[{' key | |
4096 VK_OEM_5: For the US standard keyboard, the '\|' key | |
4097 VK_OEM_6: For the US standard keyboard, the ']}' key | |
4098 VK_OEM_7: For the US standard keyboard, the 'single-quote/double-quote' key | |
4099 VK_OEM_8: | |
4100 E0 Reserved; | |
4101 E1 OEM specific; | |
4102 VK_OEM_102: Either the angle bracket key or the backslash key on the RT 102-key keyboard | |
4103 E3~E4 OEM specific; | |
4104 #endif /* VK_BROWSER_BACK */ | |
4105 VK_PROCESSKEY: E5 Windows 95/98, Windows NT 4.0, Windows 2000: IME PROCESS key | |
4106 E6 OEM specific; | |
4107 VK_PACKET: Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP | |
4108 E8 Unassigned; | |
4109 E9~F5 OEM specific; | |
4110 VK_ATTN: Attn key | |
4111 VK_CRSEL: CrSel key | |
4112 VK_EXSEL: ExSel key | |
4113 VK_EREOF: Erase EOF key | |
4114 VK_PLAY: Play key | |
4115 VK_ZOOM: Zoom key | |
4116 VK_NONAME: Reserved for future use | |
4117 VK_PA1: PA1 key | |
4118 VK_OEM_CLEAR: Clear key | |
4119 #endif /* 0 */ | |
4120 | |
428 | 4121 } |
4122 } | |
4123 return Qnil; | |
4124 } | |
4125 | |
4126 /* | |
4127 * Find the console that matches the supplied mswindows window handle | |
4128 */ | |
4129 Lisp_Object | |
2286 | 4130 mswindows_find_console (HWND UNUSED (hwnd)) |
428 | 4131 { |
4132 /* We only support one console */ | |
4133 return XCAR (Vconsole_list); | |
4134 } | |
4135 | |
4136 /* | |
4137 * Find the frame that matches the supplied mswindows window handle | |
4138 */ | |
546 | 4139 Lisp_Object |
428 | 4140 mswindows_find_frame (HWND hwnd) |
4141 { | |
771 | 4142 LONG l = qxeGetWindowLong (hwnd, XWL_FRAMEOBJ); |
428 | 4143 Lisp_Object f; |
4144 if (l == 0) | |
4145 { | |
4146 /* We are in progress of frame creation. Return the frame | |
4147 being created, as it still not remembered in the window | |
4148 extra storage. */ | |
4149 assert (!NILP (Vmswindows_frame_being_created)); | |
4150 return Vmswindows_frame_being_created; | |
4151 } | |
5013 | 4152 f = GET_LISP_FROM_VOID ((void *) l); |
428 | 4153 return f; |
4154 } | |
4155 | |
4156 | |
4157 /************************************************************************/ | |
4158 /* methods */ | |
4159 /************************************************************************/ | |
4160 | |
4161 static int | |
4162 emacs_mswindows_add_timeout (EMACS_TIME thyme) | |
4163 { | |
4164 int milliseconds; | |
4165 EMACS_TIME current_time; | |
4166 EMACS_GET_TIME (current_time); | |
4167 EMACS_SUB_TIME (thyme, thyme, current_time); | |
4168 milliseconds = EMACS_SECS (thyme) * 1000 + | |
4169 (EMACS_USECS (thyme) + 500) / 1000; | |
4170 if (milliseconds < 1) | |
4171 milliseconds = 1; | |
4172 ++mswindows_pending_timers_count; | |
4173 return SetTimer (NULL, 0, milliseconds, | |
4174 (TIMERPROC) mswindows_wm_timer_callback); | |
4175 } | |
4176 | |
1204 | 4177 static int |
4178 remove_timeout_mapper (Lisp_Object ev, void *data) | |
4179 { | |
4180 if (XEVENT_TYPE (ev) == timeout_event) | |
4181 { | |
4182 if ((int) data == XEVENT_TIMEOUT_INTERVAL_ID (ev)) | |
4183 return 1; | |
4184 } | |
4185 | |
4186 return 0; | |
4187 } | |
4188 | |
428 | 4189 static void |
4190 emacs_mswindows_remove_timeout (int id) | |
4191 { | |
4192 if (KillTimer (NULL, id)) | |
4193 --mswindows_pending_timers_count; | |
4194 | |
4195 /* If there is a dispatch event generated by this | |
4196 timeout in the queue, we have to remove it too. */ | |
1204 | 4197 map_event_chain_remove (remove_timeout_mapper, |
4198 &mswindows_s_dispatch_event_queue, | |
4199 &mswindows_s_dispatch_event_queue_tail, | |
4200 (void *) id, MECR_DEALLOCATE_EVENT); | |
428 | 4201 } |
4202 | |
4203 /* If `user_p' is false, then return whether there are any win32, timeout, | |
4204 * or subprocess events pending (that is, whether | |
4205 * emacs_mswindows_next_event() would return immediately without blocking). | |
4206 * | |
4207 * if `user_p' is true, then return whether there are any *user generated* | |
4208 * events available (that is, whether there are keyboard or mouse-click | |
4209 * events ready to be read). This also implies that | |
4210 * emacs_mswindows_next_event() would not block. | |
4211 */ | |
4212 static int | |
1268 | 4213 emacs_mswindows_event_pending_p (int how_many) |
428 | 4214 { |
1318 | 4215 /* This can call Lisp */ |
1268 | 4216 if (!how_many) |
4217 { | |
4218 mswindows_need_event (0); | |
4219 return (!NILP (dispatch_event_queue) | |
4220 || !NILP (mswindows_s_dispatch_event_queue)); | |
4221 } | |
4222 else | |
4223 { | |
4224 Lisp_Object event; | |
4225 int count = 0; | |
4226 | |
4227 EVENT_CHAIN_LOOP (event, dispatch_event_queue) | |
4228 count++; | |
4229 | |
4230 if (count >= how_many) | |
4231 return 1; | |
4232 | |
4233 emacs_mswindows_drain_queue (); | |
4234 | |
4235 EVENT_CHAIN_LOOP (event, dispatch_event_queue) | |
4236 count++; | |
4237 | |
4238 return count >= how_many; | |
4239 } | |
428 | 4240 } |
4241 | |
4242 /* | |
4243 * Return the next event | |
4244 */ | |
4245 static void | |
440 | 4246 emacs_mswindows_next_event (Lisp_Event *emacs_event) |
428 | 4247 { |
4248 Lisp_Object event, event2; | |
4249 | |
4250 mswindows_need_event (1); | |
4251 | |
4252 event = mswindows_dequeue_dispatch_event (); | |
793 | 4253 event2 = wrap_event (emacs_event); |
428 | 4254 Fcopy_event (event, event2); |
4255 Fdeallocate_event (event); | |
4256 } | |
4257 | |
788 | 4258 static void |
4259 emacs_mswindows_format_magic_event (Lisp_Event *emacs_event, | |
4260 Lisp_Object pstream) | |
4261 { | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
4262 #define FROB(msg) case msg: write_ascstring (pstream, "type=" #msg); break |
788 | 4263 |
1204 | 4264 switch (EVENT_MAGIC_MSWINDOWS_EVENT (emacs_event)) |
788 | 4265 { |
4266 FROB (XM_BUMPQUEUE); | |
4267 FROB (WM_PAINT); | |
4268 FROB (WM_SETFOCUS); | |
4269 FROB (WM_KILLFOCUS); | |
4270 FROB (XM_MAPFRAME); | |
4271 FROB (XM_UNMAPFRAME); | |
4272 | |
2500 | 4273 default: ABORT (); |
788 | 4274 } |
4275 #undef FROB | |
4276 | |
4277 if (!NILP (EVENT_CHANNEL (emacs_event))) | |
4278 { | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
4279 write_ascstring (pstream, " "); |
788 | 4280 print_internal (EVENT_CHANNEL (emacs_event), pstream, 1); |
4281 } | |
4282 } | |
4283 | |
4284 static int | |
4285 emacs_mswindows_compare_magic_event (Lisp_Event *e1, Lisp_Event *e2) | |
4286 { | |
1204 | 4287 return (EVENT_MAGIC_MSWINDOWS_EVENT (e1) == |
4288 EVENT_MAGIC_MSWINDOWS_EVENT (e2)); | |
788 | 4289 } |
4290 | |
4291 static Hashcode | |
4292 emacs_mswindows_hash_magic_event (Lisp_Event *e) | |
4293 { | |
1204 | 4294 return (EVENT_MAGIC_MSWINDOWS_EVENT (e)); |
788 | 4295 } |
4296 | |
428 | 4297 /* |
4298 * Handle a magic event off the dispatch queue. | |
4299 */ | |
4300 static void | |
440 | 4301 emacs_mswindows_handle_magic_event (Lisp_Event *emacs_event) |
428 | 4302 { |
1204 | 4303 switch (EVENT_MAGIC_MSWINDOWS_EVENT (emacs_event)) |
428 | 4304 { |
4305 case XM_BUMPQUEUE: | |
4306 break; | |
4307 | |
442 | 4308 case WM_PAINT: |
4309 { | |
4310 struct frame *f = XFRAME (EVENT_CHANNEL (emacs_event)); | |
4311 mswindows_handle_paint (f); | |
4312 (FRAME_MSWINDOWS_DATA (f))->paint_pending = 0; | |
4313 } | |
4314 break; | |
4315 | |
428 | 4316 case WM_SETFOCUS: |
4317 case WM_KILLFOCUS: | |
4318 { | |
4319 Lisp_Object frame = EVENT_CHANNEL (emacs_event); | |
4320 struct frame *f = XFRAME (frame); | |
1204 | 4321 int in_p = (EVENT_MAGIC_MSWINDOWS_EVENT (emacs_event) |
964 | 4322 == WM_SETFOCUS); |
428 | 4323 Lisp_Object conser; |
442 | 4324 struct gcpro gcpro1; |
4325 | |
4326 /* On focus change, clear all memory of sticky modifiers | |
4327 to avoid non-intuitive behavior. */ | |
4328 clear_sticky_modifiers (); | |
428 | 4329 |
4330 conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil)); | |
442 | 4331 GCPRO1 (conser); |
428 | 4332 emacs_handle_focus_change_preliminary (conser); |
4333 /* Under X the stuff up to here is done in the X event handler. | |
4334 I Don't know why */ | |
4335 emacs_handle_focus_change_final (conser); | |
442 | 4336 UNGCPRO; |
428 | 4337 |
4338 } | |
4339 break; | |
4340 | |
4341 case XM_MAPFRAME: | |
4342 case XM_UNMAPFRAME: | |
4343 { | |
4344 Lisp_Object frame = EVENT_CHANNEL (emacs_event); | |
1204 | 4345 va_run_hook_with_args (EVENT_MAGIC_MSWINDOWS_EVENT (emacs_event) |
428 | 4346 == XM_MAPFRAME ? |
4347 Qmap_frame_hook : Qunmap_frame_hook, | |
4348 1, frame); | |
4349 } | |
4350 break; | |
4351 | |
4352 /* #### What about Enter & Leave */ | |
4353 #if 0 | |
4354 va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook : | |
4355 Qmouse_leave_frame_hook, 1, frame); | |
4356 #endif | |
4357 | |
4358 default: | |
4359 assert(0); | |
4360 } | |
4361 } | |
4362 | |
853 | 4363 #ifndef CYGWIN |
4364 | |
428 | 4365 static HANDLE |
440 | 4366 get_process_input_waitable (Lisp_Process *process) |
428 | 4367 { |
853 | 4368 Lisp_Object instr, outstr, errstr, p; |
793 | 4369 p = wrap_process (process); |
853 | 4370 get_process_streams (process, &instr, &outstr, &errstr); |
428 | 4371 assert (!NILP (instr)); |
4372 return (network_connection_p (p) | |
4373 ? get_winsock_stream_waitable (XLSTREAM (instr)) | |
4374 : get_ntpipe_input_stream_waitable (XLSTREAM (instr))); | |
4375 } | |
4376 | |
853 | 4377 static HANDLE |
4378 get_process_stderr_waitable (Lisp_Process *process) | |
4379 { | |
4380 Lisp_Object instr, outstr, errstr; | |
4381 get_process_streams (process, &instr, &outstr, &errstr); | |
4382 if (NILP (errstr)) | |
4383 return INVALID_HANDLE_VALUE; | |
4384 return get_ntpipe_input_stream_waitable (XLSTREAM (errstr)); | |
4385 } | |
4386 | |
4387 #endif /* not CYGWIN */ | |
4388 | |
428 | 4389 static void |
853 | 4390 emacs_mswindows_select_process (Lisp_Process *process, int doin, int doerr) |
428 | 4391 { |
853 | 4392 #ifdef CYGWIN |
4393 int infd, errfd; | |
4394 | |
4395 event_stream_unixoid_select_process (process, doin, doerr, &infd, &errfd); | |
4396 #else | |
4397 HANDLE hev = INVALID_HANDLE_VALUE; | |
4398 HANDLE herr = INVALID_HANDLE_VALUE; | |
4399 | |
4400 if (doin) | |
4401 { | |
4402 hev = get_process_input_waitable (process); | |
4403 if (!add_waitable_handle (hev)) | |
4404 { | |
4405 hev = INVALID_HANDLE_VALUE; | |
4406 goto err; | |
4407 } | |
4408 } | |
4409 | |
4410 if (doerr) | |
4411 { | |
4412 herr = get_process_stderr_waitable (process); | |
4413 if (herr != INVALID_HANDLE_VALUE && !add_waitable_handle (herr)) | |
4414 { | |
4415 herr = INVALID_HANDLE_VALUE; | |
4416 goto err; | |
4417 } | |
4418 } | |
4419 | |
428 | 4420 { |
853 | 4421 /* Also select on the process handle itself, so we can receive |
4422 exit notifications. Only do this once, not each time this | |
4423 function is called (which can happen many times, e.g. if | |
4424 (set-process-filter proc t) is called and then a process filter | |
4425 is set again). It will be unselected in mswindows_need_event(). */ | |
793 | 4426 Lisp_Object p = wrap_process (process); |
4427 | |
428 | 4428 if (!network_connection_p (p)) |
4429 { | |
853 | 4430 HANDLE hprocess = get_nt_process_handle_only_first_time (process); |
4431 if (hprocess != INVALID_HANDLE_VALUE | |
4432 && !add_waitable_handle (hprocess)) | |
4433 goto err; | |
428 | 4434 } |
4435 } | |
853 | 4436 |
4437 return; | |
4438 | |
4439 err: | |
4440 if (hev != INVALID_HANDLE_VALUE) | |
4441 remove_waitable_handle (hev); | |
4442 if (herr != INVALID_HANDLE_VALUE) | |
4443 remove_waitable_handle (herr); | |
4444 invalid_operation ("Too many active processes", wrap_process (process)); | |
4445 #endif /* CYGWIN */ | |
428 | 4446 } |
4447 | |
4448 static void | |
853 | 4449 emacs_mswindows_unselect_process (Lisp_Process *process, int doin, int doerr) |
428 | 4450 { |
853 | 4451 #ifdef CYGWIN |
4452 int infd, errfd; | |
4453 | |
4454 event_stream_unixoid_unselect_process (process, doin, doerr, &infd, &errfd); | |
4455 #else | |
4456 if (doin) | |
4457 { | |
4458 /* Process handle is removed in the event loop as soon | |
4459 as it is signaled, so don't bother here about it */ | |
4460 HANDLE hev = get_process_input_waitable (process); | |
4461 remove_waitable_handle (hev); | |
4462 } | |
4463 if (doerr) | |
4464 { | |
4465 /* Process handle is removed in the event loop as soon | |
4466 as it is signaled, so don't bother here about it */ | |
4467 HANDLE herr = get_process_stderr_waitable (process); | |
4468 if (herr != INVALID_HANDLE_VALUE) | |
4469 remove_waitable_handle (herr); | |
4470 } | |
4471 #endif /* CYGWIN */ | |
428 | 4472 } |
4473 | |
4474 static void | |
2286 | 4475 emacs_mswindows_select_console (struct console *USED_IF_CYGWIN (con)) |
428 | 4476 { |
853 | 4477 #ifdef CYGWIN |
428 | 4478 if (CONSOLE_MSWINDOWS_P (con)) |
4479 return; /* mswindows consoles are automatically selected */ | |
4480 | |
4481 event_stream_unixoid_select_console (con); | |
1204 | 4482 #else |
4483 #if 0 | |
4484 /* This is an attempt to get `xemacs -batch -l dunnet' to work. | |
4485 Doesn't currently work and fucks other things up. */ | |
4486 if (CONSOLE_STREAM_P (con) && | |
4487 !UNBOUNDP (CONSOLE_STREAM_DATA (con)->instream)) | |
4488 { | |
4489 HANDLE h = | |
4490 (HANDLE) _get_osfhandle (fileno (CONSOLE_STREAM_DATA (con)->in)); | |
4491 if (PeekNamedPipe (h, 0, 0, 0, 0, 0)) | |
4492 { | |
4493 Lisp_Object lstr = make_ntpipe_input_stream (h, 0); | |
4494 HANDLE hwait = get_ntpipe_input_stream_waitable (XLSTREAM (lstr)); | |
4495 | |
4496 if (!add_waitable_handle (hwait)) | |
4497 invalid_operation ("Too many active processes", | |
4498 wrap_console (con)); | |
4499 CONSOLE_STREAM_DATA (con)->instream = lstr; | |
4500 } | |
4501 else | |
4502 /* Unable to select on this stream */ | |
4503 CONSOLE_STREAM_DATA (con)->instream = Qunbound; | |
4504 } | |
4505 #endif /* 0 */ | |
428 | 4506 #endif |
4507 } | |
4508 | |
4509 static void | |
2286 | 4510 emacs_mswindows_unselect_console (struct console *USED_IF_CYGWIN (con)) |
428 | 4511 { |
853 | 4512 #ifdef CYGWIN |
428 | 4513 if (CONSOLE_MSWINDOWS_P (con)) |
4514 return; /* mswindows consoles are automatically selected */ | |
4515 | |
4516 event_stream_unixoid_unselect_console (con); | |
1204 | 4517 #else |
4518 #if 0 /* see above */ | |
4519 if (CONSOLE_STREAM_P (con) && | |
4520 !UNBOUNDP (CONSOLE_STREAM_DATA (con)->instream)) | |
428 | 4521 { |
1204 | 4522 Lisp_Object instr = CONSOLE_STREAM_DATA (con)->instream; |
4523 HANDLE hwait; | |
4524 | |
4525 assert (!NILP (instr)); | |
4526 hwait = get_ntpipe_input_stream_waitable (XLSTREAM (instr)); | |
4527 | |
4528 remove_waitable_handle (hwait); | |
428 | 4529 } |
1204 | 4530 #endif /* 0 */ |
4531 #endif | |
428 | 4532 } |
4533 | |
853 | 4534 static void |
4535 emacs_mswindows_create_io_streams (void *inhandle, void *outhandle, | |
4536 void *errhandle, Lisp_Object *instream, | |
4537 Lisp_Object *outstream, | |
4538 Lisp_Object *errstream, | |
4539 USID *in_usid, | |
4540 USID *err_usid, | |
4541 int flags) | |
428 | 4542 { |
853 | 4543 #ifdef CYGWIN |
4544 event_stream_unixoid_create_io_streams (inhandle, outhandle, | |
4545 errhandle, instream, | |
4546 outstream, errstream, | |
4547 in_usid, err_usid, flags); | |
4548 #else | |
428 | 4549 /* Handles for streams */ |
853 | 4550 HANDLE hin, hout, herr; |
428 | 4551 /* fds. These just stored along with the streams, and are closed in |
4552 delete stream pair method, because we need to handle fake unices | |
4553 here. */ | |
853 | 4554 int fdi, fdo, fde; |
4555 | |
4556 /* Decode inhandle, outhandle, errhandle. Their meaning depends on | |
428 | 4557 the process implementation being used. */ |
4558 hin = (HANDLE) inhandle; | |
4559 hout = (HANDLE) outhandle; | |
853 | 4560 if (errhandle == (void *) -1) |
4561 herr = INVALID_HANDLE_VALUE; | |
4562 else | |
4563 herr = (HANDLE) errhandle; | |
4564 fdi = fdo = fde = -1; | |
428 | 4565 |
4566 *instream = (hin == INVALID_HANDLE_VALUE | |
4567 ? Qnil | |
4568 : flags & STREAM_NETWORK_CONNECTION | |
853 | 4569 ? make_winsock_input_stream ((SOCKET) hin, fdi) |
428 | 4570 : make_ntpipe_input_stream (hin, fdi)); |
4571 | |
853 | 4572 *errstream = (herr == INVALID_HANDLE_VALUE |
4573 ? Qnil | |
4574 : make_ntpipe_input_stream (herr, fde)); | |
4575 | |
428 | 4576 *outstream = (hout == INVALID_HANDLE_VALUE |
4577 ? Qnil | |
4578 : flags & STREAM_NETWORK_CONNECTION | |
4579 ? make_winsock_output_stream ((SOCKET)hout, fdo) | |
4580 : make_ntpipe_output_stream (hout, fdo)); | |
853 | 4581 |
4582 *in_usid = | |
4583 (NILP (*instream) | |
4584 ? USID_ERROR | |
4585 : flags & STREAM_NETWORK_CONNECTION | |
4586 ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream))) | |
4587 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM | |
4588 (*instream)))); | |
4589 | |
4590 *err_usid = | |
4591 (NILP (*errstream) | |
4592 ? USID_DONTHASH | |
4593 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM | |
4594 (*errstream)))); | |
4595 #endif /* CYGWIN */ | |
428 | 4596 } |
4597 | |
853 | 4598 static void |
4599 emacs_mswindows_delete_io_streams (Lisp_Object instream, | |
2286 | 4600 Lisp_Object USED_IF_CYGWIN (outstream), |
853 | 4601 Lisp_Object errstream, |
4602 USID *in_usid, | |
4603 USID *err_usid) | |
428 | 4604 { |
853 | 4605 #ifdef CYGWIN |
4606 event_stream_unixoid_delete_io_streams (instream, outstream, errstream, | |
4607 in_usid, err_usid); | |
4608 #else | |
4609 *in_usid = | |
4610 (NILP (instream) | |
4611 ? USID_DONTHASH | |
4612 : LSTREAM_TYPE_P (XLSTREAM (instream), winsock) | |
4613 ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream))) | |
4614 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM | |
4615 (instream)))); | |
4616 | |
4617 *err_usid = | |
4618 (NILP (errstream) | |
4619 ? USID_DONTHASH | |
4620 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM | |
4621 (errstream)))); | |
4622 #endif /* CYGWIN */ | |
428 | 4623 } |
4624 | |
442 | 4625 static int |
2286 | 4626 emacs_mswindows_current_event_timestamp (struct console *UNUSED (c)) |
442 | 4627 { |
4628 return GetTickCount (); | |
4629 } | |
4630 | |
428 | 4631 #ifndef HAVE_X_WINDOWS |
4632 /* This is called from GC when a process object is about to be freed. | |
4633 If we've still got pointers to it in this file, we're gonna lose hard. | |
853 | 4634 */ |
4635 void debug_process_finalization (Lisp_Process *p); | |
428 | 4636 void |
2286 | 4637 debug_process_finalization (Lisp_Process *UNUSED (p)) |
428 | 4638 { |
4639 #if 0 /* #### */ | |
853 | 4640 Lisp_Object instr, outstr, errstr; |
4641 | |
4642 get_process_streams (p, &instr, &outstr, &errstr); | |
428 | 4643 /* if it still has fds, then it hasn't been killed yet. */ |
771 | 4644 assert (NILP (instr)); |
4645 assert (NILP (outstr)); | |
853 | 4646 assert (NILP (errstr)); |
428 | 4647 |
4648 /* #### More checks here */ | |
4649 #endif | |
4650 } | |
4651 #endif | |
4652 | |
593 | 4653 #ifdef DEBUG_XEMACS |
4654 | |
4655 struct mswin_message_debug | |
4656 { | |
4657 int mess; | |
4932 | 4658 const Ascbyte *string; |
593 | 4659 }; |
4660 | |
4661 #define FROB(val) { val, #val, }, | |
4662 | |
4663 struct mswin_message_debug debug_mswin_messages[] = | |
4664 { | |
4665 FROB (WM_NULL) | |
4666 FROB (WM_CREATE) | |
4667 FROB (WM_DESTROY) | |
4668 FROB (WM_MOVE) | |
4669 FROB (WM_SIZE) | |
4670 | |
4671 FROB (WM_ACTIVATE) | |
4672 | |
4673 FROB (WM_SETFOCUS) | |
4674 FROB (WM_KILLFOCUS) | |
4675 FROB (WM_ENABLE) | |
4676 FROB (WM_SETREDRAW) | |
4677 FROB (WM_SETTEXT) | |
4678 FROB (WM_GETTEXT) | |
4679 FROB (WM_GETTEXTLENGTH) | |
4680 FROB (WM_PAINT) | |
4681 FROB (WM_CLOSE) | |
4682 FROB (WM_QUERYENDSESSION) | |
4683 FROB (WM_QUIT) | |
4684 FROB (WM_QUERYOPEN) | |
4685 FROB (WM_ERASEBKGND) | |
4686 FROB (WM_SYSCOLORCHANGE) | |
4687 FROB (WM_ENDSESSION) | |
4688 FROB (WM_SHOWWINDOW) | |
4689 FROB (WM_WININICHANGE) | |
4690 #if(WINVER >= 0x0400) | |
4691 FROB (WM_SETTINGCHANGE) | |
4692 #endif /* WINVER >= 0x0400 */ | |
4693 | |
4694 FROB (WM_DEVMODECHANGE) | |
4695 FROB (WM_ACTIVATEAPP) | |
4696 FROB (WM_FONTCHANGE) | |
4697 FROB (WM_TIMECHANGE) | |
4698 FROB (WM_CANCELMODE) | |
4699 FROB (WM_SETCURSOR) | |
4700 FROB (WM_MOUSEACTIVATE) | |
4701 FROB (WM_CHILDACTIVATE) | |
4702 FROB (WM_QUEUESYNC) | |
4703 | |
4704 FROB (WM_GETMINMAXINFO) | |
4705 | |
4706 FROB (WM_PAINTICON) | |
4707 FROB (WM_ICONERASEBKGND) | |
4708 FROB (WM_NEXTDLGCTL) | |
4709 FROB (WM_SPOOLERSTATUS) | |
4710 FROB (WM_DRAWITEM) | |
4711 FROB (WM_MEASUREITEM) | |
4712 FROB (WM_DELETEITEM) | |
4713 FROB (WM_VKEYTOITEM) | |
4714 FROB (WM_CHARTOITEM) | |
4715 FROB (WM_SETFONT) | |
4716 FROB (WM_GETFONT) | |
4717 FROB (WM_SETHOTKEY) | |
4718 FROB (WM_GETHOTKEY) | |
4719 FROB (WM_QUERYDRAGICON) | |
4720 FROB (WM_COMPAREITEM) | |
1687 | 4721 #if(WINVER >= 0x0500) && defined(WM_GETOBJECT) |
593 | 4722 FROB (WM_GETOBJECT) |
4723 #endif /* WINVER >= 0x0500 */ | |
4724 FROB (WM_COMPACTING) | |
4725 FROB (WM_COMMNOTIFY) | |
4726 FROB (WM_WINDOWPOSCHANGING) | |
4727 FROB (WM_WINDOWPOSCHANGED) | |
4728 | |
4729 FROB (WM_POWER) | |
4730 | |
4731 FROB (WM_COPYDATA) | |
4732 FROB (WM_CANCELJOURNAL) | |
4733 | |
4734 #if(WINVER >= 0x0400) | |
4735 FROB (WM_NOTIFY) | |
4736 FROB (WM_INPUTLANGCHANGEREQUEST) | |
4737 FROB (WM_INPUTLANGCHANGE) | |
4738 FROB (WM_TCARD) | |
4739 FROB (WM_HELP) | |
4740 FROB (WM_USERCHANGED) | |
4741 FROB (WM_NOTIFYFORMAT) | |
4742 | |
4743 FROB (WM_CONTEXTMENU) | |
4744 FROB (WM_STYLECHANGING) | |
4745 FROB (WM_STYLECHANGED) | |
4746 FROB (WM_DISPLAYCHANGE) | |
4747 FROB (WM_GETICON) | |
4748 FROB (WM_SETICON) | |
4749 #endif /* WINVER >= 0x0400 */ | |
4750 | |
4751 FROB (WM_NCCREATE) | |
4752 FROB (WM_NCDESTROY) | |
4753 FROB (WM_NCCALCSIZE) | |
4754 FROB (WM_NCHITTEST) | |
4755 FROB (WM_NCPAINT) | |
4756 FROB (WM_NCACTIVATE) | |
4757 FROB (WM_GETDLGCODE) | |
604 | 4758 #ifdef WM_SYNCPAINT /* not in VC 5 */ |
593 | 4759 FROB (WM_SYNCPAINT) |
604 | 4760 #endif /* WM_SYNCPAINT */ |
593 | 4761 FROB (WM_NCMOUSEMOVE) |
4762 FROB (WM_NCLBUTTONDOWN) | |
4763 FROB (WM_NCLBUTTONUP) | |
4764 FROB (WM_NCLBUTTONDBLCLK) | |
4765 FROB (WM_NCRBUTTONDOWN) | |
4766 FROB (WM_NCRBUTTONUP) | |
4767 FROB (WM_NCRBUTTONDBLCLK) | |
4768 FROB (WM_NCMBUTTONDOWN) | |
4769 FROB (WM_NCMBUTTONUP) | |
4770 FROB (WM_NCMBUTTONDBLCLK) | |
4771 | |
4772 /* FROB (WM_KEYFIRST) */ | |
4773 FROB (WM_KEYDOWN) | |
4774 FROB (WM_KEYUP) | |
4775 FROB (WM_CHAR) | |
4776 FROB (WM_DEADCHAR) | |
4777 FROB (WM_SYSKEYDOWN) | |
4778 FROB (WM_SYSKEYUP) | |
4779 FROB (WM_SYSCHAR) | |
4780 FROB (WM_SYSDEADCHAR) | |
4781 FROB (WM_KEYLAST) | |
4782 | |
604 | 4783 #if(WINVER >= 0x0400) && defined (WM_IME_STARTCOMPOSITION) |
4784 /* not in Cygwin? */ | |
593 | 4785 FROB (WM_IME_STARTCOMPOSITION) |
4786 FROB (WM_IME_ENDCOMPOSITION) | |
4787 FROB (WM_IME_COMPOSITION) | |
4788 FROB (WM_IME_KEYLAST) | |
604 | 4789 #endif /* WINVER >= 0x0400 && defined (WM_IME_STARTCOMPOSITION) */ |
593 | 4790 |
4791 FROB (WM_INITDIALOG) | |
4792 FROB (WM_COMMAND) | |
4793 FROB (WM_SYSCOMMAND) | |
4794 FROB (WM_TIMER) | |
4795 FROB (WM_HSCROLL) | |
4796 FROB (WM_VSCROLL) | |
4797 FROB (WM_INITMENU) | |
4798 FROB (WM_INITMENUPOPUP) | |
4799 FROB (WM_MENUSELECT) | |
4800 FROB (WM_MENUCHAR) | |
4801 FROB (WM_ENTERIDLE) | |
4802 #if(WINVER >= 0x0500) | |
4803 FROB (WM_MENURBUTTONUP) | |
1687 | 4804 #ifdef WM_MENUDRAG |
593 | 4805 FROB (WM_MENUDRAG) |
1687 | 4806 #endif |
4807 #ifdef WM_MENUGETOBJECT | |
593 | 4808 FROB (WM_MENUGETOBJECT) |
1687 | 4809 #endif |
4810 #ifdef WM_UNINITMENUPOPUP | |
593 | 4811 FROB (WM_UNINITMENUPOPUP) |
1687 | 4812 #endif |
4813 #ifdef WM_MENUCOMMAND | |
593 | 4814 FROB (WM_MENUCOMMAND) |
1687 | 4815 #endif |
593 | 4816 #endif /* WINVER >= 0x0500 */ |
4817 | |
4818 | |
4819 FROB (WM_CTLCOLORMSGBOX) | |
4820 FROB (WM_CTLCOLOREDIT) | |
4821 FROB (WM_CTLCOLORLISTBOX) | |
4822 FROB (WM_CTLCOLORBTN) | |
4823 FROB (WM_CTLCOLORDLG) | |
4824 FROB (WM_CTLCOLORSCROLLBAR) | |
4825 FROB (WM_CTLCOLORSTATIC) | |
4826 | |
4827 | |
4828 /* FROB (WM_MOUSEFIRST) */ | |
4829 FROB (WM_MOUSEMOVE) | |
4830 FROB (WM_LBUTTONDOWN) | |
4831 FROB (WM_LBUTTONUP) | |
4832 FROB (WM_LBUTTONDBLCLK) | |
4833 FROB (WM_RBUTTONDOWN) | |
4834 FROB (WM_RBUTTONUP) | |
4835 FROB (WM_RBUTTONDBLCLK) | |
4836 FROB (WM_MBUTTONDOWN) | |
4837 FROB (WM_MBUTTONUP) | |
4838 FROB (WM_MBUTTONDBLCLK) | |
4839 | |
4840 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) | |
4841 FROB (WM_MOUSEWHEEL) | |
4842 FROB (WM_MOUSELAST) | |
4843 #else | |
4844 FROB (WM_MOUSELAST) | |
4845 #endif /* if (_WIN32_WINNT < 0x0400) */ | |
4846 | |
4847 FROB (WM_PARENTNOTIFY) | |
4848 FROB (WM_ENTERMENULOOP) | |
4849 FROB (WM_EXITMENULOOP) | |
4850 | |
4851 #if(WINVER >= 0x0400) | |
4852 FROB (WM_NEXTMENU) | |
4853 | |
4854 FROB (WM_SIZING) | |
4855 FROB (WM_CAPTURECHANGED) | |
4856 FROB (WM_MOVING) | |
4857 FROB (WM_POWERBROADCAST) | |
4858 | |
4859 FROB (WM_DEVICECHANGE) | |
4860 | |
4861 #endif /* WINVER >= 0x0400 */ | |
4862 | |
4863 FROB (WM_MDICREATE) | |
4864 FROB (WM_MDIDESTROY) | |
4865 FROB (WM_MDIACTIVATE) | |
4866 FROB (WM_MDIRESTORE) | |
4867 FROB (WM_MDINEXT) | |
4868 FROB (WM_MDIMAXIMIZE) | |
4869 FROB (WM_MDITILE) | |
4870 FROB (WM_MDICASCADE) | |
4871 FROB (WM_MDIICONARRANGE) | |
4872 FROB (WM_MDIGETACTIVE) | |
4873 | |
4874 | |
4875 FROB (WM_MDISETMENU) | |
4876 FROB (WM_ENTERSIZEMOVE) | |
4877 FROB (WM_EXITSIZEMOVE) | |
4878 FROB (WM_DROPFILES) | |
4879 FROB (WM_MDIREFRESHMENU) | |
4880 | |
604 | 4881 #ifdef WM_IME_SETCONTEXT /* not in Cygwin? */ |
593 | 4882 |
4883 #if(WINVER >= 0x0400) | |
4884 FROB (WM_IME_SETCONTEXT) | |
4885 FROB (WM_IME_NOTIFY) | |
4886 FROB (WM_IME_CONTROL) | |
4887 FROB (WM_IME_COMPOSITIONFULL) | |
4888 FROB (WM_IME_SELECT) | |
4889 FROB (WM_IME_CHAR) | |
4890 #endif /* WINVER >= 0x0400 */ | |
1687 | 4891 #if(WINVER >= 0x0500) && defined(WM_IME_REQUEST) |
593 | 4892 FROB (WM_IME_REQUEST) |
4893 #endif /* WINVER >= 0x0500 */ | |
4894 #if(WINVER >= 0x0400) | |
4895 FROB (WM_IME_KEYDOWN) | |
4896 FROB (WM_IME_KEYUP) | |
4897 #endif /* WINVER >= 0x0400 */ | |
4898 | |
604 | 4899 #endif /* WM_IME_SETCONTEXT */ |
593 | 4900 |
4901 #if(_WIN32_WINNT >= 0x0400) | |
4902 FROB (WM_MOUSEHOVER) | |
4903 FROB (WM_MOUSELEAVE) | |
4904 #endif /* _WIN32_WINNT >= 0x0400 */ | |
4905 | |
4906 FROB (WM_CUT) | |
4907 FROB (WM_COPY) | |
4908 FROB (WM_PASTE) | |
4909 FROB (WM_CLEAR) | |
4910 FROB (WM_UNDO) | |
4911 FROB (WM_RENDERFORMAT) | |
4912 FROB (WM_RENDERALLFORMATS) | |
4913 FROB (WM_DESTROYCLIPBOARD) | |
4914 FROB (WM_DRAWCLIPBOARD) | |
4915 FROB (WM_PAINTCLIPBOARD) | |
4916 FROB (WM_VSCROLLCLIPBOARD) | |
4917 FROB (WM_SIZECLIPBOARD) | |
4918 FROB (WM_ASKCBFORMATNAME) | |
4919 FROB (WM_CHANGECBCHAIN) | |
4920 FROB (WM_HSCROLLCLIPBOARD) | |
4921 FROB (WM_QUERYNEWPALETTE) | |
4922 FROB (WM_PALETTEISCHANGING) | |
4923 FROB (WM_PALETTECHANGED) | |
4924 FROB (WM_HOTKEY) | |
4925 | |
4926 #if(WINVER >= 0x0400) | |
4927 FROB (WM_PRINT) | |
4928 FROB (WM_PRINTCLIENT) | |
4929 | |
4930 FROB (WM_HANDHELDFIRST) | |
4931 FROB (WM_HANDHELDLAST) | |
4932 | |
4933 FROB (WM_AFXFIRST) | |
4934 FROB (WM_AFXLAST) | |
4935 #endif /* WINVER >= 0x0400 */ | |
4936 | |
4937 FROB (WM_PENWINFIRST) | |
4938 FROB (WM_PENWINLAST) | |
4939 }; | |
4940 | |
4941 #undef FROB | |
4942 | |
4943 static void | |
4944 debug_output_mswin_message (HWND hwnd, UINT message_, WPARAM wParam, | |
4945 LPARAM lParam) | |
4946 { | |
4947 Lisp_Object frame = mswindows_find_frame (hwnd); | |
4948 int i; | |
4932 | 4949 const Ascbyte *str = 0; |
593 | 4950 /* struct mswin_message_debug *i_hate_cranking_out_code_like_this; */ |
4951 | |
4952 for (i = 0; i < countof (debug_mswin_messages); i++) | |
4953 { | |
647 | 4954 if (debug_mswin_messages[i].mess == (int) message_) |
593 | 4955 { |
4956 str = debug_mswin_messages[i].string; | |
4957 break; | |
4958 } | |
4959 } | |
4960 | |
4961 if (str) | |
4962 stderr_out ("%s", str); | |
4963 else | |
4964 stderr_out ("%x", message_); | |
4965 | |
4966 if (debug_mswindows_events > 1) | |
4967 { | |
4968 stderr_out (" wparam=%d lparam=%d hwnd=%x frame: ", | |
4969 wParam, (int) lParam, (unsigned int) hwnd); | |
4970 debug_print (frame); | |
903 | 4971 if (message_ == WM_WINDOWPOSCHANGED || |
4972 message_ == WM_WINDOWPOSCHANGING) | |
4973 { | |
4974 WINDOWPOS *wp = (WINDOWPOS *) lParam; | |
4975 stderr_out(" WINDOWPOS: x=%d, y=%d, h=%d, w=%d\n", | |
4976 wp->x, wp->y, wp->cx, wp->cy); | |
4977 } | |
4978 else if (message_ == WM_MOVE) | |
4979 { | |
4980 int x = (int)(short) LOWORD(lParam); /* horizontal position */ | |
4981 int y = (int)(short) HIWORD(lParam); /* vertical position */ | |
4982 stderr_out(" MOVE: x=%d, y=%d\n", x, y); | |
4983 } | |
4984 else if (message_ == WM_SIZE) | |
4985 { | |
4986 int w = (int)(short) LOWORD(lParam); /* width */ | |
4987 int h = (int)(short) HIWORD(lParam); /* height */ | |
4988 stderr_out(" SIZE: w=%d, h=%d\n", w, h); | |
4989 } | |
593 | 4990 } |
4991 else | |
4992 stderr_out ("\n"); | |
4993 } | |
4994 | |
4995 #endif /* DEBUG_XEMACS */ | |
4996 | |
428 | 4997 /************************************************************************/ |
4998 /* initialization */ | |
4999 /************************************************************************/ | |
5000 | |
5001 void | |
5002 reinit_vars_of_event_mswindows (void) | |
5003 { | |
5004 mswindows_pending_timers_count = 0; | |
5005 | |
1204 | 5006 mswindows_event_stream = xnew_and_zero (struct event_stream); |
428 | 5007 |
5008 mswindows_event_stream->event_pending_p = emacs_mswindows_event_pending_p; | |
5009 mswindows_event_stream->next_event_cb = emacs_mswindows_next_event; | |
5010 mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event; | |
788 | 5011 mswindows_event_stream->format_magic_event_cb = emacs_mswindows_format_magic_event; |
5012 mswindows_event_stream->compare_magic_event_cb= emacs_mswindows_compare_magic_event; | |
5013 mswindows_event_stream->hash_magic_event_cb = emacs_mswindows_hash_magic_event; | |
428 | 5014 mswindows_event_stream->add_timeout_cb = emacs_mswindows_add_timeout; |
5015 mswindows_event_stream->remove_timeout_cb = emacs_mswindows_remove_timeout; | |
1204 | 5016 mswindows_event_stream->drain_queue_cb = emacs_mswindows_drain_queue; |
428 | 5017 mswindows_event_stream->select_console_cb = emacs_mswindows_select_console; |
5018 mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console; | |
5019 mswindows_event_stream->select_process_cb = emacs_mswindows_select_process; | |
5020 mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process; | |
853 | 5021 mswindows_event_stream->create_io_streams_cb = emacs_mswindows_create_io_streams; |
5022 mswindows_event_stream->delete_io_streams_cb = emacs_mswindows_delete_io_streams; | |
442 | 5023 mswindows_event_stream->current_event_timestamp_cb = |
5024 emacs_mswindows_current_event_timestamp; | |
903 | 5025 |
5026 dde_eval_pending = 0; | |
428 | 5027 } |
5028 | |
5029 void | |
5030 vars_of_event_mswindows (void) | |
5031 { | |
5032 mswindows_s_dispatch_event_queue = Qnil; | |
5033 staticpro (&mswindows_s_dispatch_event_queue); | |
5034 mswindows_s_dispatch_event_queue_tail = Qnil; | |
1204 | 5035 dump_add_root_lisp_object (&mswindows_s_dispatch_event_queue_tail); |
428 | 5036 |
853 | 5037 mswindows_error_caught_in_modal_loop = 0; |
442 | 5038 |
903 | 5039 #ifdef HAVE_DRAGNDROP |
5040 Fprovide (Qdde); | |
5041 | |
5042 DEFVAR_LISP ("dde-advise-items", &Vdde_advise_items /* | |
5043 A list of allocated DDE advise items. | |
5044 Each item is an uninterned symbol, created using dde-alloc-advise-item. | |
5045 | |
5046 The symbol's value is the data which is returned to the DDE client when | |
5047 a request for the item is made (or a dde-advise call is made). | |
5048 | |
3025 | 5049 The symbol also has a `HSZ' property, which holds the DDE string handle |
903 | 5050 for the item, as a float. This is for internal use only, and should not |
5051 be modified. | |
5052 */ ); | |
5053 Vdde_advise_items = Qnil; | |
5054 | |
5055 dde_eval_result = Qnil; | |
5056 staticpro (&dde_eval_result); | |
5057 dde_eval_error = Qnil; | |
5058 staticpro (&dde_eval_error); | |
5059 #endif | |
5060 | |
442 | 5061 #ifdef DEBUG_XEMACS |
5062 DEFVAR_INT ("debug-mswindows-events", &debug_mswindows_events /* | |
593 | 5063 If non-zero, display debug information about Windows messages that XEmacs sees. |
442 | 5064 Information is displayed in a console window. Currently defined values are: |
5065 | |
593 | 5066 1 == non-verbose output (just the message name) |
5067 2 == verbose output (all parameters) | |
5068 3 == even more verbose output (extra debugging info) | |
442 | 5069 */ ); |
5070 debug_mswindows_events = 0; | |
5071 #endif | |
5072 | |
5073 DEFVAR_BOOL ("mswindows-alt-by-itself-activates-menu", | |
5074 &mswindows_alt_by_itself_activates_menu /* | |
5075 *Controls whether pressing and releasing the Alt key activates the menubar. | |
5076 This applies only if no intervening key was pressed. See also | |
5077 `menu-accelerator-enabled', which is probably the behavior you actually want. | |
428 | 5078 Default is t. |
5079 */ ); | |
5080 | |
442 | 5081 DEFVAR_BOOL ("mswindows-dynamic-frame-resize", |
5082 &mswindows_dynamic_frame_resize /* | |
428 | 5083 *Controls redrawing frame contents during mouse-drag or keyboard resize |
5084 operation. When non-nil, the frame is redrawn while being resized. When | |
5085 nil, frame is not redrawn, and exposed areas are filled with default | |
5086 MDI application background color. Note that this option only has effect | |
5087 if "Show window contents while dragging" is on in system Display/Plus! | |
5088 settings. | |
5089 Default is t on fast machines, nil on slow. | |
5090 */ ); | |
5091 | |
442 | 5092 DEFVAR_INT ("mswindows-mouse-button-tolerance", |
5093 &mswindows_mouse_button_tolerance /* | |
428 | 5094 *Analogue of double click interval for faking middle mouse events. |
5095 The value is the minimum time in milliseconds that must elapse between | |
5096 left/right button down events before they are considered distinct events. | |
5097 If both mouse buttons are depressed within this interval, a middle mouse | |
5098 button down event is generated instead. | |
5099 If negative or zero, currently set system default is used instead. | |
5100 */ ); | |
5101 | |
5102 DEFVAR_INT ("mswindows-num-mouse-buttons", &mswindows_num_mouse_buttons /* | |
5103 Number of physical mouse buttons. | |
5104 */ ); | |
5105 | |
442 | 5106 DEFVAR_INT ("mswindows-mouse-button-max-skew-x", |
5107 &mswindows_mouse_button_max_skew_x /* | |
428 | 5108 *Maximum horizontal distance in pixels between points in which left and |
5109 right button clicks occurred for them to be translated into single | |
5110 middle button event. Clicks must occur in time not longer than defined | |
5111 by the variable `mswindows-mouse-button-tolerance'. | |
5112 If negative or zero, currently set system default is used instead. | |
5113 */ ); | |
5114 | |
442 | 5115 DEFVAR_INT ("mswindows-mouse-button-max-skew-y", |
5116 &mswindows_mouse_button_max_skew_y /* | |
428 | 5117 *Maximum vertical distance in pixels between points in which left and |
5118 right button clicks occurred for them to be translated into single | |
5119 middle button event. Clicks must occur in time not longer than defined | |
5120 by the variable `mswindows-mouse-button-tolerance'. | |
5121 If negative or zero, currently set system default is used instead. | |
5122 */ ); | |
5123 | |
5124 mswindows_mouse_button_max_skew_x = 0; | |
5125 mswindows_mouse_button_max_skew_y = 0; | |
5126 mswindows_mouse_button_tolerance = 0; | |
442 | 5127 mswindows_alt_by_itself_activates_menu = 1; |
428 | 5128 } |
5129 | |
5130 void | |
5131 syms_of_event_mswindows (void) | |
5132 { | |
903 | 5133 #ifdef HAVE_DRAGNDROP |
5134 DEFSYMBOL(QHSZ); | |
5135 DEFSUBR(Fdde_alloc_advise_item); | |
5136 DEFSUBR(Fdde_free_advise_item); | |
5137 DEFSUBR(Fdde_advise); | |
5138 #endif | |
428 | 5139 } |
5140 | |
5141 void | |
5142 lstream_type_create_mswindows_selectable (void) | |
5143 { | |
853 | 5144 #ifndef CYGWIN |
428 | 5145 init_slurp_stream (); |
5146 init_shove_stream (); | |
5147 init_winsock_stream (); | |
5148 #endif | |
5149 } | |
5150 | |
5151 void | |
5152 init_event_mswindows_late (void) | |
5153 { | |
853 | 5154 #ifdef CYGWIN |
771 | 5155 windows_fd = retry_open ("/dev/windows", O_RDONLY | O_NONBLOCK, 0); |
814 | 5156 assert (windows_fd >= 0); |
428 | 5157 FD_SET (windows_fd, &input_wait_mask); |
814 | 5158 FD_ZERO (&zero_mask); |
428 | 5159 #endif |
5160 | |
5161 event_stream = mswindows_event_stream; | |
5162 | |
5163 mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE); | |
5164 mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS); | |
5165 } |