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