Mercurial > hg > xemacs-beta
annotate src/event-msw.c @ 4982:3c3c1d139863
Automatic merge
author | Ben Wing <ben@xemacs.org> |
---|---|
date | Fri, 05 Feb 2010 11:25:00 -0600 |
parents | 4aebb0131297 16112448d484 |
children | ae48681c47fa |
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 { |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
835 xfree (str->buffer); |
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 */ | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1755 args[0] = build_ascstring ("(progn "); |
903 | 1756 args[1] = str; |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1757 args[2] = build_ascstring (")"); |
903 | 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 HSZ hsz; | |
1815 struct gcpro gcpro1, gcpro2; | |
1816 | |
1817 if (!NILP (name)) | |
1818 CHECK_STRING (name); | |
1819 else | |
1820 { | |
1821 static int num = 0; | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1822 Ascbyte buf[20]; |
903 | 1823 sprintf (buf, "Tok%d", num); |
1824 ++num; | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1825 name = build_ascstring (buf); |
903 | 1826 } |
1827 | |
1828 token = Qnil; | |
1829 GCPRO2 (name, token); | |
1830 token = Fmake_symbol (name); | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
1831 hsz = qxeDdeCreateStringHandle (mswindows_dde_mlid, |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
1832 LISP_STRING_TO_TSTR (name), |
903 | 1833 XEUNICODE_P ? CP_WINUNICODE : CP_WINANSI); |
1834 | |
1835 Fput(token, QHSZ, make_float ((int)hsz)); | |
1836 Vdde_advise_items = Fcons (token, Vdde_advise_items); | |
1837 | |
1204 | 1838 RETURN_UNGCPRO (token); |
903 | 1839 } |
1840 | |
1841 DEFUN("dde-free-advise-item", Fdde_free_advise_item, 1, 1, 0, /* | |
1842 Free the resources associated with advise item ITEM. | |
1843 | |
1844 Frees all resources allocated to allow clients to set up advise loops | |
1845 on ITEM. It is assumed that no active advise loops remain. However, no | |
1846 problems should arise if they do - it's just that we won't ever send any | |
1847 notifications again. | |
1848 | |
1849 If the user does not free an advise item, resources will be leaked. | |
1850 */ | |
1851 (item)) | |
1852 { | |
1853 HSZ hsz; | |
1854 Lisp_Object val; | |
1855 | |
1856 CHECK_SYMBOL (item); | |
1857 val = Fget (item, QHSZ, Qnil); | |
1858 if (!FLOATP (val)) | |
1859 return Qnil; | |
1860 hsz = (HSZ)(int)XFLOAT_DATA (val); | |
1861 DdeFreeStringHandle (mswindows_dde_mlid, hsz); | |
1862 Vdde_advise_items = delq_no_quit (item, Vdde_advise_items); | |
1863 return Qnil; | |
1864 } | |
1865 | |
1866 DEFUN("dde-advise", Fdde_advise, 2, 2, 0, /* | |
1867 Post a DDE advise for ITEM with associated data DATA. | |
1868 | |
1869 Records the value DATA for sending back to clients waiting for | |
1870 notifications on DDE item ITEM in the system topic, and posts | |
1871 the advise transaction. | |
1872 | |
1873 ITEM must be an advise token allocated using dde-alloc-advise-item. | |
1874 */ | |
1875 (item, data)) | |
1876 { | |
1877 HSZ hsz; | |
1878 Lisp_Object val; | |
1879 | |
1880 CHECK_SYMBOL (item); | |
1881 val = Fget (item, QHSZ, Qnil); | |
1882 if (!FLOATP (val)) | |
1883 return Qnil; | |
1884 hsz = (HSZ)(int)XFLOAT_DATA (val); | |
1885 | |
1886 Fset (item, data); | |
1887 DdePostAdvise (mswindows_dde_mlid, mswindows_dde_topic_eval, hsz); | |
1888 return Qnil; | |
1889 } | |
1890 | |
428 | 1891 HDDEDATA CALLBACK |
2286 | 1892 mswindows_dde_callback (UINT uType, UINT uFmt, HCONV UNUSED (hconv), |
428 | 1893 HSZ hszTopic, HSZ hszItem, HDDEDATA hdata, |
2286 | 1894 DWORD UNUSED (dwData1), DWORD UNUSED (dwData2)) |
428 | 1895 { |
1896 switch (uType) | |
1897 { | |
1898 case XTYP_CONNECT: | |
903 | 1899 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system) |
1900 || !DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval)) | |
853 | 1901 return (HDDEDATA) TRUE; |
1902 return (HDDEDATA) FALSE; | |
428 | 1903 |
1904 case XTYP_WILDCONNECT: | |
1905 { | |
903 | 1906 /* We support two {service,topic} pairs */ |
1907 HSZPAIR pairs[3] = | |
771 | 1908 { |
903 | 1909 { mswindows_dde_service, mswindows_dde_topic_system }, |
1910 { mswindows_dde_service, mswindows_dde_topic_eval }, | |
1911 { 0, 0 } | |
1912 }; | |
1913 | |
1914 if ((!hszItem | |
1915 || !DdeCmpStringHandles (hszItem, mswindows_dde_service)) && | |
1916 (!hszTopic | |
1917 || !DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system) | |
1918 || !DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval))) | |
853 | 1919 return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE) pairs, |
428 | 1920 sizeof (pairs), 0L, 0, uFmt, 0)); |
1921 } | |
853 | 1922 return (HDDEDATA) NULL; |
428 | 1923 |
903 | 1924 case XTYP_ADVSTART: |
1925 if (!mswindows_dde_enable) | |
1926 return (HDDEDATA) FALSE; | |
1927 | |
1928 /* We only support advise loops on the eval topic for text data */ | |
1929 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval) | |
1930 && (uFmt == CF_TEXT || uFmt == CF_UNICODETEXT)) | |
1931 { | |
1932 /* Only allocated items or Result, are allowed */ | |
1933 if (!DdeCmpStringHandles (hszItem, mswindows_dde_item_result)) | |
1934 return (HDDEDATA) TRUE; | |
1935 | |
1936 { | |
1937 EXTERNAL_LIST_LOOP_2 (elt, Vdde_advise_items) | |
1938 { | |
1939 Lisp_Object val; | |
1940 HSZ hsz; | |
1941 if (!SYMBOLP (elt)) | |
1942 continue; | |
1943 val = Fget (elt, QHSZ, Qnil); | |
1944 if (!FLOATP (val)) | |
1945 continue; | |
1946 hsz = (HSZ) (int) XFLOAT_DATA (val); | |
1947 if (!DdeCmpStringHandles (hszItem, hsz)) | |
1948 return (HDDEDATA) TRUE; | |
1949 } | |
1950 } | |
1951 } | |
1952 return (HDDEDATA) FALSE; | |
1953 | |
1954 /* Both advise requests and normal requests work the same */ | |
1955 case XTYP_ADVREQ: | |
1956 case XTYP_REQUEST: | |
1957 if (!mswindows_dde_enable) | |
1958 return (HDDEDATA) NULL; | |
1959 | |
1960 if (DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval) != 0) | |
1961 return (HDDEDATA) NULL; | |
1962 | |
1963 /* If this is a normal request and we're in the middle of | |
1964 * an Execute, block until the Execute completes. | |
1965 */ | |
1966 if (dde_eval_pending && uType == XTYP_REQUEST) | |
1967 return (HDDEDATA) CBR_BLOCK; | |
1968 | |
1969 /* We can only support requests for ANSI or Unicode text */ | |
1970 if (uFmt != CF_TEXT && uFmt != CF_UNICODETEXT) | |
1971 return (HDDEDATA) NULL; | |
1972 | |
1973 { | |
1974 Lisp_Object args[2]; | |
1975 struct gcpro gcpro1; | |
1976 Lisp_Object res; | |
1977 Extbyte *result; | |
1978 DWORD bytes; | |
1979 | |
1980 args[0] = Qnil; | |
1981 args[1] = Qnil; | |
1982 GCPRO1 (args[0]); | |
1983 gcpro1.nvars = 2; | |
1984 | |
1985 | |
1986 if (!DdeCmpStringHandles (hszItem, mswindows_dde_item_result)) | |
1987 { | |
1988 if (NILP (dde_eval_error)) | |
1989 { | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1990 args[0] = build_ascstring ("OK: %s"); |
903 | 1991 args[1] = dde_eval_result; |
1992 } | |
1993 else | |
1994 { | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
1995 args[0] = build_ascstring ("ERR: %s"); |
903 | 1996 args[1] = dde_eval_error; |
1997 } | |
1998 } | |
1999 else | |
2000 { | |
2001 EXTERNAL_LIST_LOOP_2 (elt, Vdde_advise_items) | |
2002 { | |
2003 Lisp_Object val; | |
2004 HSZ hsz; | |
2005 if (!SYMBOLP (elt)) | |
2006 continue; | |
2007 val = Fget (elt, QHSZ, Qnil); | |
2008 if (!FLOATP (val)) | |
2009 continue; | |
2010 hsz = (HSZ) (int) XFLOAT_DATA (val); | |
2011 if (!DdeCmpStringHandles (hszItem, hsz)) | |
2012 args[1] = Fsymbol_value (elt); | |
2013 } | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
2014 args[0] = build_ascstring ("%s"); |
903 | 2015 } |
2016 | |
2017 res = Fformat (2, args); | |
2018 UNGCPRO; | |
2019 | |
2020 bytes = (uFmt == CF_TEXT ? 1 : 2) * (XSTRING_LENGTH (res) + 1); | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2021 result = |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2022 LISP_STRING_TO_EXTERNAL (res, |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2023 uFmt == CF_TEXT ? Qmswindows_multibyte |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2024 : Qmswindows_unicode); |
903 | 2025 |
2026 /* If we cannot create the data handle, this passes the null | |
2027 * return back to the client, which signals an error as we wish. | |
2028 */ | |
2029 return DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)result, | |
2030 bytes, 0L, hszItem, uFmt, 0); | |
2031 } | |
2032 | |
428 | 2033 case XTYP_EXECUTE: |
657 | 2034 if (!mswindows_dde_enable) |
2035 return (HDDEDATA) DDE_FBUSY; | |
2036 | |
903 | 2037 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_eval)) |
2038 { | |
2039 DWORD len; | |
2040 LPBYTE extcmd; | |
2041 Lisp_Object tmp; | |
2042 | |
2043 /* Grab a pointer to the raw data supplied */ | |
2044 extcmd = DdeAccessData (hdata, &len); | |
2045 | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2046 tmp = make_extstring ((Extbyte *) extcmd, len, Qmswindows_tstr); |
903 | 2047 |
2048 /* Release and free the data handle */ | |
2049 DdeUnaccessData (hdata); | |
2050 DdeFreeDataHandle (hdata); | |
2051 | |
2052 /* Set a flag to say that the evaluation isn't yet complete, | |
2053 * enqueue the evaluation, send a dummy event to trigger the | |
2054 * event loop (I've no idea why this is needed, but it works...) | |
2055 * and return success to the client. | |
2056 */ | |
2057 dde_eval_pending = 1; | |
2058 enqueue_magic_eval_event (dde_eval, tmp); | |
2059 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); | |
2060 return (HDDEDATA) DDE_FACK; | |
2061 } | |
2062 else if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)) | |
428 | 2063 { |
2064 DWORD len = DdeGetData (hdata, NULL, 0, 0); | |
2367 | 2065 Extbyte *extcmd = alloca_extbytes (len + 1); |
867 | 2066 Ibyte *cmd; |
2067 Ibyte *end; | |
428 | 2068 struct gcpro gcpro1, gcpro2; |
657 | 2069 Lisp_Object l_dndlist = Qnil; |
428 | 2070 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); |
2071 Lisp_Object frmcons, devcons, concons; | |
440 | 2072 Lisp_Event *event = XEVENT (emacs_event); |
428 | 2073 |
2367 | 2074 DdeGetData (hdata, (LPBYTE) extcmd, len, 0); |
428 | 2075 DdeFreeDataHandle (hdata); |
2076 | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2077 cmd = SIZED_EXTERNAL_TO_ITEXT (extcmd, len, Qmswindows_tstr); |
771 | 2078 |
428 | 2079 /* Check syntax & that it's an [Open("foo")] command, which we |
2080 * treat like a file drop */ | |
2081 if (*cmd == '[') | |
2082 cmd++; | |
2367 | 2083 if (qxestrncasecmp_ascii (cmd, MSWINDOWS_DDE_ITEM_OPEN, |
771 | 2084 strlen (MSWINDOWS_DDE_ITEM_OPEN))) |
428 | 2085 return DDE_FNOTPROCESSED; |
2086 cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN); | |
771 | 2087 while (*cmd == ' ') |
428 | 2088 cmd++; |
771 | 2089 if (*cmd != '(' || *(cmd + 1) != '\"') |
428 | 2090 return DDE_FNOTPROCESSED; |
771 | 2091 end = (cmd += 2); |
2092 while (*end && *end != '\"') | |
428 | 2093 end++; |
2094 if (!*end) | |
2095 return DDE_FNOTPROCESSED; | |
2096 *end = '\0'; | |
771 | 2097 if (*++end != ')') |
428 | 2098 return DDE_FNOTPROCESSED; |
771 | 2099 if (*++end == ']') |
428 | 2100 end++; |
2101 if (*end) | |
2102 return DDE_FNOTPROCESSED; | |
2103 | |
771 | 2104 { |
2105 /* The drag-n-drop code in dragdrop.el expects pseudo-URL's, | |
2106 consisting of just file: followed by the filename. This | |
2107 should maybe work, but both Netscape and IE complain | |
2108 whenever they're not given the full file spec, like | |
2109 | |
2110 file:///C|/foo/bar/ or equivalently | |
2111 file:///C:/foo/bar/ (less portably) | |
2112 | |
2113 they don't allow relative paths at all! this is way bogus. */ | |
2114 cmd = urlify_filename (cmd); | |
4953
304aebb79cd3
function renamings to track names of char typedefs
Ben Wing <ben@xemacs.org>
parents:
4952
diff
changeset
|
2115 l_dndlist = build_istring (cmd); |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2116 xfree (cmd); |
771 | 2117 } |
428 | 2118 GCPRO2 (emacs_event, l_dndlist); |
2119 | |
2120 /* Find a mswindows frame */ | |
2121 event->channel = Qnil; | |
2122 FRAME_LOOP_NO_BREAK (frmcons, devcons, concons) | |
2123 { | |
2124 Lisp_Object frame = XCAR (frmcons); | |
2125 if (FRAME_TYPE_P (XFRAME (frame), mswindows)) | |
2126 event->channel = frame; | |
2127 }; | |
2128 assert (!NILP (event->channel)); | |
2129 | |
964 | 2130 SET_EVENT_TIMESTAMP (event, GetTickCount()); |
2131 SET_EVENT_TYPE (event, misc_user_event); | |
1204 | 2132 SET_EVENT_MISC_USER_BUTTON (event, 1); |
2133 SET_EVENT_MISC_USER_MODIFIERS (event, 0); | |
2134 SET_EVENT_MISC_USER_X (event, -1); | |
2135 SET_EVENT_MISC_USER_Y (event, -1); | |
2136 SET_EVENT_MISC_USER_FUNCTION (event, | |
964 | 2137 Qdragdrop_drop_dispatch); |
1204 | 2138 SET_EVENT_MISC_USER_OBJECT (event, |
964 | 2139 Fcons (Qdragdrop_URL, |
2140 Fcons (l_dndlist, Qnil))); | |
428 | 2141 mswindows_enqueue_dispatch_event (emacs_event); |
2142 UNGCPRO; | |
2143 return (HDDEDATA) DDE_FACK; | |
2144 } | |
2145 DdeFreeDataHandle (hdata); | |
2146 return (HDDEDATA) DDE_FNOTPROCESSED; | |
2147 | |
2148 default: | |
2149 return (HDDEDATA) NULL; | |
2150 } | |
2151 } | |
2152 #endif | |
2153 | |
2154 /* | |
442 | 2155 * Helper to do repainting - repaints can happen both from the windows |
2156 * procedure and from magic events | |
2157 */ | |
2158 static void | |
2159 mswindows_handle_paint (struct frame *frame) | |
2160 { | |
2161 HWND hwnd = FRAME_MSWINDOWS_HANDLE (frame); | |
2162 | |
2163 /* According to the docs we need to check GetUpdateRect() before | |
2164 actually doing a WM_PAINT */ | |
2165 if (GetUpdateRect (hwnd, NULL, FALSE)) | |
2166 { | |
2167 PAINTSTRUCT paintStruct; | |
2168 int x, y, width, height; | |
2169 | |
2170 BeginPaint (hwnd, &paintStruct); | |
2171 x = paintStruct.rcPaint.left; | |
2172 y = paintStruct.rcPaint.top; | |
2173 width = paintStruct.rcPaint.right - paintStruct.rcPaint.left; | |
2174 height = paintStruct.rcPaint.bottom - paintStruct.rcPaint.top; | |
2175 /* Normally we want to ignore expose events when child | |
2176 windows are unmapped, however once we are in the guts of | |
2177 WM_PAINT we need to make sure that we don't register | |
2178 unmaps then because they will not actually occur. */ | |
2179 /* #### commenting out the next line seems to fix some problems | |
2180 but not all. only andy currently understands this stuff and | |
2181 he needs to review it more carefully. --ben */ | |
2182 if (!check_for_ignored_expose (frame, x, y, width, height)) | |
2183 { | |
2184 hold_ignored_expose_registration = 1; | |
1318 | 2185 redisplay_redraw_exposed_area (frame, x, y, width, height); |
442 | 2186 hold_ignored_expose_registration = 0; |
2187 } | |
2188 EndPaint (hwnd, &paintStruct); | |
2189 } | |
2190 } | |
2191 | |
2192 /* | |
2193 * Returns 1 if a key is a real modifier or special key, which | |
440 | 2194 * is better handled by DefWindowProc |
2195 */ | |
2196 static int | |
2197 key_needs_default_processing_p (UINT vkey) | |
2198 { | |
442 | 2199 if (mswindows_alt_by_itself_activates_menu && vkey == VK_MENU |
2200 /* if we let ALT activate the menu like this, then sticky ALT-modified | |
2201 keystrokes become impossible. */ | |
2202 && !modifier_keys_are_sticky) | |
440 | 2203 return 1; |
2204 | |
2205 return 0; | |
2206 } | |
2207 | |
442 | 2208 /* key-handling code is always ugly. It just ends up working out |
2209 that way. | |
2210 | |
2211 #### Most of the sticky-modifier code below is copied from similar | |
2212 code in event-Xt.c. They should somehow or other be merged. | |
2213 | |
2214 Here are some pointers: | |
2215 | |
2216 -- DOWN_MASK indicates which modifiers should be treated as "down" | |
2217 when the corresponding upstroke happens. It gets reset for | |
2218 a particular modifier when that modifier goes up, and reset | |
2219 for all modifiers when a non-modifier key is pressed. Example: | |
2220 | |
2221 I press Control-A-Shift and then release Control-A-Shift. | |
2222 I want the Shift key to be sticky but not the Control key. | |
2223 | |
2224 -- If a modifier key is sticky, I can unstick it by pressing | |
2225 the modifier key again. */ | |
2226 | |
2227 static WPARAM last_downkey; | |
2228 static int need_to_add_mask, down_mask; | |
2229 | |
2230 #define XEMSW_LCONTROL (1<<0) | |
2231 #define XEMSW_RCONTROL (1<<1) | |
2232 #define XEMSW_LSHIFT (1<<2) | |
2233 #define XEMSW_RSHIFT (1<<3) | |
2234 #define XEMSW_LMENU (1<<4) | |
2235 #define XEMSW_RMENU (1<<5) | |
2236 | |
2237 static int | |
2238 mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam, | |
2239 int downp, int keyp) | |
2240 { | |
2241 int mods = 0; | |
2242 | |
2243 if (!modifier_keys_are_sticky) /* Optimize for non-sticky modifiers */ | |
2244 return 0; | |
2245 | |
2246 if (! (keyp && | |
2247 (wParam == VK_CONTROL || wParam == VK_LCONTROL || | |
2248 wParam == VK_RCONTROL || | |
2249 wParam == VK_MENU || wParam == VK_LMENU || | |
2250 wParam == VK_RMENU || | |
2251 wParam == VK_SHIFT || wParam == VK_LSHIFT || | |
2252 wParam == VK_RSHIFT))) | |
2253 { /* Not a modifier key */ | |
2254 if (downp && keyp && !last_downkey) | |
2255 last_downkey = wParam; | |
2256 /* If I hold press-and-release the Control key and then press | |
2257 and hold down the right arrow, I want it to auto-repeat | |
2258 Control-Right. On the other hand, if I do the same but | |
2259 manually press the Right arrow a bunch of times, I want | |
2260 to see one Control-Right and then a bunch of Rights. | |
2261 This means that we need to distinguish between an | |
2262 auto-repeated key and a key pressed and released a bunch | |
2263 of times. */ | |
2264 else if ((downp && !keyp) || | |
2265 (downp && keyp && last_downkey && | |
2266 (wParam != last_downkey || | |
2267 /* the "previous key state" bit indicates autorepeat */ | |
2268 ! (lParam & (1 << 30))))) | |
2269 { | |
2270 need_to_add_mask = 0; | |
2271 last_downkey = 0; | |
2272 } | |
2273 if (downp) | |
2274 down_mask = 0; | |
2275 | |
2276 mods = need_to_add_mask; | |
2277 } | |
2278 else /* Modifier key pressed */ | |
2279 { | |
2280 /* If a non-modifier key was pressed in the middle of a bunch | |
2281 of modifiers, then it unsticks all the modifiers that were | |
2282 previously pressed. We cannot unstick the modifiers until | |
2283 now because we want to check for auto-repeat of the | |
2284 non-modifier key. */ | |
2285 | |
2286 if (last_downkey) | |
2287 { | |
2288 last_downkey = 0; | |
2289 need_to_add_mask = 0; | |
2290 } | |
2291 | |
2292 #define FROB(mask) \ | |
2293 do { \ | |
2294 if (downp && keyp) \ | |
2295 { \ | |
2296 /* If modifier key is already sticky, \ | |
2297 then unstick it. Note that we do \ | |
2298 not test down_mask to deal with the \ | |
2299 unlikely but possible case that the \ | |
2300 modifier key auto-repeats. */ \ | |
2301 if (need_to_add_mask & mask) \ | |
2302 { \ | |
2303 need_to_add_mask &= ~mask; \ | |
2304 down_mask &= ~mask; \ | |
2305 } \ | |
2306 else \ | |
2307 down_mask |= mask; \ | |
2308 } \ | |
2309 else \ | |
2310 { \ | |
2311 if (down_mask & mask) \ | |
2312 { \ | |
2313 down_mask &= ~mask; \ | |
2314 need_to_add_mask |= mask; \ | |
2315 } \ | |
2316 } \ | |
2317 } while (0) | |
2318 | |
2319 if ((wParam == VK_CONTROL && (lParam & 0x1000000)) | |
2320 || wParam == VK_RCONTROL) | |
2321 FROB (XEMSW_RCONTROL); | |
2322 if ((wParam == VK_CONTROL && !(lParam & 0x1000000)) | |
2323 || wParam == VK_LCONTROL) | |
2324 FROB (XEMSW_LCONTROL); | |
2325 | |
2326 if ((wParam == VK_SHIFT && (lParam & 0x1000000)) | |
2327 || wParam == VK_RSHIFT) | |
2328 FROB (XEMSW_RSHIFT); | |
2329 if ((wParam == VK_SHIFT && !(lParam & 0x1000000)) | |
2330 || wParam == VK_LSHIFT) | |
2331 FROB (XEMSW_LSHIFT); | |
2332 | |
2333 if ((wParam == VK_MENU && (lParam & 0x1000000)) | |
2334 || wParam == VK_RMENU) | |
2335 FROB (XEMSW_RMENU); | |
2336 if ((wParam == VK_MENU && !(lParam & 0x1000000)) | |
2337 || wParam == VK_LMENU) | |
2338 FROB (XEMSW_LMENU); | |
2339 } | |
2340 #undef FROB | |
2341 | |
2342 if (mods && downp) | |
2343 { | |
2344 BYTE keymap[256]; | |
2345 | |
2346 GetKeyboardState (keymap); | |
2347 | |
2348 if (mods & XEMSW_LCONTROL) | |
2349 { | |
2350 keymap [VK_CONTROL] |= 0x80; | |
2351 keymap [VK_LCONTROL] |= 0x80; | |
2352 } | |
2353 if (mods & XEMSW_RCONTROL) | |
2354 { | |
2355 keymap [VK_CONTROL] |= 0x80; | |
2356 keymap [VK_RCONTROL] |= 0x80; | |
2357 } | |
2358 | |
2359 if (mods & XEMSW_LSHIFT) | |
2360 { | |
2361 keymap [VK_SHIFT] |= 0x80; | |
2362 keymap [VK_LSHIFT] |= 0x80; | |
2363 } | |
2364 if (mods & XEMSW_RSHIFT) | |
2365 { | |
2366 keymap [VK_SHIFT] |= 0x80; | |
2367 keymap [VK_RSHIFT] |= 0x80; | |
2368 } | |
2369 | |
2370 if (mods & XEMSW_LMENU) | |
2371 { | |
2372 keymap [VK_MENU] |= 0x80; | |
2373 keymap [VK_LMENU] |= 0x80; | |
2374 } | |
2375 if (mods & XEMSW_RMENU) | |
2376 { | |
2377 keymap [VK_MENU] |= 0x80; | |
2378 keymap [VK_RMENU] |= 0x80; | |
2379 } | |
2380 | |
2381 SetKeyboardState (keymap); | |
2382 return 1; | |
2383 } | |
2384 | |
2385 return 0; | |
2386 } | |
2387 | |
2388 static void | |
2389 clear_sticky_modifiers (void) | |
2390 { | |
2391 need_to_add_mask = 0; | |
2392 last_downkey = 0; | |
2393 down_mask = 0; | |
2394 } | |
2395 | |
2396 #ifdef DEBUG_XEMACS | |
2397 | |
2398 #if 0 | |
2399 | |
2400 static void | |
2401 output_modifier_keyboard_state (void) | |
2402 { | |
2403 BYTE keymap[256]; | |
2404 | |
2405 GetKeyboardState (keymap); | |
2406 | |
2407 stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", | |
2408 keymap[VK_MENU] & 0x80 ? 1 : 0, | |
2409 keymap[VK_MENU] & 0x1 ? 1 : 0, | |
2410 keymap[VK_LMENU] & 0x80 ? 1 : 0, | |
2411 keymap[VK_LMENU] & 0x1 ? 1 : 0, | |
2412 keymap[VK_RMENU] & 0x80 ? 1 : 0, | |
2413 keymap[VK_RMENU] & 0x1 ? 1 : 0); | |
2414 stderr_out ("GetKeyboardState VK_CONTROL %d %d VK_LCONTROL %d %d VK_RCONTROL %d %d\n", | |
2415 keymap[VK_CONTROL] & 0x80 ? 1 : 0, | |
2416 keymap[VK_CONTROL] & 0x1 ? 1 : 0, | |
2417 keymap[VK_LCONTROL] & 0x80 ? 1 : 0, | |
2418 keymap[VK_LCONTROL] & 0x1 ? 1 : 0, | |
2419 keymap[VK_RCONTROL] & 0x80 ? 1 : 0, | |
2420 keymap[VK_RCONTROL] & 0x1 ? 1 : 0); | |
2421 stderr_out ("GetKeyboardState VK_SHIFT %d %d VK_LSHIFT %d %d VK_RSHIFT %d %d\n", | |
2422 keymap[VK_SHIFT] & 0x80 ? 1 : 0, | |
2423 keymap[VK_SHIFT] & 0x1 ? 1 : 0, | |
2424 keymap[VK_LSHIFT] & 0x80 ? 1 : 0, | |
2425 keymap[VK_LSHIFT] & 0x1 ? 1 : 0, | |
2426 keymap[VK_RSHIFT] & 0x80 ? 1 : 0, | |
2427 keymap[VK_RSHIFT] & 0x1 ? 1 : 0); | |
2428 } | |
2429 | |
2430 #endif | |
2431 | |
2432 /* try to debug the stuck-alt-key problem. | |
2433 | |
2434 #### this happens only inconsistently, and may only happen when using | |
2435 StickyKeys in the Win2000 accessibility section of the control panel, | |
2436 which is extremely broken for other reasons. */ | |
2437 | |
2438 static void | |
2439 output_alt_keyboard_state (void) | |
2440 { | |
2441 BYTE keymap[256]; | |
2442 SHORT keystate[3]; | |
1242 | 2443 /* SHORT asyncstate[3]; */ |
442 | 2444 |
2445 GetKeyboardState (keymap); | |
2446 keystate[0] = GetKeyState (VK_MENU); | |
2447 keystate[1] = GetKeyState (VK_LMENU); | |
2448 keystate[2] = GetKeyState (VK_RMENU); | |
2449 /* Doing this interferes with key processing. */ | |
2450 /* asyncstate[0] = GetAsyncKeyState (VK_MENU); */ | |
2451 /* asyncstate[1] = GetAsyncKeyState (VK_LMENU); */ | |
2452 /* asyncstate[2] = GetAsyncKeyState (VK_RMENU); */ | |
2453 | |
2454 stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", | |
2455 keymap[VK_MENU] & 0x80 ? 1 : 0, | |
2456 keymap[VK_MENU] & 0x1 ? 1 : 0, | |
2457 keymap[VK_LMENU] & 0x80 ? 1 : 0, | |
2458 keymap[VK_LMENU] & 0x1 ? 1 : 0, | |
2459 keymap[VK_RMENU] & 0x80 ? 1 : 0, | |
2460 keymap[VK_RMENU] & 0x1 ? 1 : 0); | |
2461 stderr_out ("GetKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", | |
2462 keystate[0] & 0x8000 ? 1 : 0, | |
2463 keystate[0] & 0x1 ? 1 : 0, | |
2464 keystate[1] & 0x8000 ? 1 : 0, | |
2465 keystate[1] & 0x1 ? 1 : 0, | |
2466 keystate[2] & 0x8000 ? 1 : 0, | |
2467 keystate[2] & 0x1 ? 1 : 0); | |
2468 /* stderr_out ("GetAsyncKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", */ | |
2469 /* asyncstate[0] & 0x8000 ? 1 : 0, */ | |
2470 /* asyncstate[0] & 0x1 ? 1 : 0, */ | |
2471 /* asyncstate[1] & 0x8000 ? 1 : 0, */ | |
2472 /* asyncstate[1] & 0x1 ? 1 : 0, */ | |
2473 /* asyncstate[2] & 0x8000 ? 1 : 0, */ | |
2474 /* asyncstate[2] & 0x1 ? 1 : 0); */ | |
2475 } | |
2476 | |
2477 #endif /* DEBUG_XEMACS */ | |
2478 | |
2479 | |
440 | 2480 /* |
428 | 2481 * The windows procedure for the window class XEMACS_CLASS |
2482 */ | |
2483 LRESULT WINAPI | |
442 | 2484 mswindows_wnd_proc (HWND hwnd, UINT message_, WPARAM wParam, LPARAM lParam) |
428 | 2485 { |
1204 | 2486 /* Note: Remember to initialize emacs_event and event before use. This |
2487 code calls code that can GC. You must GCPRO before calling such | |
2488 code. */ | |
428 | 2489 Lisp_Object emacs_event = Qnil; |
2490 Lisp_Object fobj = Qnil; | |
2491 | |
440 | 2492 Lisp_Event *event; |
428 | 2493 struct frame *frame; |
647 | 2494 struct mswindows_frame *msframe; |
428 | 2495 |
3092 | 2496 #ifndef NEW_GC |
611 | 2497 /* If you hit this, rewrite the offending API call to occur after GC, |
2498 using register_post_gc_action(). */ | |
2499 assert (!gc_in_progress); | |
3263 | 2500 #endif /* not NEW_GC */ |
593 | 2501 |
2502 #ifdef DEBUG_XEMACS | |
2503 if (debug_mswindows_events) | |
2504 debug_output_mswin_message (hwnd, message_, wParam, lParam); | |
2505 #endif /* DEBUG_XEMACS */ | |
442 | 2506 |
771 | 2507 assert (!qxeGetWindowLong (hwnd, GWL_USERDATA)); |
442 | 2508 switch (message_) |
428 | 2509 { |
442 | 2510 case WM_DESTROYCLIPBOARD: |
771 | 2511 mswindows_handle_destroyclipboard (); |
442 | 2512 break; |
2513 | |
2514 case WM_ERASEBKGND: | |
2515 /* Erase background only during non-dynamic sizing */ | |
771 | 2516 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 2517 if (msframe->sizing && !mswindows_dynamic_frame_resize) |
2518 goto defproc; | |
2519 return 1; | |
2520 | |
2521 case WM_CLOSE: | |
2522 fobj = mswindows_find_frame (hwnd); | |
853 | 2523 mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, |
2524 Qt)); | |
440 | 2525 break; |
428 | 2526 |
442 | 2527 case WM_KEYUP: |
2528 case WM_SYSKEYUP: | |
2529 | |
2530 /* See Win95 comment under WM_KEYDOWN */ | |
2531 { | |
2532 BYTE keymap[256]; | |
2533 int should_set_keymap = 0; | |
2534 | |
2535 #ifdef DEBUG_XEMACS | |
593 | 2536 if (debug_mswindows_events > 2) |
2537 output_alt_keyboard_state (); | |
442 | 2538 #endif /* DEBUG_XEMACS */ |
2539 | |
2540 mswindows_handle_sticky_modifiers (wParam, lParam, 0, 1); | |
2541 if (wParam == VK_CONTROL) | |
2542 { | |
2543 GetKeyboardState (keymap); | |
2544 keymap [(lParam & 0x1000000) ? VK_RCONTROL : VK_LCONTROL] &= ~0x80; | |
2545 should_set_keymap = 1; | |
2546 } | |
2547 else if (wParam == VK_MENU) | |
2548 { | |
2549 GetKeyboardState (keymap); | |
2550 keymap [(lParam & 0x1000000) ? VK_RMENU : VK_LMENU] &= ~0x80; | |
2551 should_set_keymap = 1; | |
2552 } | |
2553 | |
2554 if (should_set_keymap) | |
1242 | 2555 /* && (message_ != WM_SYSKEYUP */ |
2556 /* || NILP (Vmenu_accelerator_enabled))) */ | |
428 | 2557 SetKeyboardState (keymap); |
2558 | |
2559 } | |
442 | 2560 |
2561 if (key_needs_default_processing_p (wParam)) | |
2562 goto defproc; | |
2563 else | |
2564 break; | |
2565 | |
2566 case WM_KEYDOWN: | |
2567 case WM_SYSKEYDOWN: | |
2568 | |
2569 /* In some locales the right-hand Alt key is labelled AltGr. This key | |
2570 * should produce alternative characters when combined with another key. | |
2571 * eg on a German keyboard pressing AltGr+q should produce '@'. | |
2572 * AltGr generates exactly the same keystrokes as LCtrl+RAlt. But if | |
2573 * TranslateMessage() is called with *any* combination of Ctrl+Alt down, | |
2574 * it translates as if AltGr were down. | |
2575 * We get round this by removing all modifiers from the keymap before | |
2576 * calling TranslateMessage() unless AltGr is *really* down. */ | |
428 | 2577 { |
442 | 2578 BYTE keymap_trans[256]; |
2579 BYTE keymap_orig[256]; | |
2580 BYTE keymap_sticky[256]; | |
771 | 2581 /* WARNING: XEmacs code paths are far more subtle than you |
2582 think. In particular, QUIT checking will query and remove | |
2583 events, including keyboard events, from the queue. (QUIT is | |
2584 definitely invoked from TO_INTERNAL_FORMAT().) If we do | |
2585 this recursively anywhere in the following code, it will | |
2586 mess certain things up -- in particular, the OS-provided | |
2587 sticky modifier code available as part of the accessibility | |
2588 package. | |
2589 | |
2590 (Academic question: If QUIT checking is supposed to be | |
2591 triggered only every 1/4 second, why is it getting | |
2592 consistently triggered here? I saw the problem | |
2593 consistently. Answer: It appears that, currently, | |
2594 sometimes the code to pump messages is wrapped with | |
2595 begin_dont_check_for_quit() and sometimes it isn't. (#### | |
2596 FIX THIS SHIT!) cmdloop.c, for example, has it, but not | |
2597 everywhere. The current games with avoiding QUIT mean that | |
2598 the 1/4-second timer consistently fires while | |
2599 dont_check_for_quit is set [which causes the quit check to | |
2600 get deferred but the flag is still on], and so the next | |
2601 time it's unset and we call QUIT is *right here*. | |
2602 | |
2603 In my stderr-proc ws I majorly cleaned up the whole shit by | |
2604 just wrapping all the entry points in dont_check_for_quit. | |
2605 This fixed the remaining bugs with C-g getting interpreted | |
2606 wrong.) | |
2607 | |
2608 #### We should probably wrap this whole function in | |
2609 begin_dont_check_for_quit(); but then we should set this | |
2610 back to 0 when handling a menu callback, which gets invoked | |
2611 from within this function, specifically from | |
2612 DefWindowProc(). (We already do the latter in my new | |
2613 stderr-proc ws, because in that ws next_event_internal() | |
2614 calls begin_dont_check_for_quit(). */ | |
2615 | |
2616 int count = begin_dont_check_for_quit (); | |
442 | 2617 int has_AltGr = mswindows_current_layout_has_AltGr (); |
502 | 2618 int mods = 0, mods_with_shift = 0; |
442 | 2619 int extendedp = lParam & 0x1000000; |
2620 Lisp_Object keysym; | |
2621 int sticky_changed; | |
2622 | |
2623 #ifdef DEBUG_XEMACS | |
593 | 2624 if (debug_mswindows_events > 2) |
2625 output_alt_keyboard_state (); | |
442 | 2626 #endif /* DEBUG_XEMACS */ |
2627 | |
2628 GetKeyboardState (keymap_orig); | |
2629 frame = XFRAME (mswindows_find_frame (hwnd)); | |
2630 if ((sticky_changed = | |
2631 mswindows_handle_sticky_modifiers (wParam, lParam, 1, 1))) | |
428 | 2632 { |
442 | 2633 GetKeyboardState (keymap_sticky); |
2634 if (keymap_sticky[VK_MENU] & 0x80) | |
2635 { | |
2636 message_ = WM_SYSKEYDOWN; | |
2637 /* We have to set the "context bit" so that the | |
2638 TranslateMessage() call below that generates the | |
2639 SYSCHAR message does its thing; see the documentation | |
2640 on WM_SYSKEYDOWN */ | |
2641 lParam |= 1 << 29; | |
2642 } | |
428 | 2643 } |
2644 else | |
442 | 2645 memcpy (keymap_sticky, keymap_orig, 256); |
2646 | |
2647 mods = mswindows_modifier_state (keymap_sticky, (DWORD) -1, has_AltGr); | |
502 | 2648 mods_with_shift = mods; |
442 | 2649 |
2650 /* Handle non-printables */ | |
2651 if (!NILP (keysym = mswindows_key_to_emacs_keysym (wParam, mods, | |
2652 extendedp))) | |
428 | 2653 { |
442 | 2654 mswindows_enqueue_keypress_event (hwnd, keysym, mods); |
2655 if (sticky_changed) | |
2656 SetKeyboardState (keymap_orig); | |
428 | 2657 } |
442 | 2658 else /* Normal keys & modifiers */ |
428 | 2659 { |
442 | 2660 POINT pnt = { LOWORD (GetMessagePos()), HIWORD (GetMessagePos()) }; |
2661 MSG msg, tranmsg; | |
1204 | 2662 #ifdef HAVE_MENUBARS |
442 | 2663 int potential_accelerator = 0; |
1204 | 2664 #endif |
442 | 2665 int got_accelerator = 0; |
771 | 2666 /* No need to gcpro because the event is already on a |
2667 queue when we retrieve it. */ | |
2668 Lisp_Object lastev = Qnil; | |
442 | 2669 |
2670 msg.hwnd = hwnd; | |
2671 msg.message = message_; | |
2672 msg.wParam = wParam; | |
2673 msg.lParam = lParam; | |
2674 msg.time = GetMessageTime(); | |
2675 msg.pt = pnt; | |
2676 | |
2677 /* GetKeyboardState() does not work as documented on Win95. We have | |
2678 * to loosely track Left and Right modifiers on behalf of the OS, | |
2679 * without screwing up Windows NT which tracks them properly. */ | |
2680 if (wParam == VK_CONTROL) | |
2681 { | |
2682 keymap_orig[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80; | |
2683 keymap_sticky[extendedp ? VK_RCONTROL : VK_LCONTROL] |= 0x80; | |
2684 } | |
2685 else if (wParam == VK_MENU) | |
2686 { | |
2687 keymap_orig[extendedp ? VK_RMENU : VK_LMENU] |= 0x80; | |
2688 keymap_sticky[extendedp ? VK_RMENU : VK_LMENU] |= 0x80; | |
2689 } | |
2690 | |
827 | 2691 #ifdef HAVE_MENUBARS |
442 | 2692 if (!NILP (Vmenu_accelerator_enabled) && |
2693 !(mods & XEMACS_MOD_SHIFT) && message_ == WM_SYSKEYDOWN) | |
2694 potential_accelerator = 1; | |
827 | 2695 #endif |
442 | 2696 |
2697 /* Remove shift modifier from an ascii character */ | |
2698 mods &= ~XEMACS_MOD_SHIFT; | |
2699 | |
2700 memcpy (keymap_trans, keymap_sticky, 256); | |
2701 | |
2702 /* Clear control and alt modifiers unless AltGr is pressed */ | |
2703 keymap_trans[VK_RCONTROL] = 0; | |
2704 keymap_trans[VK_LMENU] = 0; | |
2705 if (!has_AltGr || !(keymap_trans[VK_LCONTROL] & 0x80) | |
2706 || !(keymap_trans[VK_RMENU] & 0x80)) | |
2707 { | |
2708 keymap_trans[VK_LCONTROL] = 0; | |
2709 keymap_trans[VK_CONTROL] = 0; | |
2710 keymap_trans[VK_RMENU] = 0; | |
2711 keymap_trans[VK_MENU] = 0; | |
2712 } | |
2713 SetKeyboardState (keymap_trans); | |
2714 | |
2715 /* Maybe generate some WM_[SYS]CHARs in the queue */ | |
2716 TranslateMessage (&msg); | |
2717 | |
771 | 2718 while (qxePeekMessage (&tranmsg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE) |
2719 || qxePeekMessage (&tranmsg, hwnd, WM_SYSCHAR, WM_SYSCHAR, | |
2720 PM_REMOVE)) | |
442 | 2721 { |
502 | 2722 int mods_with_quit = mods; |
771 | 2723 int length; |
2724 Extbyte extchar[4]; | |
867 | 2725 Ibyte *intchar; |
2726 Ichar ch; | |
771 | 2727 |
2728 if (XEUNICODE_P) | |
2729 { | |
2730 length = unicode_char_to_text (tranmsg.wParam, extchar); | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2731 intchar = SIZED_EXTERNAL_TO_ITEXT (extchar, length, |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2732 Qmswindows_unicode); |
867 | 2733 ch = itext_ichar (intchar); |
771 | 2734 } |
2735 else | |
2736 { | |
2737 length = ansi_char_to_text (tranmsg.wParam, extchar); | |
2738 intchar = (convert_multibyte_to_internal_malloc | |
2739 (extchar, length, | |
2740 mswindows_locale_to_code_page | |
2741 /* See intl-win32.c for an explanation of | |
2742 the following */ | |
2743 ((LCID) GetKeyboardLayout (0) & 0xFFFF), | |
2744 NULL)); | |
867 | 2745 ch = itext_ichar (intchar); |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2746 xfree (intchar); |
771 | 2747 } |
442 | 2748 |
593 | 2749 #ifdef DEBUG_XEMACS |
2750 if (debug_mswindows_events) | |
2751 { | |
2752 stderr_out ("-> "); | |
2753 debug_output_mswin_message (tranmsg.hwnd, tranmsg.message, | |
2754 tranmsg.wParam, | |
2755 tranmsg.lParam); | |
2756 } | |
2757 #endif /* DEBUG_XEMACS */ | |
2758 | |
827 | 2759 #ifdef HAVE_MENUBARS |
1204 | 2760 if (potential_accelerator && !got_accelerator && |
2761 mswindows_char_is_accelerator (frame, ch)) | |
442 | 2762 { |
2763 got_accelerator = 1; | |
2764 break; | |
2765 } | |
827 | 2766 #endif /* HAVE_MENUBARS */ |
2767 | |
771 | 2768 lastev = mswindows_enqueue_keypress_event (hwnd, |
2769 make_char (ch), | |
2770 mods_with_quit); | |
442 | 2771 } /* while */ |
2772 | |
771 | 2773 /* Also figure out what the character would be in other |
2774 possible keyboard layouts, in this order: | |
2775 | |
2776 -- current language environment | |
2777 -- user default language environment | |
2778 -- system default language environment | |
2779 -- same three, but checking the underlying virtual key, | |
2780 and only paying attention if it's alphabetic | |
2781 -- US ASCII | |
2782 | |
2783 See events.h, struct key_data, for why we do this. | |
2784 */ | |
2785 | |
2786 if (!NILP (lastev)) | |
2787 { | |
2788 int i; | |
2789 int scan = (lParam >> 16) && 0xFF; | |
2790 | |
2791 for (i = 0; i < KEYCHAR_LAST; i++) | |
2792 { | |
2793 int vk_only = 0; | |
2794 LCID lcid; | |
2795 int virtual_key; | |
2796 | |
2797 switch (i) | |
2798 { | |
2799 case KEYCHAR_UNDERLYING_VIRTUAL_KEY_CURRENT_LANGENV: | |
2800 vk_only = 1; | |
2801 case KEYCHAR_CURRENT_LANGENV: | |
2802 lcid = mswindows_current_locale (); | |
2803 break; | |
2804 | |
2805 case KEYCHAR_UNDERLYING_VIRTUAL_KEY_DEFAULT_USER: | |
2806 vk_only = 1; | |
2807 case KEYCHAR_DEFAULT_USER: | |
2808 lcid = GetUserDefaultLCID (); | |
2809 break; | |
2810 | |
2811 case KEYCHAR_UNDERLYING_VIRTUAL_KEY_DEFAULT_SYSTEM: | |
2812 vk_only = 1; | |
2813 case KEYCHAR_DEFAULT_SYSTEM: | |
2814 lcid = GetSystemDefaultLCID (); | |
2815 break; | |
2816 | |
2817 case KEYCHAR_QWERTY: | |
2818 lcid = MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US); | |
2819 break; | |
2820 | |
2500 | 2821 default: ABORT (); lcid = 0; |
771 | 2822 } |
2823 | |
2824 /* VERY CONFUSING! See intl-win32.c. */ | |
2825 lcid = lcid & 0xFFFF; | |
2826 | |
800 | 2827 virtual_key = qxeMapVirtualKeyEx (scan, 1, (HKL) lcid); |
771 | 2828 if (!vk_only) |
2829 { | |
2830 if (XEUNICODE_P) | |
2831 { | |
2832 Extbyte received_keys[32]; | |
2833 int tounret = | |
2834 ToUnicodeEx | |
2835 (virtual_key, scan, keymap_trans, | |
2836 (LPWSTR) received_keys, | |
2837 sizeof (received_keys) / XETCHAR_SIZE, | |
2838 0, /* #### what about this flag? "if | |
2839 bit 0 is set, a menu is | |
2840 active???" */ | |
2841 (HKL) lcid); | |
2842 if (tounret > 0) | |
2843 { | |
867 | 2844 Ibyte *intchar; |
771 | 2845 |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2846 intchar = |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2847 SIZED_EXTERNAL_TO_ITEXT |
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2848 (received_keys + (tounret - 1) * 2, 2, |
771 | 2849 Qmswindows_unicode); |
1204 | 2850 XSET_EVENT_KEY_ALT_KEYCHARS |
2851 (lastev, i, itext_ichar (intchar)); | |
771 | 2852 } |
2853 } | |
2854 else | |
2855 { | |
2856 WORD received_keys[32]; | |
2857 int tounret = | |
2858 ToAsciiEx (virtual_key, scan, keymap_trans, | |
2859 received_keys, | |
2860 0, /* #### what about this | |
2861 flag? "if bit 0 is set, a | |
2862 menu is active???" */ | |
2863 (HKL) lcid); | |
2864 if (tounret > 0) | |
2865 { | |
2866 /* #### I cannot find proper | |
2867 documentation on what format the | |
2868 return value is in. I'm assuming | |
2869 it's like WM_IME_CHAR: DBCS chars | |
2870 have the lead byte in bits 8-15 of | |
2871 the short. */ | |
867 | 2872 Ibyte *intchar; |
771 | 2873 Extbyte mbstuff[2]; |
2874 Bytecount mblength = 0; | |
2875 WORD thechar = received_keys[tounret - 1]; | |
2876 | |
2877 mbstuff[mblength++] = | |
2878 (Extbyte) (thechar & 0xFF); | |
2879 if (thechar > 0xFF) | |
2880 mbstuff[mblength++] = | |
2881 (Extbyte) ((thechar >> 8) & 0xFF); | |
2882 | |
2883 intchar = convert_multibyte_to_internal_malloc | |
2884 (mbstuff, mblength, | |
2885 mswindows_locale_to_code_page (lcid), | |
2886 NULL); | |
2887 | |
1204 | 2888 XSET_EVENT_KEY_ALT_KEYCHARS |
2889 (lastev, i, itext_ichar (intchar)); | |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
2890 xfree (intchar); |
771 | 2891 } |
2892 } | |
2893 } | |
2894 else | |
2895 { | |
867 | 2896 Ichar altch; |
771 | 2897 |
2898 if (virtual_key >= 'A' && virtual_key <= 'Z') | |
2899 altch = | |
2900 virtual_key + (mods_with_shift & XEMACS_MOD_SHIFT ? | |
2901 'a' - 'A' : 0); | |
2902 else | |
2903 altch = 0; | |
2904 | |
1204 | 2905 XSET_EVENT_KEY_ALT_KEYCHARS (lastev, i, altch); |
771 | 2906 } |
2907 } | |
2908 } | |
2909 | |
442 | 2910 /* This generates WM_SYSCHAR messages, which are interpreted |
2911 by DefWindowProc as the menu selections. */ | |
2912 if (got_accelerator) | |
2913 { | |
2914 SetKeyboardState (keymap_sticky); | |
2915 TranslateMessage (&msg); | |
2916 SetKeyboardState (keymap_orig); | |
771 | 2917 unbind_to (count); |
442 | 2918 goto defproc; |
2919 } | |
2920 | |
2921 SetKeyboardState (keymap_orig); | |
2922 } /* else */ | |
771 | 2923 |
2924 if (key_needs_default_processing_p (wParam)) | |
2925 { | |
2926 unbind_to (count); | |
2927 goto defproc; | |
2928 } | |
2929 else | |
2930 { | |
2931 unbind_to (count); | |
2932 break; | |
2933 } | |
428 | 2934 } |
442 | 2935 |
2936 case WM_MBUTTONDOWN: | |
2937 case WM_MBUTTONUP: | |
2938 /* Real middle mouse button has nothing to do with emulated one: | |
2939 if one wants to exercise fingers playing chords on the mouse, | |
2940 he is allowed to do that! */ | |
2941 mswindows_enqueue_mouse_button_event (hwnd, message_, | |
2367 | 2942 XE_MAKEPOINTS (lParam), |
442 | 2943 wParam &~ MK_MBUTTON, |
2944 GetMessageTime()); | |
2945 break; | |
2946 | |
2947 case WM_LBUTTONUP: | |
771 | 2948 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
2949 msframe->last_click_time = GetMessageTime(); | |
442 | 2950 |
2951 KillTimer (hwnd, BUTTON_2_TIMER_ID); | |
2952 msframe->button2_need_lbutton = 0; | |
2953 if (msframe->ignore_next_lbutton_up) | |
2954 { | |
2955 msframe->ignore_next_lbutton_up = 0; | |
2956 } | |
2957 else if (msframe->button2_is_down) | |
2958 { | |
2959 msframe->button2_is_down = 0; | |
2960 msframe->ignore_next_rbutton_up = 1; | |
2961 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, | |
2367 | 2962 XE_MAKEPOINTS (lParam), |
442 | 2963 wParam |
2964 &~ (MK_LBUTTON | MK_MBUTTON | |
2965 | MK_RBUTTON), | |
2966 GetMessageTime()); | |
2967 } | |
2968 else | |
2969 { | |
2970 if (msframe->button2_need_rbutton) | |
2971 { | |
2972 msframe->button2_need_rbutton = 0; | |
2973 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, | |
2367 | 2974 XE_MAKEPOINTS (lParam), |
442 | 2975 wParam &~ MK_LBUTTON, |
2976 GetMessageTime()); | |
2977 } | |
2978 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP, | |
2367 | 2979 XE_MAKEPOINTS (lParam), |
442 | 2980 wParam &~ MK_LBUTTON, |
2981 GetMessageTime()); | |
2982 } | |
2983 break; | |
2984 | |
2985 case WM_RBUTTONUP: | |
771 | 2986 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
2987 msframe->last_click_time = GetMessageTime(); | |
442 | 2988 |
2989 KillTimer (hwnd, BUTTON_2_TIMER_ID); | |
2990 msframe->button2_need_rbutton = 0; | |
2991 if (msframe->ignore_next_rbutton_up) | |
2992 { | |
2993 msframe->ignore_next_rbutton_up = 0; | |
2994 } | |
2995 else if (msframe->button2_is_down) | |
2996 { | |
2997 msframe->button2_is_down = 0; | |
2998 msframe->ignore_next_lbutton_up = 1; | |
2999 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, | |
2367 | 3000 XE_MAKEPOINTS (lParam), |
442 | 3001 wParam |
3002 &~ (MK_LBUTTON | MK_MBUTTON | |
3003 | MK_RBUTTON), | |
3004 GetMessageTime()); | |
3005 } | |
3006 else | |
3007 { | |
3008 if (msframe->button2_need_lbutton) | |
3009 { | |
3010 msframe->button2_need_lbutton = 0; | |
3011 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, | |
2367 | 3012 XE_MAKEPOINTS (lParam), |
442 | 3013 wParam &~ MK_RBUTTON, |
3014 GetMessageTime()); | |
3015 } | |
3016 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP, | |
2367 | 3017 XE_MAKEPOINTS (lParam), |
442 | 3018 wParam &~ MK_RBUTTON, |
3019 GetMessageTime()); | |
3020 } | |
3021 break; | |
3022 | |
3023 case WM_LBUTTONDOWN: | |
771 | 3024 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3025 |
3026 if (msframe->button2_need_lbutton) | |
428 | 3027 { |
3028 KillTimer (hwnd, BUTTON_2_TIMER_ID); | |
442 | 3029 msframe->button2_need_lbutton = 0; |
3030 msframe->button2_need_rbutton = 0; | |
3031 if (mswindows_button2_near_enough (msframe->last_click_point, | |
2367 | 3032 XE_MAKEPOINTS (lParam))) |
428 | 3033 { |
442 | 3034 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, |
2367 | 3035 XE_MAKEPOINTS (lParam), |
442 | 3036 wParam |
3037 &~ (MK_LBUTTON | MK_MBUTTON | |
3038 | MK_RBUTTON), | |
3039 GetMessageTime()); | |
3040 msframe->button2_is_down = 1; | |
428 | 3041 } |
442 | 3042 else |
428 | 3043 { |
442 | 3044 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, |
3045 msframe->last_click_point, | |
3046 msframe->last_click_mods | |
3047 &~ MK_RBUTTON, | |
3048 msframe->last_click_time); | |
3049 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, | |
2367 | 3050 XE_MAKEPOINTS (lParam), |
442 | 3051 wParam &~ MK_LBUTTON, |
3052 GetMessageTime()); | |
428 | 3053 } |
3054 } | |
3055 else | |
442 | 3056 { |
3057 mswindows_set_chord_timer (hwnd); | |
3058 msframe->button2_need_rbutton = 1; | |
2367 | 3059 msframe->last_click_point = XE_MAKEPOINTS (lParam); |
442 | 3060 msframe->last_click_mods = wParam; |
3061 } | |
771 | 3062 msframe->last_click_time = GetMessageTime(); |
442 | 3063 break; |
3064 | |
3065 case WM_RBUTTONDOWN: | |
771 | 3066 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3067 |
3068 if (msframe->button2_need_rbutton) | |
428 | 3069 { |
442 | 3070 KillTimer (hwnd, BUTTON_2_TIMER_ID); |
3071 msframe->button2_need_lbutton = 0; | |
3072 msframe->button2_need_rbutton = 0; | |
3073 if (mswindows_button2_near_enough (msframe->last_click_point, | |
2367 | 3074 XE_MAKEPOINTS (lParam))) |
442 | 3075 { |
3076 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, | |
2367 | 3077 XE_MAKEPOINTS (lParam), |
442 | 3078 wParam |
3079 &~ (MK_LBUTTON | MK_MBUTTON | |
3080 | MK_RBUTTON), | |
3081 GetMessageTime()); | |
3082 msframe->button2_is_down = 1; | |
3083 } | |
3084 else | |
3085 { | |
3086 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, | |
3087 msframe->last_click_point, | |
3088 msframe->last_click_mods | |
3089 &~ MK_LBUTTON, | |
3090 msframe->last_click_time); | |
3091 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, | |
2367 | 3092 XE_MAKEPOINTS (lParam), |
442 | 3093 wParam &~ MK_RBUTTON, |
3094 GetMessageTime()); | |
3095 } | |
428 | 3096 } |
3097 else | |
3098 { | |
442 | 3099 mswindows_set_chord_timer (hwnd); |
3100 msframe->button2_need_lbutton = 1; | |
2367 | 3101 msframe->last_click_point = XE_MAKEPOINTS (lParam); |
442 | 3102 msframe->last_click_mods = wParam; |
3103 } | |
771 | 3104 msframe->last_click_time = GetMessageTime(); |
442 | 3105 break; |
3106 | |
3107 case WM_TIMER: | |
3108 if (wParam == BUTTON_2_TIMER_ID) | |
3109 { | |
771 | 3110 msframe = |
3111 FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); | |
442 | 3112 KillTimer (hwnd, BUTTON_2_TIMER_ID); |
3113 | |
3114 if (msframe->button2_need_lbutton) | |
3115 { | |
3116 msframe->button2_need_lbutton = 0; | |
3117 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, | |
3118 msframe->last_click_point, | |
3119 msframe->last_click_mods | |
3120 &~ MK_RBUTTON, | |
3121 msframe->last_click_time); | |
3122 } | |
3123 else if (msframe->button2_need_rbutton) | |
3124 { | |
3125 msframe->button2_need_rbutton = 0; | |
3126 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, | |
3127 msframe->last_click_point, | |
3128 msframe->last_click_mods | |
3129 &~ MK_LBUTTON, | |
3130 msframe->last_click_time); | |
3131 } | |
3132 } | |
3133 else | |
3134 assert ("Spurious timer fired" == 0); | |
3135 break; | |
3136 | |
3137 case WM_MOUSEMOVE: | |
3138 /* Optimization: don't report mouse movement while size is changing */ | |
771 | 3139 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3140 if (!msframe->sizing) |
3141 { | |
3142 /* When waiting for the second mouse button to finish | |
3143 button2 emulation, and have moved too far, just pretend | |
3144 as if timer has expired. This improves drag-select feedback */ | |
3145 if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton) | |
3146 && !mswindows_button2_near_enough (msframe->last_click_point, | |
2367 | 3147 XE_MAKEPOINTS (lParam))) |
428 | 3148 { |
442 | 3149 KillTimer (hwnd, BUTTON_2_TIMER_ID); |
771 | 3150 qxeSendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0); |
442 | 3151 } |
3152 | |
3153 emacs_event = Fmake_event (Qnil, Qnil); | |
3154 event = XEVENT(emacs_event); | |
3155 | |
964 | 3156 XSET_EVENT_CHANNEL (emacs_event, mswindows_find_frame(hwnd)); |
3157 XSET_EVENT_TIMESTAMP (emacs_event, GetMessageTime()); | |
3158 XSET_EVENT_TYPE (emacs_event, pointer_motion_event); | |
2367 | 3159 XSET_EVENT_MOTION_X (emacs_event, XE_MAKEPOINTS (lParam).x); |
3160 XSET_EVENT_MOTION_Y (emacs_event, XE_MAKEPOINTS (lParam).y); | |
1204 | 3161 XSET_EVENT_MOTION_MODIFIERS (emacs_event, |
964 | 3162 mswindows_modifier_state (NULL, wParam, 0)); |
442 | 3163 |
3164 mswindows_enqueue_dispatch_event (emacs_event); | |
3165 } | |
3166 break; | |
3167 | |
3168 case WM_CANCELMODE: | |
3169 ReleaseCapture (); | |
3170 /* Queue a `cancel-mode-internal' misc user event, so mouse | |
3171 selection would be canceled if any */ | |
3172 mswindows_enqueue_misc_user_event (mswindows_find_frame (hwnd), | |
3173 Qcancel_mode_internal, Qnil); | |
3174 break; | |
3175 | |
3176 case WM_NOTIFY: | |
3177 { | |
647 | 3178 LPNMHDR nmhdr = (LPNMHDR) lParam; |
3179 | |
1111 | 3180 if (nmhdr->code == TTN_NEEDTEXT) |
442 | 3181 { |
3182 #ifdef HAVE_TOOLBARS | |
771 | 3183 LPTOOLTIPTEXTW tttextw = (LPTOOLTIPTEXTW) lParam; |
442 | 3184 Lisp_Object btext; |
771 | 3185 Extbyte *btextext = 0; |
442 | 3186 |
3187 /* find out which toolbar */ | |
3188 frame = XFRAME (mswindows_find_frame (hwnd)); | |
647 | 3189 btext = mswindows_get_toolbar_button_text (frame, nmhdr->idFrom); |
442 | 3190 |
771 | 3191 tttextw->hinst = NULL; |
3192 | |
3193 if (!NILP (btext)) | |
4981
4aebb0131297
Cleanups/renaming of EXTERNAL_TO_C_STRING and friends
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
3194 btextext = LISP_STRING_TO_TSTR (btext); |
771 | 3195 |
3196 if (btextext) | |
442 | 3197 { |
771 | 3198 /* WARNING: We can't just write a '\0' into the 79th |
3199 "character" because tttextw->szText is in WCHAR's but we | |
3200 may be copying an ANSI string into it. Easiest to just | |
3201 zero the whole thing. */ | |
3202 xzero (*tttextw->szText); | |
2421 | 3203 qxetcsncpy ((Extbyte *) tttextw->szText, btextext, 79); |
442 | 3204 } |
771 | 3205 else |
3206 tttextw->lpszText = NULL; | |
442 | 3207 #endif |
3208 } | |
3209 /* handle tree view callbacks */ | |
1111 | 3210 else if (nmhdr->code == TVN_SELCHANGED) |
442 | 3211 { |
647 | 3212 NM_TREEVIEW *ptree = (NM_TREEVIEW *) lParam; |
442 | 3213 frame = XFRAME (mswindows_find_frame (hwnd)); |
3214 mswindows_handle_gui_wm_command (frame, 0, ptree->itemNew.lParam); | |
3215 } | |
3216 /* handle tab control callbacks */ | |
1111 | 3217 else if (nmhdr->code == TCN_SELCHANGE) |
442 | 3218 { |
3219 TC_ITEM item; | |
771 | 3220 int idx = qxeSendMessage (nmhdr->hwndFrom, TCM_GETCURSEL, 0, 0); |
442 | 3221 frame = XFRAME (mswindows_find_frame (hwnd)); |
3222 | |
3223 item.mask = TCIF_PARAM; | |
771 | 3224 qxeSendMessage (nmhdr->hwndFrom, TCM_GETITEM, (WPARAM) idx, |
3225 (LPARAM) &item); | |
442 | 3226 |
3227 mswindows_handle_gui_wm_command (frame, 0, item.lParam); | |
3228 } | |
3229 } | |
3230 break; | |
3231 | |
3232 case WM_PAINT: | |
3233 /* hdc will be NULL unless this is a subwindow - in which case we | |
3234 shouldn't have received a paint message for it here. */ | |
3235 assert (wParam == 0); | |
3236 | |
3237 /* Can't queue a magic event because windows goes modal and sends paint | |
3238 messages directly to the windows procedure when doing solid drags | |
3239 and the message queue doesn't get processed. */ | |
3240 mswindows_handle_paint (XFRAME (mswindows_find_frame (hwnd))); | |
3241 break; | |
3242 | |
903 | 3243 case WM_ACTIVATE: |
3244 { | |
3245 /* | |
3246 * If we receive a WM_ACTIVATE message that indicates that our frame | |
3247 * is being activated, make sure that the frame is marked visible | |
3248 * if the window itself is visible. This seems to fix the problem | |
3249 * where XEmacs appears to lock-up after switching desktops with | |
3250 * some virtual window managers. | |
3251 */ | |
3252 int state = (int)(short) LOWORD(wParam); | |
3253 #ifdef DEBUG_XEMACS | |
3254 if (debug_mswindows_events) | |
3255 stderr_out("state = %d\n", state); | |
3256 #endif /* DEBUG_XEMACS */ | |
3257 if (state == WA_ACTIVE || state == WA_CLICKACTIVE) | |
3258 { | |
3259 #ifdef DEBUG_XEMACS | |
3260 if (debug_mswindows_events) | |
3261 stderr_out(" activating\n"); | |
3262 #endif /* DEBUG_XEMACS */ | |
3263 | |
3264 fobj = mswindows_find_frame (hwnd); | |
3265 frame = XFRAME (fobj); | |
3266 if (IsWindowVisible (hwnd)) | |
3267 { | |
3268 #ifdef DEBUG_XEMACS | |
3269 if (debug_mswindows_events) | |
3270 stderr_out(" window is visible\n"); | |
3271 #endif /* DEBUG_XEMACS */ | |
3272 if (!FRAME_VISIBLE_P (frame)) | |
3273 { | |
3274 #ifdef DEBUG_XEMACS | |
3275 if (debug_mswindows_events) | |
3276 stderr_out(" frame is not visible\n"); | |
3277 #endif /* DEBUG_XEMACS */ | |
3278 /* | |
3279 * It seems that we have to enqueue the XM_MAPFRAME event | |
3280 * prior to setting the frame visible so that | |
3281 * suspend-or-iconify-emacs works properly. | |
3282 */ | |
3283 mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME); | |
3284 FRAME_VISIBLE_P (frame) = 1; | |
3285 FRAME_ICONIFIED_P (frame) = 0; | |
3286 } | |
3287 #ifdef DEBUG_XEMACS | |
3288 else | |
3289 { | |
3290 if (debug_mswindows_events) | |
3291 stderr_out(" frame is visible\n"); | |
3292 } | |
3293 #endif /* DEBUG_XEMACS */ | |
3294 } | |
3295 #ifdef DEBUG_XEMACS | |
3296 else | |
3297 { | |
3298 if (debug_mswindows_events) | |
3299 stderr_out(" window is not visible\n"); | |
3300 } | |
3301 #endif /* DEBUG_XEMACS */ | |
3302 } | |
3303 return qxeDefWindowProc (hwnd, message_, wParam, lParam); | |
3304 } | |
3305 break; | |
3306 | |
593 | 3307 case WM_WINDOWPOSCHANGED: |
3308 /* This is sent before WM_SIZE; in fact, the processing of this | |
3309 by DefWindowProc() sends WM_SIZE. But WM_SIZE is not sent when | |
3310 a window is hidden (make-frame-invisible), so we need to process | |
3311 this and update the state flags. */ | |
3312 { | |
3313 fobj = mswindows_find_frame (hwnd); | |
3314 frame = XFRAME (fobj); | |
3315 if (IsIconic (hwnd)) | |
3316 { | |
3317 FRAME_VISIBLE_P (frame) = 0; | |
3318 FRAME_ICONIFIED_P (frame) = 1; | |
3319 } | |
3320 else if (IsWindowVisible (hwnd)) | |
3321 { | |
707 | 3322 /* APA: It's too early here to set the frame visible. |
3323 * Let's do this later, in WM_SIZE processing, after the | |
3324 * magic XM_MAPFRAME event has been sent (just like 21.1 | |
3325 * did). */ | |
3326 /* FRAME_VISIBLE_P (frame) = 1; */ | |
593 | 3327 FRAME_ICONIFIED_P (frame) = 0; |
3328 } | |
3329 else | |
3330 { | |
3331 FRAME_VISIBLE_P (frame) = 0; | |
3332 FRAME_ICONIFIED_P (frame) = 0; | |
3333 } | |
3334 | |
771 | 3335 goto defproc; |
593 | 3336 } |
3337 | |
731 | 3338 case WM_SHOWWINDOW: |
3339 /* | |
3340 The WM_SHOWWINDOW message is sent to a window when the window | |
3341 is about to be hidden or shown. | |
3342 APA: This message is also sent when switching to a virtual | |
3343 desktop under the virtuawin virtual window manager. | |
3344 | |
3345 */ | |
3346 { | |
3347 fobj = mswindows_find_frame (hwnd); | |
3348 frame = XFRAME (fobj); | |
3349 if (wParam == TRUE) | |
3350 { | |
3351 mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME); | |
3352 FRAME_VISIBLE_P (frame) = 1; | |
3353 } | |
3354 else | |
3355 { | |
3356 mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME); | |
3357 FRAME_VISIBLE_P (frame) = 0; | |
3358 } | |
3359 } | |
3360 break; | |
3361 | |
442 | 3362 case WM_SIZE: |
3363 /* We only care about this message if our size has really changed */ | |
771 | 3364 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || |
3365 wParam == SIZE_MINIMIZED) | |
442 | 3366 { |
3367 RECT rect; | |
3368 int columns, rows; | |
3369 | |
3370 fobj = mswindows_find_frame (hwnd); | |
3371 frame = XFRAME (fobj); | |
771 | 3372 msframe = FRAME_MSWINDOWS_DATA (frame); |
442 | 3373 |
3374 /* We cannot handle frame map and unmap hooks right in | |
3375 this routine, because these may throw. We queue | |
3376 magic events to run these hooks instead - kkm */ | |
3377 | |
771 | 3378 if (wParam == SIZE_MINIMIZED) |
442 | 3379 { |
3380 /* Iconified */ | |
3381 mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME); | |
428 | 3382 } |
3383 else | |
3384 { | |
1279 | 3385 GetClientRect (hwnd, &rect); |
3386 FRAME_PIXWIDTH (frame) = rect.right; | |
3387 FRAME_PIXHEIGHT (frame) = rect.bottom; | |
442 | 3388 |
3389 pixel_to_real_char_size (frame, rect.right, rect.bottom, | |
3390 &FRAME_MSWINDOWS_CHARWIDTH (frame), | |
3391 &FRAME_MSWINDOWS_CHARHEIGHT (frame)); | |
3392 | |
771 | 3393 pixel_to_char_size (frame, rect.right, rect.bottom, &columns, |
3394 &rows); | |
442 | 3395 change_frame_size (frame, rows, columns, 1); |
3396 | |
3397 /* If we are inside frame creation, we have to apply geometric | |
3398 properties now. */ | |
3399 if (FRAME_MSWINDOWS_TARGET_RECT (frame)) | |
3400 { | |
3401 /* Yes, we have to size again */ | |
771 | 3402 mswindows_size_frame_internal (frame, |
3403 FRAME_MSWINDOWS_TARGET_RECT | |
3404 (frame)); | |
3405 /* Reset so we do not get here again. The SetWindowPos | |
3406 * call in mswindows_size_frame_internal can cause | |
3407 * recursion here. */ | |
442 | 3408 if (FRAME_MSWINDOWS_TARGET_RECT (frame)) |
3409 { | |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
3410 xfree (FRAME_MSWINDOWS_TARGET_RECT (frame)); |
442 | 3411 FRAME_MSWINDOWS_TARGET_RECT (frame) = 0; |
3412 } | |
3413 } | |
3414 else | |
3415 { | |
903 | 3416 if (!msframe->sizing && !FRAME_VISIBLE_P (frame)) |
3417 { | |
3418 mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME); | |
3419 /* APA: Now that the magic XM_MAPFRAME event has | |
3420 * been sent we can mark the frame as visible (just | |
3421 * like 21.1 did). */ | |
3422 FRAME_VISIBLE_P (frame) = 1; | |
3423 } | |
442 | 3424 |
1279 | 3425 if (frame->init_finished && |
3426 (!msframe->sizing || mswindows_dynamic_frame_resize)) | |
442 | 3427 redisplay (); |
3428 } | |
428 | 3429 } |
3430 } | |
442 | 3431 break; |
3432 | |
3433 case WM_DISPLAYCHANGE: | |
3434 { | |
3435 struct device *d; | |
3436 DWORD message_tick = GetMessageTime (); | |
3437 | |
3438 fobj = mswindows_find_frame (hwnd); | |
3439 frame = XFRAME (fobj); | |
3440 d = XDEVICE (FRAME_DEVICE (frame)); | |
3441 | |
3442 /* Do this only once per message. XEmacs can receive this message | |
3443 through as many frames as it currently has open. Message time | |
3444 will be the same for all these messages. Despite extreme | |
3445 efficiency, the code below has about one in 4 billion | |
3446 probability that the HDC is not recreated, provided that | |
3447 XEmacs is running sufficiently longer than 52 days. */ | |
1279 | 3448 if (DEVICE_MSWINDOWS_UPDATE_TICK (d) != message_tick) |
442 | 3449 { |
1279 | 3450 DEVICE_MSWINDOWS_UPDATE_TICK (d) = message_tick; |
3451 DeleteDC (DEVICE_MSWINDOWS_HCDC (d)); | |
3452 DEVICE_MSWINDOWS_HCDC (d) = CreateCompatibleDC (NULL); | |
442 | 3453 } |
3454 } | |
3455 break; | |
3456 | |
3457 /* Misc magic events which only require that the frame be identified */ | |
3458 case WM_SETFOCUS: | |
3459 case WM_KILLFOCUS: | |
3460 mswindows_enqueue_magic_event (hwnd, message_); | |
3461 break; | |
3462 | |
3463 case WM_WINDOWPOSCHANGING: | |
428 | 3464 { |
442 | 3465 WINDOWPOS *wp = (LPWINDOWPOS) lParam; |
3466 WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) }; | |
3467 GetWindowPlacement(hwnd, &wpl); | |
3468 | |
3469 /* Only interested if size is changing and we're not being iconified */ | |
3470 if (wpl.showCmd != SW_SHOWMINIMIZED | |
3471 && wpl.showCmd != SW_SHOWMAXIMIZED | |
3472 && !(wp->flags & SWP_NOSIZE)) | |
428 | 3473 { |
442 | 3474 RECT ncsize = { 0, 0, 0, 0 }; |
3475 int pixwidth, pixheight; | |
771 | 3476 AdjustWindowRectEx (&ncsize, qxeGetWindowLong (hwnd, GWL_STYLE), |
442 | 3477 GetMenu(hwnd) != NULL, |
771 | 3478 qxeGetWindowLong (hwnd, GWL_EXSTYLE)); |
442 | 3479 |
3480 round_size_to_real_char (XFRAME (mswindows_find_frame (hwnd)), | |
3481 wp->cx - (ncsize.right - ncsize.left), | |
3482 wp->cy - (ncsize.bottom - ncsize.top), | |
3483 &pixwidth, &pixheight); | |
3484 | |
3485 /* Convert client sizes to window sizes */ | |
3486 pixwidth += (ncsize.right - ncsize.left); | |
3487 pixheight += (ncsize.bottom - ncsize.top); | |
3488 | |
3489 if (wpl.showCmd != SW_SHOWMAXIMIZED) | |
3490 { | |
3491 /* Adjust so that the bottom or right doesn't move if it's | |
3492 * the top or left that's being changed */ | |
3493 RECT rect; | |
3494 GetWindowRect (hwnd, &rect); | |
3495 | |
3496 if (rect.left != wp->x) | |
3497 wp->x += wp->cx - pixwidth; | |
3498 if (rect.top != wp->y) | |
3499 wp->y += wp->cy - pixheight; | |
3500 } | |
3501 | |
3502 wp->cx = pixwidth; | |
3503 wp->cy = pixheight; | |
428 | 3504 } |
442 | 3505 /* DefWindowProc sends useful WM_GETMINMAXINFO message, and adjusts |
3506 window position if the user tries to track window too small */ | |
428 | 3507 } |
442 | 3508 goto defproc; |
3509 | |
3510 case WM_ENTERSIZEMOVE: | |
771 | 3511 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3512 msframe->sizing = 1; |
3513 return 0; | |
3514 | |
3515 case WM_EXITSIZEMOVE: | |
771 | 3516 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); |
442 | 3517 msframe->sizing = 0; |
3518 /* Queue noop event */ | |
3519 mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE); | |
3520 return 0; | |
428 | 3521 |
3522 #ifdef HAVE_SCROLLBARS | |
442 | 3523 case WM_VSCROLL: |
3524 case WM_HSCROLL: | |
3525 { | |
3526 /* Direction of scroll is determined by scrollbar instance. */ | |
1279 | 3527 int code = (int) LOWORD (wParam); |
3528 int pos = (short int) HIWORD (wParam); | |
442 | 3529 HWND hwndScrollBar = (HWND) lParam; |
3530 struct gcpro gcpro1, gcpro2; | |
3531 | |
3532 mswindows_handle_scrollbar_event (hwndScrollBar, code, pos); | |
3533 GCPRO2 (emacs_event, fobj); | |
853 | 3534 if (UNBOUNDP (mswindows_pump_outstanding_events ())) /* Can GC */ |
442 | 3535 { |
3536 /* Error during event pumping - cancel scroll */ | |
771 | 3537 qxeSendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0); |
442 | 3538 } |
3539 UNGCPRO; | |
3540 break; | |
3541 } | |
3542 | |
3543 case WM_MOUSEWHEEL: | |
3544 { | |
3545 int keys = LOWORD (wParam); /* Modifier key flags */ | |
3546 int delta = (short) HIWORD (wParam); /* Wheel rotation amount */ | |
3547 | |
1622 | 3548 /* enqueue button4/5 events if mswindows_handle_mousewheel_event |
3549 doesn't handle the event, such as when the scrollbars are not | |
3550 displayed */ | |
3551 if (!mswindows_handle_mousewheel_event (mswindows_find_frame (hwnd), | |
464 | 3552 keys, delta, |
2367 | 3553 XE_MAKEPOINTS (lParam))) |
1622 | 3554 mswindows_enqueue_mouse_button_event (hwnd, message_, |
2367 | 3555 XE_MAKEPOINTS (lParam), |
1622 | 3556 wParam, |
3557 GetMessageTime()); | |
3558 /* We are not in a modal loop so no pumping is necessary. */ | |
3559 break; | |
442 | 3560 } |
428 | 3561 #endif |
3562 | |
3563 #ifdef HAVE_MENUBARS | |
442 | 3564 case WM_INITMENU: |
771 | 3565 if (UNBOUNDP (mswindows_handle_wm_initmenu |
3566 ((HMENU) wParam, | |
3567 XFRAME (mswindows_find_frame (hwnd))))) | |
3568 qxeSendMessage (hwnd, WM_CANCELMODE, 0, 0); | |
442 | 3569 break; |
3570 | |
3571 case WM_INITMENUPOPUP: | |
3572 if (!HIWORD(lParam)) | |
3573 { | |
771 | 3574 if (UNBOUNDP (mswindows_handle_wm_initmenupopup |
3575 ((HMENU) wParam, | |
3576 XFRAME (mswindows_find_frame (hwnd))))) | |
3577 qxeSendMessage (hwnd, WM_CANCELMODE, 0, 0); | |
442 | 3578 } |
3579 break; | |
428 | 3580 |
3581 #endif /* HAVE_MENUBARS */ | |
3582 | |
442 | 3583 case WM_COMMAND: |
3584 { | |
3585 WORD id = LOWORD (wParam); | |
3586 WORD nid = HIWORD (wParam); | |
3587 HWND cid = (HWND)lParam; | |
3588 frame = XFRAME (mswindows_find_frame (hwnd)); | |
428 | 3589 |
3590 #ifdef HAVE_TOOLBARS | |
442 | 3591 if (!NILP (mswindows_handle_toolbar_wm_command (frame, cid, id))) |
3592 break; | |
428 | 3593 #endif |
771 | 3594 /* widgets in a buffer only eval a callback for suitable events. */ |
442 | 3595 switch (nid) |
3596 { | |
3597 case BN_CLICKED: | |
3598 case EN_CHANGE: | |
3599 case CBN_EDITCHANGE: | |
3600 case CBN_SELCHANGE: | |
3601 if (!NILP (mswindows_handle_gui_wm_command (frame, cid, id))) | |
3602 return 0; | |
3603 } | |
3604 /* menubars always must come last since the hashtables do not | |
771 | 3605 always exist */ |
428 | 3606 #ifdef HAVE_MENUBARS |
442 | 3607 if (!NILP (mswindows_handle_wm_command (frame, id))) |
3608 break; | |
428 | 3609 #endif |
3610 | |
771 | 3611 goto defproc; |
3612 /* Bite me - a spurious command. This used to not be able to | |
3613 happen but with the introduction of widgets it's now | |
3614 possible. #### Andy, fix the god-damn widget code! It has | |
3615 more bugs than a termite's nest! */ | |
442 | 3616 } |
3617 break; | |
3618 | |
3619 case WM_CTLCOLORBTN: | |
3620 case WM_CTLCOLORLISTBOX: | |
3621 case WM_CTLCOLOREDIT: | |
3622 case WM_CTLCOLORSTATIC: | |
3623 case WM_CTLCOLORSCROLLBAR: | |
3624 { | |
3625 /* if we get an opportunity to paint a widget then do so if | |
3626 there is an appropriate face */ | |
771 | 3627 HWND crtlwnd = (HWND) lParam; |
3628 LONG ii = qxeGetWindowLong (crtlwnd, GWL_USERDATA); | |
442 | 3629 if (ii) |
3630 { | |
3631 Lisp_Object image_instance; | |
826 | 3632 image_instance = VOID_TO_LISP ((void *) ii); |
442 | 3633 if (IMAGE_INSTANCEP (image_instance) |
3634 && | |
3635 IMAGE_INSTANCE_TYPE_P (image_instance, IMAGE_WIDGET)) | |
3636 { | |
3637 /* set colors for the buttons */ | |
771 | 3638 HDC hdc = (HDC) wParam; |
442 | 3639 if (last_widget_brushed != ii) |
3640 { | |
3641 if (widget_brush) | |
3642 DeleteObject (widget_brush); | |
3643 widget_brush = CreateSolidBrush | |
3644 (COLOR_INSTANCE_MSWINDOWS_COLOR | |
3645 (XCOLOR_INSTANCE | |
3646 (FACE_BACKGROUND | |
3647 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), | |
3648 XIMAGE_INSTANCE_FRAME (image_instance))))); | |
3649 } | |
3650 last_widget_brushed = ii; | |
3651 SetTextColor | |
3652 (hdc, | |
3653 COLOR_INSTANCE_MSWINDOWS_COLOR | |
3654 (XCOLOR_INSTANCE | |
3655 (FACE_FOREGROUND | |
3656 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), | |
3657 XIMAGE_INSTANCE_FRAME (image_instance))))); | |
3658 SetBkMode (hdc, OPAQUE); | |
3659 SetBkColor | |
3660 (hdc, | |
3661 COLOR_INSTANCE_MSWINDOWS_COLOR | |
3662 (XCOLOR_INSTANCE | |
3663 (FACE_BACKGROUND | |
3664 (XIMAGE_INSTANCE_WIDGET_FACE (image_instance), | |
3665 XIMAGE_INSTANCE_FRAME (image_instance))))); | |
3666 return (LRESULT)widget_brush; | |
3667 } | |
3668 } | |
3669 } | |
3670 goto defproc; | |
428 | 3671 |
3672 #ifdef HAVE_DRAGNDROP | |
853 | 3673 case WM_DROPFILES: /* implementation ripped-off from event-Xt.c */ |
442 | 3674 { |
771 | 3675 UINT filecount, i; |
442 | 3676 POINT point; |
3677 | |
3678 Lisp_Object l_dndlist = Qnil, l_item = Qnil; | |
3679 struct gcpro gcpro1, gcpro2, gcpro3; | |
3680 | |
3681 emacs_event = Fmake_event (Qnil, Qnil); | |
771 | 3682 event = XEVENT (emacs_event); |
442 | 3683 |
3684 GCPRO3 (emacs_event, l_dndlist, l_item); | |
3685 | |
3686 if (!DragQueryPoint ((HDROP) wParam, &point)) | |
853 | 3687 point.x = point.y = -1; /* outside client area */ |
442 | 3688 |
964 | 3689 XSET_EVENT_TYPE (emacs_event, misc_user_event); |
3690 XSET_EVENT_CHANNEL (emacs_event, mswindows_find_frame(hwnd)); | |
3691 XSET_EVENT_TIMESTAMP (emacs_event, GetMessageTime()); | |
1204 | 3692 XSET_EVENT_MISC_USER_BUTTON (emacs_event, 1); |
3693 XSET_EVENT_MISC_USER_MODIFIERS (emacs_event, | |
964 | 3694 mswindows_modifier_state (NULL, (DWORD) -1, 0)); |
1204 | 3695 XSET_EVENT_MISC_USER_X (emacs_event, point.x); |
3696 XSET_EVENT_MISC_USER_Y (emacs_event, point.y); | |
3697 XSET_EVENT_MISC_USER_FUNCTION (emacs_event, | |
964 | 3698 Qdragdrop_drop_dispatch); |
442 | 3699 |
771 | 3700 filecount = qxeDragQueryFile ((HDROP) wParam, 0xffffffff, NULL, 0); |
3701 for (i = 0; i < filecount; i++) | |
442 | 3702 { |
867 | 3703 Ibyte *fname; |
771 | 3704 Extbyte *fname_ext; |
3705 Bytecount fnamelen; | |
3706 Charcount len = qxeDragQueryFile ((HDROP) wParam, i, NULL, 0); | |
2526 | 3707 int freeme = 0; |
442 | 3708 /* The URLs that we make here aren't correct according to section |
3709 * 3.10 of rfc1738 because they're missing the //<host>/ part and | |
3710 * because they may contain reserved characters. But that's OK - | |
3711 * they just need to be good enough to keep dragdrop.el happy. */ | |
2367 | 3712 fname_ext = alloca_extbytes ((len + 1) * XETCHAR_SIZE); |
771 | 3713 qxeDragQueryFile ((HDROP) wParam, i, fname_ext, len + 1); |
3714 | |
3715 TO_INTERNAL_FORMAT (DATA, (fname_ext, len * XETCHAR_SIZE), | |
3716 ALLOCA, (fname, fnamelen), | |
3717 Qmswindows_tstr); | |
442 | 3718 |
2526 | 3719 |
442 | 3720 /* May be a shell link aka "shortcut" - replace fname if so */ |
2367 | 3721 if (!qxestrcasecmp_ascii (fname + fnamelen - 4, ".LNK")) |
442 | 3722 { |
2526 | 3723 fname = mswindows_read_link (fname); |
3724 freeme = 1; | |
442 | 3725 } |
2526 | 3726 |
771 | 3727 { |
2526 | 3728 Ibyte *fname2 = urlify_filename (fname); |
4953
304aebb79cd3
function renamings to track names of char typedefs
Ben Wing <ben@xemacs.org>
parents:
4952
diff
changeset
|
3729 l_item = build_istring (fname2); |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
3730 xfree (fname2); |
2526 | 3731 if (freeme) |
4976
16112448d484
Rename xfree(FOO, TYPE) -> xfree(FOO)
Ben Wing <ben@xemacs.org>
parents:
4953
diff
changeset
|
3732 xfree (fname); |
771 | 3733 l_dndlist = Fcons (l_item, l_dndlist); |
3734 } | |
442 | 3735 } |
771 | 3736 |
442 | 3737 DragFinish ((HDROP) wParam); |
3738 | |
1204 | 3739 SET_EVENT_MISC_USER_OBJECT (event, |
964 | 3740 Fcons (Qdragdrop_URL, l_dndlist)); |
442 | 3741 mswindows_enqueue_dispatch_event (emacs_event); |
3742 UNGCPRO; | |
3743 } | |
3744 break; | |
771 | 3745 #endif /* HAVE_DRAGNDROP */ |
3746 | |
3747 #ifdef MULE | |
3748 case WM_IME_CHAR: | |
3749 | |
3750 case WM_IME_STARTCOMPOSITION: | |
3751 mswindows_start_ime_composition (XFRAME (mswindows_find_frame (hwnd))); | |
3752 goto defproc; | |
3753 | |
3754 case WM_IME_COMPOSITION: | |
3755 if (lParam & GCS_RESULTSTR) | |
3756 { | |
3757 HIMC imc = ImmGetContext (hwnd); | |
3758 Extbyte *result; | |
3759 Bytecount len; | |
867 | 3760 Ibyte *resultint, *endptr; |
771 | 3761 Bytecount lenint; |
3762 int speccount; | |
3763 | |
3764 if (!imc) | |
3765 break; | |
3766 | |
3767 /* See WM_KEYDOWN above. */ | |
3768 speccount = begin_dont_check_for_quit (); | |
3769 | |
3770 /* Sizes always in bytes, even for unicode. | |
3771 ImmGetCompositionStringW is supported even on Windows 9x, and | |
3772 allows us to handle multiple languages. */ | |
3773 len = ImmGetCompositionStringW (imc, GCS_RESULTSTR, NULL, 0); | |
2367 | 3774 result = alloca_extbytes (len); |
771 | 3775 ImmGetCompositionStringW (imc, GCS_RESULTSTR, (WCHAR *) result, len); |
3776 ImmReleaseContext (hwnd, imc); | |
3777 | |
3778 TO_INTERNAL_FORMAT (DATA, (result, len), | |
3779 ALLOCA, (resultint, lenint), | |
3780 Qmswindows_tstr); | |
3781 | |
3782 endptr = resultint + lenint; | |
3783 | |
3784 while (resultint < endptr) | |
3785 { | |
867 | 3786 Ichar ch = itext_ichar (resultint); |
771 | 3787 if (ch == ' ') |
3788 mswindows_enqueue_keypress_event (hwnd, QKspace, 0); | |
3789 else | |
3790 mswindows_enqueue_keypress_event (hwnd, make_char (ch), 0); | |
867 | 3791 INC_IBYTEPTR (resultint); |
771 | 3792 } |
3793 | |
3794 unbind_to (speccount); | |
3795 } | |
3796 goto defproc; | |
3797 #endif /* MULE */ | |
442 | 3798 |
3799 defproc: | |
3800 default: | |
771 | 3801 return qxeDefWindowProc (hwnd, message_, wParam, lParam); |
428 | 3802 } |
3803 return (0); | |
3804 } | |
3805 | |
3806 | |
3807 /************************************************************************/ | |
3808 /* keyboard, mouse & other helpers for the windows procedure */ | |
3809 /************************************************************************/ | |
3810 static void | |
3811 mswindows_set_chord_timer (HWND hwnd) | |
3812 { | |
3813 int interval; | |
3814 | |
3815 /* We get one third half system double click threshold */ | |
3816 if (mswindows_mouse_button_tolerance <= 0) | |
3817 interval = GetDoubleClickTime () / 3; | |
3818 else | |
3819 interval = mswindows_mouse_button_tolerance; | |
3820 | |
3821 SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0); | |
3822 } | |
3823 | |
3824 static int | |
3825 mswindows_button2_near_enough (POINTS p1, POINTS p2) | |
3826 { | |
3827 int dx, dy; | |
3828 if (mswindows_mouse_button_max_skew_x <= 0) | |
3829 dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2; | |
3830 else | |
3831 dx = mswindows_mouse_button_max_skew_x; | |
3832 | |
3833 if (mswindows_mouse_button_max_skew_y <= 0) | |
3834 dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2; | |
3835 else | |
3836 dy = mswindows_mouse_button_max_skew_y; | |
3837 | |
3838 return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy; | |
3839 } | |
3840 | |
3841 static int | |
3842 mswindows_current_layout_has_AltGr (void) | |
3843 { | |
3844 /* This simple caching mechanism saves 10% of CPU | |
3845 time when a key typed at autorepeat rate of 30 cps! */ | |
3846 static HKL last_hkl = 0; | |
3847 static int last_hkl_has_AltGr; | |
771 | 3848 HKL current_hkl = GetKeyboardLayout (0); |
3849 | |
428 | 3850 if (current_hkl != last_hkl) |
3851 { | |
647 | 3852 int c; |
428 | 3853 last_hkl_has_AltGr = 0; |
3854 /* In this loop, we query whether a character requires | |
3855 AltGr to be down to generate it. If at least such one | |
3856 found, this means that the layout does regard AltGr */ | |
647 | 3857 for (c = ' '; c <= 255 && !last_hkl_has_AltGr; ++c) |
3858 /* #### This is not really such a good check. What about under | |
3859 CJK locales? It may not matter there, though. We always | |
3860 call VkKeyScanA so that we check the locale-specific characters | |
3861 in non-Latin-1 locales, instead of just the Latin-1 characters. */ | |
3862 if (HIBYTE (VkKeyScanA ((char) c)) == 6) | |
428 | 3863 last_hkl_has_AltGr = 1; |
3864 last_hkl = current_hkl; | |
3865 } | |
3866 return last_hkl_has_AltGr; | |
3867 } | |
3868 | |
3869 | |
3870 /* Returns the state of the modifier keys in the format expected by the | |
3871 * Lisp_Event key_data, button_data and motion_data modifiers member */ | |
442 | 3872 static int |
771 | 3873 mswindows_modifier_state (BYTE *keymap, DWORD fwKeys, int has_AltGr) |
428 | 3874 { |
3875 int mods = 0; | |
442 | 3876 int keys_is_real = 0; |
3877 BYTE keymap2[256]; | |
3878 | |
3879 if (fwKeys == (DWORD) -1) | |
3880 fwKeys = mswindows_last_mouse_button_state; | |
3881 else | |
3882 { | |
3883 keys_is_real = 1; | |
3884 mswindows_last_mouse_button_state = fwKeys; | |
3885 } | |
428 | 3886 |
3887 if (keymap == NULL) | |
3888 { | |
442 | 3889 keymap = keymap2; |
428 | 3890 GetKeyboardState (keymap); |
3891 has_AltGr = mswindows_current_layout_has_AltGr (); | |
3892 } | |
3893 | |
442 | 3894 /* #### should look at fwKeys for MK_CONTROL. I don't understand how |
3895 AltGr works. */ | |
428 | 3896 if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80)) |
3897 { | |
442 | 3898 mods |= (keymap [VK_LMENU] & 0x80) ? XEMACS_MOD_META : 0; |
3899 mods |= (keymap [VK_RCONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0; | |
428 | 3900 } |
3901 else | |
3902 { | |
442 | 3903 mods |= (keymap [VK_MENU] & 0x80) ? XEMACS_MOD_META : 0; |
3904 mods |= (keymap [VK_CONTROL] & 0x80) ? XEMACS_MOD_CONTROL : 0; | |
428 | 3905 } |
3906 | |
1111 | 3907 mods |= (keys_is_real ? (int) (fwKeys & MK_SHIFT) : |
3908 (keymap [VK_SHIFT] & 0x80)) ? XEMACS_MOD_SHIFT : 0; | |
442 | 3909 mods |= fwKeys & MK_LBUTTON ? XEMACS_MOD_BUTTON1 : 0; |
3910 mods |= fwKeys & MK_MBUTTON ? XEMACS_MOD_BUTTON2 : 0; | |
3911 mods |= fwKeys & MK_RBUTTON ? XEMACS_MOD_BUTTON3 : 0; | |
428 | 3912 |
3913 return mods; | |
3914 } | |
3915 | |
3916 /* | |
3917 * Translate a mswindows virtual key to a keysym. | |
3918 * Only returns non-Qnil for keys that don't generate WM_CHAR messages | |
3919 * or whose ASCII codes (like space) xemacs doesn't like. | |
3920 */ | |
2286 | 3921 Lisp_Object mswindows_key_to_emacs_keysym (int mswindows_key, |
3922 int UNUSED (mods), int extendedp) | |
428 | 3923 { |
3924 if (extendedp) /* Keys not present on a 82 key keyboard */ | |
3925 { | |
3926 switch (mswindows_key) | |
3927 { | |
442 | 3928 case VK_CANCEL: return KEYSYM ("pause"); |
428 | 3929 case VK_RETURN: return KEYSYM ("kp-enter"); |
3930 case VK_PRIOR: return KEYSYM ("prior"); | |
3931 case VK_NEXT: return KEYSYM ("next"); | |
3932 case VK_END: return KEYSYM ("end"); | |
3933 case VK_HOME: return KEYSYM ("home"); | |
3934 case VK_LEFT: return KEYSYM ("left"); | |
3935 case VK_UP: return KEYSYM ("up"); | |
3936 case VK_RIGHT: return KEYSYM ("right"); | |
3937 case VK_DOWN: return KEYSYM ("down"); | |
3938 case VK_INSERT: return KEYSYM ("insert"); | |
3939 case VK_DELETE: return QKdelete; | |
442 | 3940 #if 0 /* FSF Emacs allows these to return configurable syms/mods */ |
3941 case VK_LWIN return KEYSYM (""); | |
3942 case VK_RWIN return KEYSYM (""); | |
3943 #endif | |
3944 case VK_APPS: return KEYSYM ("menu"); | |
428 | 3945 } |
3946 } | |
3947 else | |
3948 { | |
3949 switch (mswindows_key) | |
3950 { | |
771 | 3951 |
3952 #if 0 | |
3953 VK_LBUTTON: | |
3954 VK_RBUTTON: | |
3955 VK_CANCEL: | |
3956 VK_MBUTTON: | |
3957 VK_XBUTTON1: | |
3958 VK_XBUTTON2: | |
3959 #endif /* 0 */ | |
3960 | |
428 | 3961 case VK_BACK: return QKbackspace; |
3962 case VK_TAB: return QKtab; | |
771 | 3963 /* #### Officially 0A (and 0B too) are "reserved". */ |
428 | 3964 case '\n': return QKlinefeed; |
3965 case VK_CLEAR: return KEYSYM ("clear"); | |
3966 case VK_RETURN: return QKreturn; | |
771 | 3967 |
3968 #if 0 | |
3969 VK_SHIFT: "shift" | |
3970 VK_CONTROL: "control" | |
3971 VK_MENU: "alt" | |
3972 #endif /* 0 */ | |
3973 | |
442 | 3974 case VK_PAUSE: return KEYSYM ("pause"); |
771 | 3975 |
3976 #if 0 | |
3977 VK_CAPITAL: "caps-lock" | |
3978 VK_KANA: IME Kana mode | |
3979 VK_HANGUEL: IME Hanguel mode (maintained for compatibility; use VK_HANGUL) | |
3980 VK_HANGUL: IME Hangul mode | |
3981 VK_JUNJA: IME Junja mode | |
3982 VK_FINAL: IME final mode | |
3983 VK_HANJA: IME Hanja mode | |
3984 VK_KANJI: IME Kanji mode | |
3985 #endif /* 0 */ | |
3986 | |
428 | 3987 case VK_ESCAPE: return QKescape; |
771 | 3988 |
3989 #if 0 | |
3990 VK_CONVERT: IME convert | |
3991 VK_NONCONVERT: IME nonconvert | |
3992 VK_ACCEPT: IME accept | |
3993 VK_MODECHANGE: IME mode change request | |
3994 #endif /* 0 */ | |
3995 | |
428 | 3996 case VK_SPACE: return QKspace; |
3997 case VK_PRIOR: return KEYSYM ("kp-prior"); | |
3998 case VK_NEXT: return KEYSYM ("kp-next"); | |
3999 case VK_END: return KEYSYM ("kp-end"); | |
4000 case VK_HOME: return KEYSYM ("kp-home"); | |
4001 case VK_LEFT: return KEYSYM ("kp-left"); | |
4002 case VK_UP: return KEYSYM ("kp-up"); | |
4003 case VK_RIGHT: return KEYSYM ("kp-right"); | |
4004 case VK_DOWN: return KEYSYM ("kp-down"); | |
4005 case VK_SELECT: return KEYSYM ("select"); | |
4006 case VK_PRINT: return KEYSYM ("print"); | |
4007 case VK_EXECUTE: return KEYSYM ("execute"); | |
4008 case VK_SNAPSHOT: return KEYSYM ("print"); | |
4009 case VK_INSERT: return KEYSYM ("kp-insert"); | |
4010 case VK_DELETE: return KEYSYM ("kp-delete"); | |
4011 case VK_HELP: return KEYSYM ("help"); | |
771 | 4012 #if 0 |
4013 '0' through '9': numeric keys | |
4014 'A' through 'Z': alphabetic keys | |
4015 VK_LWIN: "lwin" | |
4016 VK_RWIN: "rwin" | |
4017 VK_APPS: "apps" | |
4018 VK_SLEEP: "sleep" | |
4019 #endif /* 0 */ | |
428 | 4020 case VK_NUMPAD0: return KEYSYM ("kp-0"); |
4021 case VK_NUMPAD1: return KEYSYM ("kp-1"); | |
4022 case VK_NUMPAD2: return KEYSYM ("kp-2"); | |
4023 case VK_NUMPAD3: return KEYSYM ("kp-3"); | |
4024 case VK_NUMPAD4: return KEYSYM ("kp-4"); | |
4025 case VK_NUMPAD5: return KEYSYM ("kp-5"); | |
4026 case VK_NUMPAD6: return KEYSYM ("kp-6"); | |
4027 case VK_NUMPAD7: return KEYSYM ("kp-7"); | |
4028 case VK_NUMPAD8: return KEYSYM ("kp-8"); | |
4029 case VK_NUMPAD9: return KEYSYM ("kp-9"); | |
4030 case VK_MULTIPLY: return KEYSYM ("kp-multiply"); | |
4031 case VK_ADD: return KEYSYM ("kp-add"); | |
4032 case VK_SEPARATOR: return KEYSYM ("kp-separator"); | |
4033 case VK_SUBTRACT: return KEYSYM ("kp-subtract"); | |
4034 case VK_DECIMAL: return KEYSYM ("kp-decimal"); | |
4035 case VK_DIVIDE: return KEYSYM ("kp-divide"); | |
4036 case VK_F1: return KEYSYM ("f1"); | |
4037 case VK_F2: return KEYSYM ("f2"); | |
4038 case VK_F3: return KEYSYM ("f3"); | |
4039 case VK_F4: return KEYSYM ("f4"); | |
4040 case VK_F5: return KEYSYM ("f5"); | |
4041 case VK_F6: return KEYSYM ("f6"); | |
4042 case VK_F7: return KEYSYM ("f7"); | |
4043 case VK_F8: return KEYSYM ("f8"); | |
4044 case VK_F9: return KEYSYM ("f9"); | |
4045 case VK_F10: return KEYSYM ("f10"); | |
4046 case VK_F11: return KEYSYM ("f11"); | |
4047 case VK_F12: return KEYSYM ("f12"); | |
4048 case VK_F13: return KEYSYM ("f13"); | |
4049 case VK_F14: return KEYSYM ("f14"); | |
4050 case VK_F15: return KEYSYM ("f15"); | |
4051 case VK_F16: return KEYSYM ("f16"); | |
4052 case VK_F17: return KEYSYM ("f17"); | |
4053 case VK_F18: return KEYSYM ("f18"); | |
4054 case VK_F19: return KEYSYM ("f19"); | |
4055 case VK_F20: return KEYSYM ("f20"); | |
4056 case VK_F21: return KEYSYM ("f21"); | |
4057 case VK_F22: return KEYSYM ("f22"); | |
4058 case VK_F23: return KEYSYM ("f23"); | |
4059 case VK_F24: return KEYSYM ("f24"); | |
771 | 4060 |
4061 #if 0 | |
4062 VK_NUMLOCK: 90 NUM LOCK key | |
4063 VK_SCROLL: 91 SCROLL LOCK key | |
4064 92~96 OEM specific; | |
4065 VK_LSHIFT: | |
4066 VK_RSHIFT: | |
4067 VK_LCONTROL: | |
4068 VK_RCONTROL: | |
4069 VK_LMENU: | |
4070 VK_RMENU: | |
4071 | |
4072 #ifdef VK_BROWSER_BACK /* Windows 2000 only */ | |
4073 VK_BROWSER_BACK: Browser Back key | |
4074 VK_BROWSER_FORWARD: Browser Forward key | |
4075 VK_BROWSER_REFRESH: Browser Refresh key | |
4076 VK_BROWSER_STOP: Browser Stop key | |
4077 VK_BROWSER_SEARCH: Browser Search key | |
4078 VK_BROWSER_FAVORITES: Browser Favorites key | |
4079 VK_BROWSER_HOME: Browser Start and Home key | |
4080 VK_VOLUME_MUTE: Volume Mute key | |
4081 VK_VOLUME_DOWN: Volume Down key | |
4082 VK_VOLUME_UP: Volume Up key | |
4083 VK_MEDIA_NEXT_TRACK: Next Track key | |
4084 VK_MEDIA_PREV_TRACK: Previous Track key | |
4085 VK_MEDIA_STOP: Stop Media key | |
4086 VK_MEDIA_PLAY_PAUSE: Play/Pause Media key | |
4087 VK_LAUNCH_MAIL: Start Mail key | |
4088 VK_LAUNCH_MEDIA_SELECT: Select Media key | |
4089 VK_LAUNCH_APP1: Start Application 1 key | |
4090 VK_LAUNCH_APP2: Start Application 2 key | |
4091 B8-B9 Reserved; | |
4092 VK_OEM_1: For the US standard keyboard, the ';:' key | |
4093 VK_OEM_PLUS: For any country/region, the '+' key | |
4094 VK_OEM_COMMA: For any country/region, the ',' key | |
4095 VK_OEM_MINUS: For any country/region, the '-' key | |
4096 VK_OEM_PERIOD: For any country/region, the '.' key | |
4097 VK_OEM_2: For the US standard keyboard, the '/?' key | |
4098 VK_OEM_3: For the US standard keyboard, the '`~' key | |
4099 C1~D7 Reserved; | |
4100 D8~DA Unassigned; | |
4101 VK_OEM_4: For the US standard keyboard, the '[{' key | |
4102 VK_OEM_5: For the US standard keyboard, the '\|' key | |
4103 VK_OEM_6: For the US standard keyboard, the ']}' key | |
4104 VK_OEM_7: For the US standard keyboard, the 'single-quote/double-quote' key | |
4105 VK_OEM_8: | |
4106 E0 Reserved; | |
4107 E1 OEM specific; | |
4108 VK_OEM_102: Either the angle bracket key or the backslash key on the RT 102-key keyboard | |
4109 E3~E4 OEM specific; | |
4110 #endif /* VK_BROWSER_BACK */ | |
4111 VK_PROCESSKEY: E5 Windows 95/98, Windows NT 4.0, Windows 2000: IME PROCESS key | |
4112 E6 OEM specific; | |
4113 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 | |
4114 E8 Unassigned; | |
4115 E9~F5 OEM specific; | |
4116 VK_ATTN: Attn key | |
4117 VK_CRSEL: CrSel key | |
4118 VK_EXSEL: ExSel key | |
4119 VK_EREOF: Erase EOF key | |
4120 VK_PLAY: Play key | |
4121 VK_ZOOM: Zoom key | |
4122 VK_NONAME: Reserved for future use | |
4123 VK_PA1: PA1 key | |
4124 VK_OEM_CLEAR: Clear key | |
4125 #endif /* 0 */ | |
4126 | |
428 | 4127 } |
4128 } | |
4129 return Qnil; | |
4130 } | |
4131 | |
4132 /* | |
4133 * Find the console that matches the supplied mswindows window handle | |
4134 */ | |
4135 Lisp_Object | |
2286 | 4136 mswindows_find_console (HWND UNUSED (hwnd)) |
428 | 4137 { |
4138 /* We only support one console */ | |
4139 return XCAR (Vconsole_list); | |
4140 } | |
4141 | |
4142 /* | |
4143 * Find the frame that matches the supplied mswindows window handle | |
4144 */ | |
546 | 4145 Lisp_Object |
428 | 4146 mswindows_find_frame (HWND hwnd) |
4147 { | |
771 | 4148 LONG l = qxeGetWindowLong (hwnd, XWL_FRAMEOBJ); |
428 | 4149 Lisp_Object f; |
4150 if (l == 0) | |
4151 { | |
4152 /* We are in progress of frame creation. Return the frame | |
4153 being created, as it still not remembered in the window | |
4154 extra storage. */ | |
4155 assert (!NILP (Vmswindows_frame_being_created)); | |
4156 return Vmswindows_frame_being_created; | |
4157 } | |
826 | 4158 f = VOID_TO_LISP ((void *) l); |
428 | 4159 return f; |
4160 } | |
4161 | |
4162 | |
4163 /************************************************************************/ | |
4164 /* methods */ | |
4165 /************************************************************************/ | |
4166 | |
4167 static int | |
4168 emacs_mswindows_add_timeout (EMACS_TIME thyme) | |
4169 { | |
4170 int milliseconds; | |
4171 EMACS_TIME current_time; | |
4172 EMACS_GET_TIME (current_time); | |
4173 EMACS_SUB_TIME (thyme, thyme, current_time); | |
4174 milliseconds = EMACS_SECS (thyme) * 1000 + | |
4175 (EMACS_USECS (thyme) + 500) / 1000; | |
4176 if (milliseconds < 1) | |
4177 milliseconds = 1; | |
4178 ++mswindows_pending_timers_count; | |
4179 return SetTimer (NULL, 0, milliseconds, | |
4180 (TIMERPROC) mswindows_wm_timer_callback); | |
4181 } | |
4182 | |
1204 | 4183 static int |
4184 remove_timeout_mapper (Lisp_Object ev, void *data) | |
4185 { | |
4186 if (XEVENT_TYPE (ev) == timeout_event) | |
4187 { | |
4188 if ((int) data == XEVENT_TIMEOUT_INTERVAL_ID (ev)) | |
4189 return 1; | |
4190 } | |
4191 | |
4192 return 0; | |
4193 } | |
4194 | |
428 | 4195 static void |
4196 emacs_mswindows_remove_timeout (int id) | |
4197 { | |
4198 if (KillTimer (NULL, id)) | |
4199 --mswindows_pending_timers_count; | |
4200 | |
4201 /* If there is a dispatch event generated by this | |
4202 timeout in the queue, we have to remove it too. */ | |
1204 | 4203 map_event_chain_remove (remove_timeout_mapper, |
4204 &mswindows_s_dispatch_event_queue, | |
4205 &mswindows_s_dispatch_event_queue_tail, | |
4206 (void *) id, MECR_DEALLOCATE_EVENT); | |
428 | 4207 } |
4208 | |
4209 /* If `user_p' is false, then return whether there are any win32, timeout, | |
4210 * or subprocess events pending (that is, whether | |
4211 * emacs_mswindows_next_event() would return immediately without blocking). | |
4212 * | |
4213 * if `user_p' is true, then return whether there are any *user generated* | |
4214 * events available (that is, whether there are keyboard or mouse-click | |
4215 * events ready to be read). This also implies that | |
4216 * emacs_mswindows_next_event() would not block. | |
4217 */ | |
4218 static int | |
1268 | 4219 emacs_mswindows_event_pending_p (int how_many) |
428 | 4220 { |
1318 | 4221 /* This can call Lisp */ |
1268 | 4222 if (!how_many) |
4223 { | |
4224 mswindows_need_event (0); | |
4225 return (!NILP (dispatch_event_queue) | |
4226 || !NILP (mswindows_s_dispatch_event_queue)); | |
4227 } | |
4228 else | |
4229 { | |
4230 Lisp_Object event; | |
4231 int count = 0; | |
4232 | |
4233 EVENT_CHAIN_LOOP (event, dispatch_event_queue) | |
4234 count++; | |
4235 | |
4236 if (count >= how_many) | |
4237 return 1; | |
4238 | |
4239 emacs_mswindows_drain_queue (); | |
4240 | |
4241 EVENT_CHAIN_LOOP (event, dispatch_event_queue) | |
4242 count++; | |
4243 | |
4244 return count >= how_many; | |
4245 } | |
428 | 4246 } |
4247 | |
4248 /* | |
4249 * Return the next event | |
4250 */ | |
4251 static void | |
440 | 4252 emacs_mswindows_next_event (Lisp_Event *emacs_event) |
428 | 4253 { |
4254 Lisp_Object event, event2; | |
4255 | |
4256 mswindows_need_event (1); | |
4257 | |
4258 event = mswindows_dequeue_dispatch_event (); | |
793 | 4259 event2 = wrap_event (emacs_event); |
428 | 4260 Fcopy_event (event, event2); |
4261 Fdeallocate_event (event); | |
4262 } | |
4263 | |
788 | 4264 static void |
4265 emacs_mswindows_format_magic_event (Lisp_Event *emacs_event, | |
4266 Lisp_Object pstream) | |
4267 { | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
4268 #define FROB(msg) case msg: write_ascstring (pstream, "type=" #msg); break |
788 | 4269 |
1204 | 4270 switch (EVENT_MAGIC_MSWINDOWS_EVENT (emacs_event)) |
788 | 4271 { |
4272 FROB (XM_BUMPQUEUE); | |
4273 FROB (WM_PAINT); | |
4274 FROB (WM_SETFOCUS); | |
4275 FROB (WM_KILLFOCUS); | |
4276 FROB (XM_MAPFRAME); | |
4277 FROB (XM_UNMAPFRAME); | |
4278 | |
2500 | 4279 default: ABORT (); |
788 | 4280 } |
4281 #undef FROB | |
4282 | |
4283 if (!NILP (EVENT_CHANNEL (emacs_event))) | |
4284 { | |
4952
19a72041c5ed
Mule-izing, various fixes related to char * arguments
Ben Wing <ben@xemacs.org>
parents:
4932
diff
changeset
|
4285 write_ascstring (pstream, " "); |
788 | 4286 print_internal (EVENT_CHANNEL (emacs_event), pstream, 1); |
4287 } | |
4288 } | |
4289 | |
4290 static int | |
4291 emacs_mswindows_compare_magic_event (Lisp_Event *e1, Lisp_Event *e2) | |
4292 { | |
1204 | 4293 return (EVENT_MAGIC_MSWINDOWS_EVENT (e1) == |
4294 EVENT_MAGIC_MSWINDOWS_EVENT (e2)); | |
788 | 4295 } |
4296 | |
4297 static Hashcode | |
4298 emacs_mswindows_hash_magic_event (Lisp_Event *e) | |
4299 { | |
1204 | 4300 return (EVENT_MAGIC_MSWINDOWS_EVENT (e)); |
788 | 4301 } |
4302 | |
428 | 4303 /* |
4304 * Handle a magic event off the dispatch queue. | |
4305 */ | |
4306 static void | |
440 | 4307 emacs_mswindows_handle_magic_event (Lisp_Event *emacs_event) |
428 | 4308 { |
1204 | 4309 switch (EVENT_MAGIC_MSWINDOWS_EVENT (emacs_event)) |
428 | 4310 { |
4311 case XM_BUMPQUEUE: | |
4312 break; | |
4313 | |
442 | 4314 case WM_PAINT: |
4315 { | |
4316 struct frame *f = XFRAME (EVENT_CHANNEL (emacs_event)); | |
4317 mswindows_handle_paint (f); | |
4318 (FRAME_MSWINDOWS_DATA (f))->paint_pending = 0; | |
4319 } | |
4320 break; | |
4321 | |
428 | 4322 case WM_SETFOCUS: |
4323 case WM_KILLFOCUS: | |
4324 { | |
4325 Lisp_Object frame = EVENT_CHANNEL (emacs_event); | |
4326 struct frame *f = XFRAME (frame); | |
1204 | 4327 int in_p = (EVENT_MAGIC_MSWINDOWS_EVENT (emacs_event) |
964 | 4328 == WM_SETFOCUS); |
428 | 4329 Lisp_Object conser; |
442 | 4330 struct gcpro gcpro1; |
4331 | |
4332 /* On focus change, clear all memory of sticky modifiers | |
4333 to avoid non-intuitive behavior. */ | |
4334 clear_sticky_modifiers (); | |
428 | 4335 |
4336 conser = Fcons (frame, Fcons (FRAME_DEVICE (f), in_p ? Qt : Qnil)); | |
442 | 4337 GCPRO1 (conser); |
428 | 4338 emacs_handle_focus_change_preliminary (conser); |
4339 /* Under X the stuff up to here is done in the X event handler. | |
4340 I Don't know why */ | |
4341 emacs_handle_focus_change_final (conser); | |
442 | 4342 UNGCPRO; |
428 | 4343 |
4344 } | |
4345 break; | |
4346 | |
4347 case XM_MAPFRAME: | |
4348 case XM_UNMAPFRAME: | |
4349 { | |
4350 Lisp_Object frame = EVENT_CHANNEL (emacs_event); | |
1204 | 4351 va_run_hook_with_args (EVENT_MAGIC_MSWINDOWS_EVENT (emacs_event) |
428 | 4352 == XM_MAPFRAME ? |
4353 Qmap_frame_hook : Qunmap_frame_hook, | |
4354 1, frame); | |
4355 } | |
4356 break; | |
4357 | |
4358 /* #### What about Enter & Leave */ | |
4359 #if 0 | |
4360 va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook : | |
4361 Qmouse_leave_frame_hook, 1, frame); | |
4362 #endif | |
4363 | |
4364 default: | |
4365 assert(0); | |
4366 } | |
4367 } | |
4368 | |
853 | 4369 #ifndef CYGWIN |
4370 | |
428 | 4371 static HANDLE |
440 | 4372 get_process_input_waitable (Lisp_Process *process) |
428 | 4373 { |
853 | 4374 Lisp_Object instr, outstr, errstr, p; |
793 | 4375 p = wrap_process (process); |
853 | 4376 get_process_streams (process, &instr, &outstr, &errstr); |
428 | 4377 assert (!NILP (instr)); |
4378 return (network_connection_p (p) | |
4379 ? get_winsock_stream_waitable (XLSTREAM (instr)) | |
4380 : get_ntpipe_input_stream_waitable (XLSTREAM (instr))); | |
4381 } | |
4382 | |
853 | 4383 static HANDLE |
4384 get_process_stderr_waitable (Lisp_Process *process) | |
4385 { | |
4386 Lisp_Object instr, outstr, errstr; | |
4387 get_process_streams (process, &instr, &outstr, &errstr); | |
4388 if (NILP (errstr)) | |
4389 return INVALID_HANDLE_VALUE; | |
4390 return get_ntpipe_input_stream_waitable (XLSTREAM (errstr)); | |
4391 } | |
4392 | |
4393 #endif /* not CYGWIN */ | |
4394 | |
428 | 4395 static void |
853 | 4396 emacs_mswindows_select_process (Lisp_Process *process, int doin, int doerr) |
428 | 4397 { |
853 | 4398 #ifdef CYGWIN |
4399 int infd, errfd; | |
4400 | |
4401 event_stream_unixoid_select_process (process, doin, doerr, &infd, &errfd); | |
4402 #else | |
4403 HANDLE hev = INVALID_HANDLE_VALUE; | |
4404 HANDLE herr = INVALID_HANDLE_VALUE; | |
4405 | |
4406 if (doin) | |
4407 { | |
4408 hev = get_process_input_waitable (process); | |
4409 if (!add_waitable_handle (hev)) | |
4410 { | |
4411 hev = INVALID_HANDLE_VALUE; | |
4412 goto err; | |
4413 } | |
4414 } | |
4415 | |
4416 if (doerr) | |
4417 { | |
4418 herr = get_process_stderr_waitable (process); | |
4419 if (herr != INVALID_HANDLE_VALUE && !add_waitable_handle (herr)) | |
4420 { | |
4421 herr = INVALID_HANDLE_VALUE; | |
4422 goto err; | |
4423 } | |
4424 } | |
4425 | |
428 | 4426 { |
853 | 4427 /* Also select on the process handle itself, so we can receive |
4428 exit notifications. Only do this once, not each time this | |
4429 function is called (which can happen many times, e.g. if | |
4430 (set-process-filter proc t) is called and then a process filter | |
4431 is set again). It will be unselected in mswindows_need_event(). */ | |
793 | 4432 Lisp_Object p = wrap_process (process); |
4433 | |
428 | 4434 if (!network_connection_p (p)) |
4435 { | |
853 | 4436 HANDLE hprocess = get_nt_process_handle_only_first_time (process); |
4437 if (hprocess != INVALID_HANDLE_VALUE | |
4438 && !add_waitable_handle (hprocess)) | |
4439 goto err; | |
428 | 4440 } |
4441 } | |
853 | 4442 |
4443 return; | |
4444 | |
4445 err: | |
4446 if (hev != INVALID_HANDLE_VALUE) | |
4447 remove_waitable_handle (hev); | |
4448 if (herr != INVALID_HANDLE_VALUE) | |
4449 remove_waitable_handle (herr); | |
4450 invalid_operation ("Too many active processes", wrap_process (process)); | |
4451 #endif /* CYGWIN */ | |
428 | 4452 } |
4453 | |
4454 static void | |
853 | 4455 emacs_mswindows_unselect_process (Lisp_Process *process, int doin, int doerr) |
428 | 4456 { |
853 | 4457 #ifdef CYGWIN |
4458 int infd, errfd; | |
4459 | |
4460 event_stream_unixoid_unselect_process (process, doin, doerr, &infd, &errfd); | |
4461 #else | |
4462 if (doin) | |
4463 { | |
4464 /* Process handle is removed in the event loop as soon | |
4465 as it is signaled, so don't bother here about it */ | |
4466 HANDLE hev = get_process_input_waitable (process); | |
4467 remove_waitable_handle (hev); | |
4468 } | |
4469 if (doerr) | |
4470 { | |
4471 /* Process handle is removed in the event loop as soon | |
4472 as it is signaled, so don't bother here about it */ | |
4473 HANDLE herr = get_process_stderr_waitable (process); | |
4474 if (herr != INVALID_HANDLE_VALUE) | |
4475 remove_waitable_handle (herr); | |
4476 } | |
4477 #endif /* CYGWIN */ | |
428 | 4478 } |
4479 | |
4480 static void | |
2286 | 4481 emacs_mswindows_select_console (struct console *USED_IF_CYGWIN (con)) |
428 | 4482 { |
853 | 4483 #ifdef CYGWIN |
428 | 4484 if (CONSOLE_MSWINDOWS_P (con)) |
4485 return; /* mswindows consoles are automatically selected */ | |
4486 | |
4487 event_stream_unixoid_select_console (con); | |
1204 | 4488 #else |
4489 #if 0 | |
4490 /* This is an attempt to get `xemacs -batch -l dunnet' to work. | |
4491 Doesn't currently work and fucks other things up. */ | |
4492 if (CONSOLE_STREAM_P (con) && | |
4493 !UNBOUNDP (CONSOLE_STREAM_DATA (con)->instream)) | |
4494 { | |
4495 HANDLE h = | |
4496 (HANDLE) _get_osfhandle (fileno (CONSOLE_STREAM_DATA (con)->in)); | |
4497 if (PeekNamedPipe (h, 0, 0, 0, 0, 0)) | |
4498 { | |
4499 Lisp_Object lstr = make_ntpipe_input_stream (h, 0); | |
4500 HANDLE hwait = get_ntpipe_input_stream_waitable (XLSTREAM (lstr)); | |
4501 | |
4502 if (!add_waitable_handle (hwait)) | |
4503 invalid_operation ("Too many active processes", | |
4504 wrap_console (con)); | |
4505 CONSOLE_STREAM_DATA (con)->instream = lstr; | |
4506 } | |
4507 else | |
4508 /* Unable to select on this stream */ | |
4509 CONSOLE_STREAM_DATA (con)->instream = Qunbound; | |
4510 } | |
4511 #endif /* 0 */ | |
428 | 4512 #endif |
4513 } | |
4514 | |
4515 static void | |
2286 | 4516 emacs_mswindows_unselect_console (struct console *USED_IF_CYGWIN (con)) |
428 | 4517 { |
853 | 4518 #ifdef CYGWIN |
428 | 4519 if (CONSOLE_MSWINDOWS_P (con)) |
4520 return; /* mswindows consoles are automatically selected */ | |
4521 | |
4522 event_stream_unixoid_unselect_console (con); | |
1204 | 4523 #else |
4524 #if 0 /* see above */ | |
4525 if (CONSOLE_STREAM_P (con) && | |
4526 !UNBOUNDP (CONSOLE_STREAM_DATA (con)->instream)) | |
428 | 4527 { |
1204 | 4528 Lisp_Object instr = CONSOLE_STREAM_DATA (con)->instream; |
4529 HANDLE hwait; | |
4530 | |
4531 assert (!NILP (instr)); | |
4532 hwait = get_ntpipe_input_stream_waitable (XLSTREAM (instr)); | |
4533 | |
4534 remove_waitable_handle (hwait); | |
428 | 4535 } |
1204 | 4536 #endif /* 0 */ |
4537 #endif | |
428 | 4538 } |
4539 | |
853 | 4540 static void |
4541 emacs_mswindows_create_io_streams (void *inhandle, void *outhandle, | |
4542 void *errhandle, Lisp_Object *instream, | |
4543 Lisp_Object *outstream, | |
4544 Lisp_Object *errstream, | |
4545 USID *in_usid, | |
4546 USID *err_usid, | |
4547 int flags) | |
428 | 4548 { |
853 | 4549 #ifdef CYGWIN |
4550 event_stream_unixoid_create_io_streams (inhandle, outhandle, | |
4551 errhandle, instream, | |
4552 outstream, errstream, | |
4553 in_usid, err_usid, flags); | |
4554 #else | |
428 | 4555 /* Handles for streams */ |
853 | 4556 HANDLE hin, hout, herr; |
428 | 4557 /* fds. These just stored along with the streams, and are closed in |
4558 delete stream pair method, because we need to handle fake unices | |
4559 here. */ | |
853 | 4560 int fdi, fdo, fde; |
4561 | |
4562 /* Decode inhandle, outhandle, errhandle. Their meaning depends on | |
428 | 4563 the process implementation being used. */ |
4564 hin = (HANDLE) inhandle; | |
4565 hout = (HANDLE) outhandle; | |
853 | 4566 if (errhandle == (void *) -1) |
4567 herr = INVALID_HANDLE_VALUE; | |
4568 else | |
4569 herr = (HANDLE) errhandle; | |
4570 fdi = fdo = fde = -1; | |
428 | 4571 |
4572 *instream = (hin == INVALID_HANDLE_VALUE | |
4573 ? Qnil | |
4574 : flags & STREAM_NETWORK_CONNECTION | |
853 | 4575 ? make_winsock_input_stream ((SOCKET) hin, fdi) |
428 | 4576 : make_ntpipe_input_stream (hin, fdi)); |
4577 | |
853 | 4578 *errstream = (herr == INVALID_HANDLE_VALUE |
4579 ? Qnil | |
4580 : make_ntpipe_input_stream (herr, fde)); | |
4581 | |
428 | 4582 *outstream = (hout == INVALID_HANDLE_VALUE |
4583 ? Qnil | |
4584 : flags & STREAM_NETWORK_CONNECTION | |
4585 ? make_winsock_output_stream ((SOCKET)hout, fdo) | |
4586 : make_ntpipe_output_stream (hout, fdo)); | |
853 | 4587 |
4588 *in_usid = | |
4589 (NILP (*instream) | |
4590 ? USID_ERROR | |
4591 : flags & STREAM_NETWORK_CONNECTION | |
4592 ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream))) | |
4593 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM | |
4594 (*instream)))); | |
4595 | |
4596 *err_usid = | |
4597 (NILP (*errstream) | |
4598 ? USID_DONTHASH | |
4599 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM | |
4600 (*errstream)))); | |
4601 #endif /* CYGWIN */ | |
428 | 4602 } |
4603 | |
853 | 4604 static void |
4605 emacs_mswindows_delete_io_streams (Lisp_Object instream, | |
2286 | 4606 Lisp_Object USED_IF_CYGWIN (outstream), |
853 | 4607 Lisp_Object errstream, |
4608 USID *in_usid, | |
4609 USID *err_usid) | |
428 | 4610 { |
853 | 4611 #ifdef CYGWIN |
4612 event_stream_unixoid_delete_io_streams (instream, outstream, errstream, | |
4613 in_usid, err_usid); | |
4614 #else | |
4615 *in_usid = | |
4616 (NILP (instream) | |
4617 ? USID_DONTHASH | |
4618 : LSTREAM_TYPE_P (XLSTREAM (instream), winsock) | |
4619 ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream))) | |
4620 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM | |
4621 (instream)))); | |
4622 | |
4623 *err_usid = | |
4624 (NILP (errstream) | |
4625 ? USID_DONTHASH | |
4626 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM | |
4627 (errstream)))); | |
4628 #endif /* CYGWIN */ | |
428 | 4629 } |
4630 | |
442 | 4631 static int |
2286 | 4632 emacs_mswindows_current_event_timestamp (struct console *UNUSED (c)) |
442 | 4633 { |
4634 return GetTickCount (); | |
4635 } | |
4636 | |
428 | 4637 #ifndef HAVE_X_WINDOWS |
4638 /* This is called from GC when a process object is about to be freed. | |
4639 If we've still got pointers to it in this file, we're gonna lose hard. | |
853 | 4640 */ |
4641 void debug_process_finalization (Lisp_Process *p); | |
428 | 4642 void |
2286 | 4643 debug_process_finalization (Lisp_Process *UNUSED (p)) |
428 | 4644 { |
4645 #if 0 /* #### */ | |
853 | 4646 Lisp_Object instr, outstr, errstr; |
4647 | |
4648 get_process_streams (p, &instr, &outstr, &errstr); | |
428 | 4649 /* if it still has fds, then it hasn't been killed yet. */ |
771 | 4650 assert (NILP (instr)); |
4651 assert (NILP (outstr)); | |
853 | 4652 assert (NILP (errstr)); |
428 | 4653 |
4654 /* #### More checks here */ | |
4655 #endif | |
4656 } | |
4657 #endif | |
4658 | |
593 | 4659 #ifdef DEBUG_XEMACS |
4660 | |
4661 struct mswin_message_debug | |
4662 { | |
4663 int mess; | |
4932 | 4664 const Ascbyte *string; |
593 | 4665 }; |
4666 | |
4667 #define FROB(val) { val, #val, }, | |
4668 | |
4669 struct mswin_message_debug debug_mswin_messages[] = | |
4670 { | |
4671 FROB (WM_NULL) | |
4672 FROB (WM_CREATE) | |
4673 FROB (WM_DESTROY) | |
4674 FROB (WM_MOVE) | |
4675 FROB (WM_SIZE) | |
4676 | |
4677 FROB (WM_ACTIVATE) | |
4678 | |
4679 FROB (WM_SETFOCUS) | |
4680 FROB (WM_KILLFOCUS) | |
4681 FROB (WM_ENABLE) | |
4682 FROB (WM_SETREDRAW) | |
4683 FROB (WM_SETTEXT) | |
4684 FROB (WM_GETTEXT) | |
4685 FROB (WM_GETTEXTLENGTH) | |
4686 FROB (WM_PAINT) | |
4687 FROB (WM_CLOSE) | |
4688 FROB (WM_QUERYENDSESSION) | |
4689 FROB (WM_QUIT) | |
4690 FROB (WM_QUERYOPEN) | |
4691 FROB (WM_ERASEBKGND) | |
4692 FROB (WM_SYSCOLORCHANGE) | |
4693 FROB (WM_ENDSESSION) | |
4694 FROB (WM_SHOWWINDOW) | |
4695 FROB (WM_WININICHANGE) | |
4696 #if(WINVER >= 0x0400) | |
4697 FROB (WM_SETTINGCHANGE) | |
4698 #endif /* WINVER >= 0x0400 */ | |
4699 | |
4700 FROB (WM_DEVMODECHANGE) | |
4701 FROB (WM_ACTIVATEAPP) | |
4702 FROB (WM_FONTCHANGE) | |
4703 FROB (WM_TIMECHANGE) | |
4704 FROB (WM_CANCELMODE) | |
4705 FROB (WM_SETCURSOR) | |
4706 FROB (WM_MOUSEACTIVATE) | |
4707 FROB (WM_CHILDACTIVATE) | |
4708 FROB (WM_QUEUESYNC) | |
4709 | |
4710 FROB (WM_GETMINMAXINFO) | |
4711 | |
4712 FROB (WM_PAINTICON) | |
4713 FROB (WM_ICONERASEBKGND) | |
4714 FROB (WM_NEXTDLGCTL) | |
4715 FROB (WM_SPOOLERSTATUS) | |
4716 FROB (WM_DRAWITEM) | |
4717 FROB (WM_MEASUREITEM) | |
4718 FROB (WM_DELETEITEM) | |
4719 FROB (WM_VKEYTOITEM) | |
4720 FROB (WM_CHARTOITEM) | |
4721 FROB (WM_SETFONT) | |
4722 FROB (WM_GETFONT) | |
4723 FROB (WM_SETHOTKEY) | |
4724 FROB (WM_GETHOTKEY) | |
4725 FROB (WM_QUERYDRAGICON) | |
4726 FROB (WM_COMPAREITEM) | |
1687 | 4727 #if(WINVER >= 0x0500) && defined(WM_GETOBJECT) |
593 | 4728 FROB (WM_GETOBJECT) |
4729 #endif /* WINVER >= 0x0500 */ | |
4730 FROB (WM_COMPACTING) | |
4731 FROB (WM_COMMNOTIFY) | |
4732 FROB (WM_WINDOWPOSCHANGING) | |
4733 FROB (WM_WINDOWPOSCHANGED) | |
4734 | |
4735 FROB (WM_POWER) | |
4736 | |
4737 FROB (WM_COPYDATA) | |
4738 FROB (WM_CANCELJOURNAL) | |
4739 | |
4740 #if(WINVER >= 0x0400) | |
4741 FROB (WM_NOTIFY) | |
4742 FROB (WM_INPUTLANGCHANGEREQUEST) | |
4743 FROB (WM_INPUTLANGCHANGE) | |
4744 FROB (WM_TCARD) | |
4745 FROB (WM_HELP) | |
4746 FROB (WM_USERCHANGED) | |
4747 FROB (WM_NOTIFYFORMAT) | |
4748 | |
4749 FROB (WM_CONTEXTMENU) | |
4750 FROB (WM_STYLECHANGING) | |
4751 FROB (WM_STYLECHANGED) | |
4752 FROB (WM_DISPLAYCHANGE) | |
4753 FROB (WM_GETICON) | |
4754 FROB (WM_SETICON) | |
4755 #endif /* WINVER >= 0x0400 */ | |
4756 | |
4757 FROB (WM_NCCREATE) | |
4758 FROB (WM_NCDESTROY) | |
4759 FROB (WM_NCCALCSIZE) | |
4760 FROB (WM_NCHITTEST) | |
4761 FROB (WM_NCPAINT) | |
4762 FROB (WM_NCACTIVATE) | |
4763 FROB (WM_GETDLGCODE) | |
604 | 4764 #ifdef WM_SYNCPAINT /* not in VC 5 */ |
593 | 4765 FROB (WM_SYNCPAINT) |
604 | 4766 #endif /* WM_SYNCPAINT */ |
593 | 4767 FROB (WM_NCMOUSEMOVE) |
4768 FROB (WM_NCLBUTTONDOWN) | |
4769 FROB (WM_NCLBUTTONUP) | |
4770 FROB (WM_NCLBUTTONDBLCLK) | |
4771 FROB (WM_NCRBUTTONDOWN) | |
4772 FROB (WM_NCRBUTTONUP) | |
4773 FROB (WM_NCRBUTTONDBLCLK) | |
4774 FROB (WM_NCMBUTTONDOWN) | |
4775 FROB (WM_NCMBUTTONUP) | |
4776 FROB (WM_NCMBUTTONDBLCLK) | |
4777 | |
4778 /* FROB (WM_KEYFIRST) */ | |
4779 FROB (WM_KEYDOWN) | |
4780 FROB (WM_KEYUP) | |
4781 FROB (WM_CHAR) | |
4782 FROB (WM_DEADCHAR) | |
4783 FROB (WM_SYSKEYDOWN) | |
4784 FROB (WM_SYSKEYUP) | |
4785 FROB (WM_SYSCHAR) | |
4786 FROB (WM_SYSDEADCHAR) | |
4787 FROB (WM_KEYLAST) | |
4788 | |
604 | 4789 #if(WINVER >= 0x0400) && defined (WM_IME_STARTCOMPOSITION) |
4790 /* not in Cygwin? */ | |
593 | 4791 FROB (WM_IME_STARTCOMPOSITION) |
4792 FROB (WM_IME_ENDCOMPOSITION) | |
4793 FROB (WM_IME_COMPOSITION) | |
4794 FROB (WM_IME_KEYLAST) | |
604 | 4795 #endif /* WINVER >= 0x0400 && defined (WM_IME_STARTCOMPOSITION) */ |
593 | 4796 |
4797 FROB (WM_INITDIALOG) | |
4798 FROB (WM_COMMAND) | |
4799 FROB (WM_SYSCOMMAND) | |
4800 FROB (WM_TIMER) | |
4801 FROB (WM_HSCROLL) | |
4802 FROB (WM_VSCROLL) | |
4803 FROB (WM_INITMENU) | |
4804 FROB (WM_INITMENUPOPUP) | |
4805 FROB (WM_MENUSELECT) | |
4806 FROB (WM_MENUCHAR) | |
4807 FROB (WM_ENTERIDLE) | |
4808 #if(WINVER >= 0x0500) | |
4809 FROB (WM_MENURBUTTONUP) | |
1687 | 4810 #ifdef WM_MENUDRAG |
593 | 4811 FROB (WM_MENUDRAG) |
1687 | 4812 #endif |
4813 #ifdef WM_MENUGETOBJECT | |
593 | 4814 FROB (WM_MENUGETOBJECT) |
1687 | 4815 #endif |
4816 #ifdef WM_UNINITMENUPOPUP | |
593 | 4817 FROB (WM_UNINITMENUPOPUP) |
1687 | 4818 #endif |
4819 #ifdef WM_MENUCOMMAND | |
593 | 4820 FROB (WM_MENUCOMMAND) |
1687 | 4821 #endif |
593 | 4822 #endif /* WINVER >= 0x0500 */ |
4823 | |
4824 | |
4825 FROB (WM_CTLCOLORMSGBOX) | |
4826 FROB (WM_CTLCOLOREDIT) | |
4827 FROB (WM_CTLCOLORLISTBOX) | |
4828 FROB (WM_CTLCOLORBTN) | |
4829 FROB (WM_CTLCOLORDLG) | |
4830 FROB (WM_CTLCOLORSCROLLBAR) | |
4831 FROB (WM_CTLCOLORSTATIC) | |
4832 | |
4833 | |
4834 /* FROB (WM_MOUSEFIRST) */ | |
4835 FROB (WM_MOUSEMOVE) | |
4836 FROB (WM_LBUTTONDOWN) | |
4837 FROB (WM_LBUTTONUP) | |
4838 FROB (WM_LBUTTONDBLCLK) | |
4839 FROB (WM_RBUTTONDOWN) | |
4840 FROB (WM_RBUTTONUP) | |
4841 FROB (WM_RBUTTONDBLCLK) | |
4842 FROB (WM_MBUTTONDOWN) | |
4843 FROB (WM_MBUTTONUP) | |
4844 FROB (WM_MBUTTONDBLCLK) | |
4845 | |
4846 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) | |
4847 FROB (WM_MOUSEWHEEL) | |
4848 FROB (WM_MOUSELAST) | |
4849 #else | |
4850 FROB (WM_MOUSELAST) | |
4851 #endif /* if (_WIN32_WINNT < 0x0400) */ | |
4852 | |
4853 FROB (WM_PARENTNOTIFY) | |
4854 FROB (WM_ENTERMENULOOP) | |
4855 FROB (WM_EXITMENULOOP) | |
4856 | |
4857 #if(WINVER >= 0x0400) | |
4858 FROB (WM_NEXTMENU) | |
4859 | |
4860 FROB (WM_SIZING) | |
4861 FROB (WM_CAPTURECHANGED) | |
4862 FROB (WM_MOVING) | |
4863 FROB (WM_POWERBROADCAST) | |
4864 | |
4865 FROB (WM_DEVICECHANGE) | |
4866 | |
4867 #endif /* WINVER >= 0x0400 */ | |
4868 | |
4869 FROB (WM_MDICREATE) | |
4870 FROB (WM_MDIDESTROY) | |
4871 FROB (WM_MDIACTIVATE) | |
4872 FROB (WM_MDIRESTORE) | |
4873 FROB (WM_MDINEXT) | |
4874 FROB (WM_MDIMAXIMIZE) | |
4875 FROB (WM_MDITILE) | |
4876 FROB (WM_MDICASCADE) | |
4877 FROB (WM_MDIICONARRANGE) | |
4878 FROB (WM_MDIGETACTIVE) | |
4879 | |
4880 | |
4881 FROB (WM_MDISETMENU) | |
4882 FROB (WM_ENTERSIZEMOVE) | |
4883 FROB (WM_EXITSIZEMOVE) | |
4884 FROB (WM_DROPFILES) | |
4885 FROB (WM_MDIREFRESHMENU) | |
4886 | |
604 | 4887 #ifdef WM_IME_SETCONTEXT /* not in Cygwin? */ |
593 | 4888 |
4889 #if(WINVER >= 0x0400) | |
4890 FROB (WM_IME_SETCONTEXT) | |
4891 FROB (WM_IME_NOTIFY) | |
4892 FROB (WM_IME_CONTROL) | |
4893 FROB (WM_IME_COMPOSITIONFULL) | |
4894 FROB (WM_IME_SELECT) | |
4895 FROB (WM_IME_CHAR) | |
4896 #endif /* WINVER >= 0x0400 */ | |
1687 | 4897 #if(WINVER >= 0x0500) && defined(WM_IME_REQUEST) |
593 | 4898 FROB (WM_IME_REQUEST) |
4899 #endif /* WINVER >= 0x0500 */ | |
4900 #if(WINVER >= 0x0400) | |
4901 FROB (WM_IME_KEYDOWN) | |
4902 FROB (WM_IME_KEYUP) | |
4903 #endif /* WINVER >= 0x0400 */ | |
4904 | |
604 | 4905 #endif /* WM_IME_SETCONTEXT */ |
593 | 4906 |
4907 #if(_WIN32_WINNT >= 0x0400) | |
4908 FROB (WM_MOUSEHOVER) | |
4909 FROB (WM_MOUSELEAVE) | |
4910 #endif /* _WIN32_WINNT >= 0x0400 */ | |
4911 | |
4912 FROB (WM_CUT) | |
4913 FROB (WM_COPY) | |
4914 FROB (WM_PASTE) | |
4915 FROB (WM_CLEAR) | |
4916 FROB (WM_UNDO) | |
4917 FROB (WM_RENDERFORMAT) | |
4918 FROB (WM_RENDERALLFORMATS) | |
4919 FROB (WM_DESTROYCLIPBOARD) | |
4920 FROB (WM_DRAWCLIPBOARD) | |
4921 FROB (WM_PAINTCLIPBOARD) | |
4922 FROB (WM_VSCROLLCLIPBOARD) | |
4923 FROB (WM_SIZECLIPBOARD) | |
4924 FROB (WM_ASKCBFORMATNAME) | |
4925 FROB (WM_CHANGECBCHAIN) | |
4926 FROB (WM_HSCROLLCLIPBOARD) | |
4927 FROB (WM_QUERYNEWPALETTE) | |
4928 FROB (WM_PALETTEISCHANGING) | |
4929 FROB (WM_PALETTECHANGED) | |
4930 FROB (WM_HOTKEY) | |
4931 | |
4932 #if(WINVER >= 0x0400) | |
4933 FROB (WM_PRINT) | |
4934 FROB (WM_PRINTCLIENT) | |
4935 | |
4936 FROB (WM_HANDHELDFIRST) | |
4937 FROB (WM_HANDHELDLAST) | |
4938 | |
4939 FROB (WM_AFXFIRST) | |
4940 FROB (WM_AFXLAST) | |
4941 #endif /* WINVER >= 0x0400 */ | |
4942 | |
4943 FROB (WM_PENWINFIRST) | |
4944 FROB (WM_PENWINLAST) | |
4945 }; | |
4946 | |
4947 #undef FROB | |
4948 | |
4949 static void | |
4950 debug_output_mswin_message (HWND hwnd, UINT message_, WPARAM wParam, | |
4951 LPARAM lParam) | |
4952 { | |
4953 Lisp_Object frame = mswindows_find_frame (hwnd); | |
4954 int i; | |
4932 | 4955 const Ascbyte *str = 0; |
593 | 4956 /* struct mswin_message_debug *i_hate_cranking_out_code_like_this; */ |
4957 | |
4958 for (i = 0; i < countof (debug_mswin_messages); i++) | |
4959 { | |
647 | 4960 if (debug_mswin_messages[i].mess == (int) message_) |
593 | 4961 { |
4962 str = debug_mswin_messages[i].string; | |
4963 break; | |
4964 } | |
4965 } | |
4966 | |
4967 if (str) | |
4968 stderr_out ("%s", str); | |
4969 else | |
4970 stderr_out ("%x", message_); | |
4971 | |
4972 if (debug_mswindows_events > 1) | |
4973 { | |
4974 stderr_out (" wparam=%d lparam=%d hwnd=%x frame: ", | |
4975 wParam, (int) lParam, (unsigned int) hwnd); | |
4976 debug_print (frame); | |
903 | 4977 if (message_ == WM_WINDOWPOSCHANGED || |
4978 message_ == WM_WINDOWPOSCHANGING) | |
4979 { | |
4980 WINDOWPOS *wp = (WINDOWPOS *) lParam; | |
4981 stderr_out(" WINDOWPOS: x=%d, y=%d, h=%d, w=%d\n", | |
4982 wp->x, wp->y, wp->cx, wp->cy); | |
4983 } | |
4984 else if (message_ == WM_MOVE) | |
4985 { | |
4986 int x = (int)(short) LOWORD(lParam); /* horizontal position */ | |
4987 int y = (int)(short) HIWORD(lParam); /* vertical position */ | |
4988 stderr_out(" MOVE: x=%d, y=%d\n", x, y); | |
4989 } | |
4990 else if (message_ == WM_SIZE) | |
4991 { | |
4992 int w = (int)(short) LOWORD(lParam); /* width */ | |
4993 int h = (int)(short) HIWORD(lParam); /* height */ | |
4994 stderr_out(" SIZE: w=%d, h=%d\n", w, h); | |
4995 } | |
593 | 4996 } |
4997 else | |
4998 stderr_out ("\n"); | |
4999 } | |
5000 | |
5001 #endif /* DEBUG_XEMACS */ | |
5002 | |
428 | 5003 /************************************************************************/ |
5004 /* initialization */ | |
5005 /************************************************************************/ | |
5006 | |
5007 void | |
5008 reinit_vars_of_event_mswindows (void) | |
5009 { | |
5010 mswindows_pending_timers_count = 0; | |
5011 | |
1204 | 5012 mswindows_event_stream = xnew_and_zero (struct event_stream); |
428 | 5013 |
5014 mswindows_event_stream->event_pending_p = emacs_mswindows_event_pending_p; | |
5015 mswindows_event_stream->next_event_cb = emacs_mswindows_next_event; | |
5016 mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event; | |
788 | 5017 mswindows_event_stream->format_magic_event_cb = emacs_mswindows_format_magic_event; |
5018 mswindows_event_stream->compare_magic_event_cb= emacs_mswindows_compare_magic_event; | |
5019 mswindows_event_stream->hash_magic_event_cb = emacs_mswindows_hash_magic_event; | |
428 | 5020 mswindows_event_stream->add_timeout_cb = emacs_mswindows_add_timeout; |
5021 mswindows_event_stream->remove_timeout_cb = emacs_mswindows_remove_timeout; | |
1204 | 5022 mswindows_event_stream->drain_queue_cb = emacs_mswindows_drain_queue; |
428 | 5023 mswindows_event_stream->select_console_cb = emacs_mswindows_select_console; |
5024 mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console; | |
5025 mswindows_event_stream->select_process_cb = emacs_mswindows_select_process; | |
5026 mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process; | |
853 | 5027 mswindows_event_stream->create_io_streams_cb = emacs_mswindows_create_io_streams; |
5028 mswindows_event_stream->delete_io_streams_cb = emacs_mswindows_delete_io_streams; | |
442 | 5029 mswindows_event_stream->current_event_timestamp_cb = |
5030 emacs_mswindows_current_event_timestamp; | |
903 | 5031 |
5032 dde_eval_pending = 0; | |
428 | 5033 } |
5034 | |
5035 void | |
5036 vars_of_event_mswindows (void) | |
5037 { | |
5038 mswindows_s_dispatch_event_queue = Qnil; | |
5039 staticpro (&mswindows_s_dispatch_event_queue); | |
5040 mswindows_s_dispatch_event_queue_tail = Qnil; | |
1204 | 5041 dump_add_root_lisp_object (&mswindows_s_dispatch_event_queue_tail); |
428 | 5042 |
853 | 5043 mswindows_error_caught_in_modal_loop = 0; |
442 | 5044 |
903 | 5045 #ifdef HAVE_DRAGNDROP |
5046 Fprovide (Qdde); | |
5047 | |
5048 DEFVAR_LISP ("dde-advise-items", &Vdde_advise_items /* | |
5049 A list of allocated DDE advise items. | |
5050 Each item is an uninterned symbol, created using dde-alloc-advise-item. | |
5051 | |
5052 The symbol's value is the data which is returned to the DDE client when | |
5053 a request for the item is made (or a dde-advise call is made). | |
5054 | |
3025 | 5055 The symbol also has a `HSZ' property, which holds the DDE string handle |
903 | 5056 for the item, as a float. This is for internal use only, and should not |
5057 be modified. | |
5058 */ ); | |
5059 Vdde_advise_items = Qnil; | |
5060 | |
5061 dde_eval_result = Qnil; | |
5062 staticpro (&dde_eval_result); | |
5063 dde_eval_error = Qnil; | |
5064 staticpro (&dde_eval_error); | |
5065 #endif | |
5066 | |
442 | 5067 #ifdef DEBUG_XEMACS |
5068 DEFVAR_INT ("debug-mswindows-events", &debug_mswindows_events /* | |
593 | 5069 If non-zero, display debug information about Windows messages that XEmacs sees. |
442 | 5070 Information is displayed in a console window. Currently defined values are: |
5071 | |
593 | 5072 1 == non-verbose output (just the message name) |
5073 2 == verbose output (all parameters) | |
5074 3 == even more verbose output (extra debugging info) | |
442 | 5075 */ ); |
5076 debug_mswindows_events = 0; | |
5077 #endif | |
5078 | |
5079 DEFVAR_BOOL ("mswindows-alt-by-itself-activates-menu", | |
5080 &mswindows_alt_by_itself_activates_menu /* | |
5081 *Controls whether pressing and releasing the Alt key activates the menubar. | |
5082 This applies only if no intervening key was pressed. See also | |
5083 `menu-accelerator-enabled', which is probably the behavior you actually want. | |
428 | 5084 Default is t. |
5085 */ ); | |
5086 | |
442 | 5087 DEFVAR_BOOL ("mswindows-dynamic-frame-resize", |
5088 &mswindows_dynamic_frame_resize /* | |
428 | 5089 *Controls redrawing frame contents during mouse-drag or keyboard resize |
5090 operation. When non-nil, the frame is redrawn while being resized. When | |
5091 nil, frame is not redrawn, and exposed areas are filled with default | |
5092 MDI application background color. Note that this option only has effect | |
5093 if "Show window contents while dragging" is on in system Display/Plus! | |
5094 settings. | |
5095 Default is t on fast machines, nil on slow. | |
5096 */ ); | |
5097 | |
442 | 5098 DEFVAR_INT ("mswindows-mouse-button-tolerance", |
5099 &mswindows_mouse_button_tolerance /* | |
428 | 5100 *Analogue of double click interval for faking middle mouse events. |
5101 The value is the minimum time in milliseconds that must elapse between | |
5102 left/right button down events before they are considered distinct events. | |
5103 If both mouse buttons are depressed within this interval, a middle mouse | |
5104 button down event is generated instead. | |
5105 If negative or zero, currently set system default is used instead. | |
5106 */ ); | |
5107 | |
5108 DEFVAR_INT ("mswindows-num-mouse-buttons", &mswindows_num_mouse_buttons /* | |
5109 Number of physical mouse buttons. | |
5110 */ ); | |
5111 | |
442 | 5112 DEFVAR_INT ("mswindows-mouse-button-max-skew-x", |
5113 &mswindows_mouse_button_max_skew_x /* | |
428 | 5114 *Maximum horizontal distance in pixels between points in which left and |
5115 right button clicks occurred for them to be translated into single | |
5116 middle button event. Clicks must occur in time not longer than defined | |
5117 by the variable `mswindows-mouse-button-tolerance'. | |
5118 If negative or zero, currently set system default is used instead. | |
5119 */ ); | |
5120 | |
442 | 5121 DEFVAR_INT ("mswindows-mouse-button-max-skew-y", |
5122 &mswindows_mouse_button_max_skew_y /* | |
428 | 5123 *Maximum vertical 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 | |
5130 mswindows_mouse_button_max_skew_x = 0; | |
5131 mswindows_mouse_button_max_skew_y = 0; | |
5132 mswindows_mouse_button_tolerance = 0; | |
442 | 5133 mswindows_alt_by_itself_activates_menu = 1; |
428 | 5134 } |
5135 | |
5136 void | |
5137 syms_of_event_mswindows (void) | |
5138 { | |
903 | 5139 #ifdef HAVE_DRAGNDROP |
5140 DEFSYMBOL(QHSZ); | |
5141 DEFSUBR(Fdde_alloc_advise_item); | |
5142 DEFSUBR(Fdde_free_advise_item); | |
5143 DEFSUBR(Fdde_advise); | |
5144 #endif | |
428 | 5145 } |
5146 | |
5147 void | |
5148 lstream_type_create_mswindows_selectable (void) | |
5149 { | |
853 | 5150 #ifndef CYGWIN |
428 | 5151 init_slurp_stream (); |
5152 init_shove_stream (); | |
5153 init_winsock_stream (); | |
5154 #endif | |
5155 } | |
5156 | |
5157 void | |
5158 init_event_mswindows_late (void) | |
5159 { | |
853 | 5160 #ifdef CYGWIN |
771 | 5161 windows_fd = retry_open ("/dev/windows", O_RDONLY | O_NONBLOCK, 0); |
814 | 5162 assert (windows_fd >= 0); |
428 | 5163 FD_SET (windows_fd, &input_wait_mask); |
814 | 5164 FD_ZERO (&zero_mask); |
428 | 5165 #endif |
5166 | |
5167 event_stream = mswindows_event_stream; | |
5168 | |
5169 mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE); | |
5170 mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS); | |
5171 } |