Mercurial > hg > xemacs-beta
comparison src/event-msw.c @ 274:ca9a9ec9c1c1 r21-0b35
Import from CVS: tag r21-0b35
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:29:42 +0200 |
parents | c5d627a313b1 |
children | 6330739388db |
comparison
equal
deleted
inserted
replaced
273:411aac7253ef | 274:ca9a9ec9c1c1 |
---|---|
44 #endif | 44 #endif |
45 | 45 |
46 #include "device.h" | 46 #include "device.h" |
47 #include "events.h" | 47 #include "events.h" |
48 #include "frame.h" | 48 #include "frame.h" |
49 #include "lstream.h" | |
49 #include "process.h" | 50 #include "process.h" |
50 #include "redisplay.h" | 51 #include "redisplay.h" |
51 #include "sysproc.h" | 52 #include "sysproc.h" |
52 #include "syswait.h" | 53 #include "syswait.h" |
53 #include "systime.h" | 54 #include "systime.h" |
54 | 55 |
55 #include "events-mod.h" | 56 #include "events-mod.h" |
57 | |
58 #include <errno.h> | |
56 | 59 |
57 #ifdef BROKEN_CYGWIN | 60 #ifdef BROKEN_CYGWIN |
58 int WINAPI DdeCmpStringHandles (HSZ, HSZ); | 61 int WINAPI DdeCmpStringHandles (HSZ, HSZ); |
59 HDDEDATA WINAPI DdeCreateDataHandle (DWORD, LPBYTE, DWORD, DWORD, HSZ, | 62 HDDEDATA WINAPI DdeCreateDataHandle (DWORD, LPBYTE, DWORD, DWORD, HSZ, |
60 UINT, UINT); | 63 UINT, UINT); |
100 | 103 |
101 /* The number of things we can wait on */ | 104 /* The number of things we can wait on */ |
102 #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1) | 105 #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1) |
103 | 106 |
104 /* List of mswindows waitable handles. */ | 107 /* List of mswindows waitable handles. */ |
105 static HANDLE mswindows_waitable[MAX_WAITABLE]; | 108 static HANDLE mswindows_waitable_handles[MAX_WAITABLE]; |
109 | |
110 /* Number of wait handles */ | |
111 static int mswindows_waitable_count=0; | |
106 | 112 |
107 /* Count of quit chars currently in the queue */ | 113 /* Count of quit chars currently in the queue */ |
108 /* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc() | 114 /* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc() |
109 Decremented in mswindows_dequeue_dispatch_event() */ | 115 Decremented in mswindows_dequeue_dispatch_event() */ |
110 int mswindows_quit_chars_count = 0; | 116 int mswindows_quit_chars_count = 0; |
114 int mswindows_num_mouse_buttons; | 120 int mswindows_num_mouse_buttons; |
115 int mswindows_mouse_button_max_skew_x; | 121 int mswindows_mouse_button_max_skew_x; |
116 int mswindows_mouse_button_max_skew_y; | 122 int mswindows_mouse_button_max_skew_y; |
117 int mswindows_mouse_button_tolerance; | 123 int mswindows_mouse_button_tolerance; |
118 | 124 |
119 /* Number of wait handles */ | |
120 static int mswindows_waitable_count=0; | |
121 | |
122 /* This is the event signaled by the event pump. | 125 /* This is the event signaled by the event pump. |
123 See mswindows_pump_outstanding_events for comments */ | 126 See mswindows_pump_outstanding_events for comments */ |
124 static Lisp_Object mswindows_error_caught_in_modal_loop; | 127 static Lisp_Object mswindows_error_caught_in_modal_loop; |
125 static int mswindows_in_modal_loop; | 128 static int mswindows_in_modal_loop; |
126 | 129 |
127 /* Count of wound timers */ | 130 /* Count of wound timers */ |
128 static int mswindows_pending_timers_count; | 131 static int mswindows_pending_timers_count; |
132 | |
133 /************************************************************************/ | |
134 /* Pipe instream - reads process output */ | |
135 /************************************************************************/ | |
136 | |
137 #define PIPE_READ_DELAY 20 | |
138 | |
139 #define HANDLE_TO_USID(h) ((USID)(h)) | |
140 | |
141 #define NTPIPE_SLURP_STREAM_DATA(stream) \ | |
142 LSTREAM_TYPE_DATA (stream, ntpipe_slurp) | |
143 | |
144 struct ntpipe_slurp_stream | |
145 { | |
146 LPARAM user_data; /* Any user data stored in the stream object */ | |
147 HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */ | |
148 /* This is a manual-reset object. */ | |
149 HANDLE hev_caller; /* Caller blocks on this, and we signal it */ | |
150 /* This is a manual-reset object. */ | |
151 HANDLE hev_unsleep; /* Pipe read delay is canceled if this is set */ | |
152 /* This is a manual-reset object. */ | |
153 HANDLE hpipe; /* Pipe read end handle. */ | |
154 HANDLE hthread; /* Reader thread handle. */ | |
155 BYTE onebyte; /* One byte buffer read by thread */ | |
156 DWORD die_p; /* Thread must exit ASAP if non-zero */ | |
157 BOOL eof_p : 1; /* Set when thread saw EOF */ | |
158 BOOL error_p : 1; /* Read error other than EOF/broken pipe */ | |
159 }; | |
160 | |
161 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-input", lstream_ntpipe_slurp, | |
162 sizeof (struct ntpipe_slurp_stream)); | |
163 | |
164 static DWORD WINAPI | |
165 slurp_thread (LPVOID vparam) | |
166 { | |
167 struct ntpipe_slurp_stream *s = (struct ntpipe_slurp_stream*)vparam; | |
168 | |
169 for (;;) | |
170 { | |
171 /* Read one byte from the pipe */ | |
172 DWORD actually_read; | |
173 if (!ReadFile (s->hpipe, &s->onebyte, 1, &actually_read, NULL)) | |
174 { | |
175 DWORD err = GetLastError (); | |
176 if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA) | |
177 s->eof_p = TRUE; | |
178 else | |
179 s->error_p = TRUE; | |
180 } | |
181 else if (actually_read == 0) | |
182 s->eof_p = TRUE; | |
183 | |
184 /* We must terminate on an error or eof */ | |
185 if (s->eof_p || s->error_p) | |
186 InterlockedIncrement (&s->die_p); | |
187 | |
188 /* Before we notify caller, we unsignal our event. */ | |
189 ResetEvent (s->hev_thread); | |
190 | |
191 /* Now we got something to notify caller, either a byte or an | |
192 error/eof indication. Before we do, allow internal pipe | |
193 buffer to accumulate little bit more data. | |
194 Reader function pulses this event before waiting for | |
195 a character, to avoid pipde delay, and to get the byte | |
196 immediately. */ | |
197 if (!s->die_p) | |
198 WaitForSingleObject (s->hev_unsleep, PIPE_READ_DELAY); | |
199 | |
200 /* Either make event loop generate a process event, or | |
201 inblock reader */ | |
202 SetEvent (s->hev_caller); | |
203 | |
204 /* Cleanup and exit if we're shot off */ | |
205 if (s->die_p) | |
206 break; | |
207 | |
208 /* Block until the client finishes with retireving the rest of | |
209 pipe data */ | |
210 WaitForSingleObject (s->hev_thread, INFINITE); | |
211 } | |
212 | |
213 return 0; | |
214 } | |
215 | |
216 static Lisp_Object | |
217 make_ntpipe_input_stream (HANDLE hpipe, LPARAM param) | |
218 { | |
219 Lisp_Object obj; | |
220 Lstream *lstr = Lstream_new (lstream_ntpipe_slurp, "r"); | |
221 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA (lstr); | |
222 DWORD thread_id_unused; | |
223 | |
224 /* We deal only with pipes, for we're using PeekNamedPipe api */ | |
225 assert (GetFileType (hpipe) == FILE_TYPE_PIPE); | |
226 | |
227 s->die_p = 0; | |
228 s->eof_p = FALSE; | |
229 s->error_p = FALSE; | |
230 s->hpipe = hpipe; | |
231 s->user_data = param; | |
232 | |
233 /* Create reader thread. This could fail, so do not | |
234 create events until thread is created */ | |
235 s->hthread = CreateThread (NULL, 0, slurp_thread, (LPVOID)s, | |
236 CREATE_SUSPENDED, &thread_id_unused); | |
237 if (s->hthread == NULL) | |
238 { | |
239 Lstream_delete (lstr); | |
240 return Qnil; | |
241 } | |
242 | |
243 /* hev_thread is a manual-reset event, initially signaled */ | |
244 s->hev_thread = CreateEvent (NULL, TRUE, TRUE, NULL); | |
245 /* hev_caller is a manual-reset event, initially nonsignaled */ | |
246 s->hev_caller = CreateEvent (NULL, TRUE, FALSE, NULL); | |
247 /* hev_unsleep is a manual-reset event, initially nonsignaled */ | |
248 s->hev_unsleep = CreateEvent (NULL, TRUE, FALSE, NULL); | |
249 | |
250 /* Now let it go */ | |
251 ResumeThread (s->hthread); | |
252 | |
253 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE; | |
254 XSETLSTREAM (obj, lstr); | |
255 return obj; | |
256 } | |
257 | |
258 static LPARAM | |
259 get_ntpipe_input_stream_param (Lstream *stream) | |
260 { | |
261 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream); | |
262 return s->user_data; | |
263 } | |
264 | |
265 static HANDLE | |
266 get_ntpipe_input_stream_waitable (Lstream *stream) | |
267 { | |
268 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream); | |
269 return s->hev_caller; | |
270 } | |
271 | |
272 static int | |
273 ntpipe_slurp_reader (Lstream *stream, unsigned char *data, size_t size) | |
274 { | |
275 /* This function must be called from the main thread only */ | |
276 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream); | |
277 | |
278 if (!s->die_p) | |
279 { | |
280 DWORD wait_result; | |
281 /* Disallow pipe read delay for the thread: we need a character ASAP */ | |
282 SetEvent (s->hev_unsleep); | |
283 | |
284 /* Check if we have a character ready. Give it a short delay, for | |
285 the thread to awake from pipe delay, just ion case*/ | |
286 wait_result = WaitForSingleObject (s->hev_caller, 2); | |
287 | |
288 /* Revert to the normal sleep behavior. */ | |
289 ResetEvent (s->hev_unsleep); | |
290 | |
291 /* If there's no byte buffered yet, give up */ | |
292 if (wait_result == WAIT_TIMEOUT) | |
293 { | |
294 errno = EAGAIN; | |
295 return -1; | |
296 } | |
297 } | |
298 | |
299 /* Reset caller unlock event now, as we've handled the pending | |
300 process output event */ | |
301 ResetEvent (s->hev_caller); | |
302 | |
303 /* It is now safe to do anything with contents of S, except for | |
304 changing s->die_p, which still should be interlocked */ | |
305 | |
306 if (s->eof_p) | |
307 return 0; | |
308 if (s->error_p || s->die_p) | |
309 return -1; | |
310 | |
311 /* Ok, there were no error neither eof - we've got a byte from the pipe */ | |
312 *(data++) = s->onebyte; | |
313 --size; | |
314 | |
315 if (size > 0) | |
316 { | |
317 DWORD bytes_available, bytes_read; | |
318 | |
319 /* If the api call fails, return at least one byte already read. | |
320 ReadFile in thread will return error */ | |
321 if (!PeekNamedPipe (s->hpipe, NULL, 0, NULL, &bytes_available, NULL)) | |
322 return 1; | |
323 | |
324 /* Fetch available bytes. The same consideration applies, so do | |
325 not check for errors. ReadFile in the thread will fail if the | |
326 next call fails. */ | |
327 ReadFile (s->hpipe, data, min (bytes_available, size), &bytes_read, NULL); | |
328 | |
329 /* Now we can unblock thread, so it attempts to read more */ | |
330 SetEvent (s->hev_thread); | |
331 return bytes_read + 1; | |
332 } | |
333 else | |
334 { | |
335 SetEvent (s->hev_thread); | |
336 return 1; | |
337 } | |
338 } | |
339 | |
340 static int | |
341 ntpipe_slurp_closer (Lstream *stream) | |
342 { | |
343 /* This function must be called from the main thread only */ | |
344 struct ntpipe_slurp_stream* s = NTPIPE_SLURP_STREAM_DATA(stream); | |
345 | |
346 /* Force thread to stop */ | |
347 InterlockedIncrement (&s->die_p); | |
348 | |
349 /* Break the pipe, in case the thread still blocked on read */ | |
350 CloseHandle (s->hpipe); | |
351 | |
352 /* Set events which could possibly block slurper */ | |
353 SetEvent (s->hev_unsleep); | |
354 SetEvent (s->hev_thread); | |
355 | |
356 /* Wait while thread terminates */ | |
357 WaitForSingleObject (s->hthread, INFINITE); | |
358 CloseHandle (s->hthread); | |
359 | |
360 /* Destroy events */ | |
361 CloseHandle (s->hev_thread); | |
362 CloseHandle (s->hev_caller); | |
363 CloseHandle (s->hev_unsleep); | |
364 | |
365 return 0; | |
366 } | |
367 | |
368 static void | |
369 init_slurp_stream (void) | |
370 { | |
371 LSTREAM_HAS_METHOD (ntpipe_slurp, reader); | |
372 LSTREAM_HAS_METHOD (ntpipe_slurp, closer); | |
373 } | |
374 | |
375 /************************************************************************/ | |
376 /* Pipe outstream - writes process input */ | |
377 /************************************************************************/ | |
378 | |
379 #define MAX_FLUSH_TIME 500 | |
380 | |
381 #define NTPIPE_SHOVE_STREAM_DATA(stream) \ | |
382 LSTREAM_TYPE_DATA (stream, ntpipe_shove) | |
383 | |
384 struct ntpipe_shove_stream | |
385 { | |
386 LPARAM user_data; /* Any user data stored in the stream object */ | |
387 HANDLE hev_thread; /* Our thread blocks on this, signaled by caller */ | |
388 /* This is an auto-reset object. */ | |
389 HANDLE hpipe; /* Pipe write end handle. */ | |
390 HANDLE hthread; /* Reader thread handle. */ | |
391 LPVOID buffer; /* Buffer being written */ | |
392 DWORD size; /* Number of bytes to write */ | |
393 DWORD die_p; /* Thread must exit ASAP if non-zero */ | |
394 DWORD idle_p; /* Non-zero if thread is waiting for job */ | |
395 BOOL error_p : 1; /* Read error other than EOF/broken pipe */ | |
396 BOOL blocking_p : 1;/* Last write attempt would cause blocking */ | |
397 }; | |
398 | |
399 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-output", lstream_ntpipe_shove, | |
400 sizeof (struct ntpipe_shove_stream)); | |
401 | |
402 static DWORD WINAPI | |
403 shove_thread (LPVOID vparam) | |
404 { | |
405 struct ntpipe_shove_stream *s = (struct ntpipe_shove_stream*) vparam; | |
406 | |
407 for (;;) | |
408 { | |
409 DWORD bytes_written; | |
410 | |
411 /* Block on event and wait for a job */ | |
412 InterlockedIncrement (&s->idle_p); | |
413 WaitForSingleObject (s->hev_thread, INFINITE); | |
414 | |
415 if (s->die_p) | |
416 break; | |
417 | |
418 /* Write passed buffer */ | |
419 if (!WriteFile (s->hpipe, s->buffer, s->size, &bytes_written, NULL) | |
420 || bytes_written != s->size) | |
421 { | |
422 s->error_p = TRUE; | |
423 InterlockedIncrement (&s->die_p); | |
424 } | |
425 | |
426 /* free it */ | |
427 LocalFree ((HLOCAL)s->buffer); | |
428 | |
429 if (s->die_p) | |
430 break; | |
431 } | |
432 | |
433 return 0; | |
434 } | |
435 | |
436 static Lisp_Object | |
437 make_ntpipe_output_stream (HANDLE hpipe, LPARAM param) | |
438 { | |
439 Lisp_Object obj; | |
440 Lstream *lstr = Lstream_new (lstream_ntpipe_shove, "w"); | |
441 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA (lstr); | |
442 DWORD thread_id_unused; | |
443 | |
444 s->die_p = 0; | |
445 s->error_p = FALSE; | |
446 s->hpipe = hpipe; | |
447 s->user_data = param; | |
448 | |
449 /* Create reader thread. This could fail, so do not | |
450 create the event until thread is created */ | |
451 s->hthread = CreateThread (NULL, 0, shove_thread, (LPVOID)s, | |
452 CREATE_SUSPENDED, &thread_id_unused); | |
453 if (s->hthread == NULL) | |
454 { | |
455 Lstream_delete (lstr); | |
456 return Qnil; | |
457 } | |
458 | |
459 /* hev_thread is an auto-reset event, initially nonsignaled */ | |
460 s->hev_thread = CreateEvent (NULL, FALSE, FALSE, NULL); | |
461 | |
462 /* Now let it go */ | |
463 ResumeThread (s->hthread); | |
464 | |
465 lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE; | |
466 XSETLSTREAM (obj, lstr); | |
467 return obj; | |
468 } | |
469 | |
470 static LPARAM | |
471 get_ntpipe_output_stream_param (Lstream *stream) | |
472 { | |
473 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); | |
474 return s->user_data; | |
475 } | |
476 | |
477 static int | |
478 ntpipe_shove_writer (Lstream *stream, const unsigned char *data, size_t size) | |
479 { | |
480 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); | |
481 | |
482 if (s->error_p) | |
483 return -1; | |
484 | |
485 s->blocking_p = !s->idle_p; | |
486 if (s->blocking_p) | |
487 return 0; | |
488 | |
489 /* Make a copy of data to be written. We intentionally avoid using | |
490 xalloc/xfree, because gnu malloc is not thread-safe */ | |
491 s->buffer = (LPVOID) LocalAlloc (LMEM_FIXED, size); | |
492 if (s->buffer == NULL) | |
493 return -1; | |
494 memcpy (s->buffer, data, size); | |
495 s->size = size; | |
496 | |
497 /* Start output */ | |
498 InterlockedDecrement (&s->idle_p); | |
499 SetEvent (s->hev_thread); | |
500 return size; | |
501 } | |
502 | |
503 static int | |
504 ntpipe_shove_was_blocked_p (Lstream *stream) | |
505 { | |
506 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); | |
507 return s->blocking_p; | |
508 } | |
509 | |
510 static int | |
511 ntpipe_shove_flusher (Lstream *stream) | |
512 { | |
513 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); | |
514 int i; | |
515 | |
516 if (s->error_p) | |
517 return -1; | |
518 | |
519 /* We do not want to be blocked forever. Instead, we wait | |
520 about 0.5 second for output to finish. If this does | |
521 not help, we just return flush failure. */ | |
522 for (i = 0; i < MAX_FLUSH_TIME / 50; ++i) | |
523 { | |
524 if (s->idle_p) | |
525 return 0; | |
526 Sleep (50); | |
527 } | |
528 return -1; | |
529 } | |
530 | |
531 static int | |
532 ntpipe_shove_closer (Lstream *stream) | |
533 { | |
534 struct ntpipe_shove_stream* s = NTPIPE_SHOVE_STREAM_DATA(stream); | |
535 | |
536 /* Force thread stop */ | |
537 InterlockedIncrement (&s->die_p); | |
538 | |
539 /* Close pipe handle, possibly breaking it */ | |
540 CloseHandle (s->hpipe); | |
541 | |
542 /* Thread will end upon unblocking */ | |
543 SetEvent (s->hev_thread); | |
544 | |
545 /* Wait while thread terminates */ | |
546 WaitForSingleObject (s->hthread, INFINITE); | |
547 CloseHandle (s->hthread); | |
548 | |
549 /* Destroy the event */ | |
550 CloseHandle (s->hev_thread); | |
551 | |
552 return 0; | |
553 } | |
554 | |
555 static void | |
556 init_shove_stream (void) | |
557 { | |
558 LSTREAM_HAS_METHOD (ntpipe_shove, writer); | |
559 LSTREAM_HAS_METHOD (ntpipe_shove, flusher); | |
560 LSTREAM_HAS_METHOD (ntpipe_shove, was_blocked_p); | |
561 LSTREAM_HAS_METHOD (ntpipe_shove, closer); | |
562 } | |
563 | |
564 /************************************************************************/ | |
565 /* Dispatch queue management */ | |
566 /************************************************************************/ | |
129 | 567 |
130 static int | 568 static int |
131 mswindows_user_event_p (struct Lisp_Event* sevt) | 569 mswindows_user_event_p (struct Lisp_Event* sevt) |
132 { | 570 { |
133 return (sevt->event_type == key_press_event | 571 return (sevt->event_type == key_press_event |
134 || sevt->event_type == button_press_event | 572 || sevt->event_type == button_press_event |
135 || sevt->event_type == button_release_event | 573 || sevt->event_type == button_release_event |
136 || sevt->event_type == dnd_drop_event); | 574 || sevt->event_type == dnd_drop_event); |
137 } | 575 } |
138 | |
139 /************************************************************************/ | |
140 /* Dispatch queue management */ | |
141 /************************************************************************/ | |
142 | 576 |
143 /* | 577 /* |
144 * Add an emacs event to the proper dispatch queue | 578 * Add an emacs event to the proper dispatch queue |
145 */ | 579 */ |
146 void | 580 void |
166 | 600 |
167 event->channel = mswindows_find_frame (hwnd); | 601 event->channel = mswindows_find_frame (hwnd); |
168 event->timestamp = GetMessageTime(); | 602 event->timestamp = GetMessageTime(); |
169 event->event_type = magic_event; | 603 event->event_type = magic_event; |
170 EVENT_MSWINDOWS_MAGIC_TYPE (event) = message; | 604 EVENT_MSWINDOWS_MAGIC_TYPE (event) = message; |
605 | |
606 mswindows_enqueue_dispatch_event (emacs_event); | |
607 } | |
608 | |
609 static void | |
610 mswindows_enqueue_process_event (struct Lisp_Process* p) | |
611 { | |
612 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); | |
613 struct Lisp_Event* event = XEVENT (emacs_event); | |
614 Lisp_Object process; | |
615 XSETPROCESS (process, p); | |
616 | |
617 event->event_type = process_event; | |
618 event->timestamp = GetTickCount (); | |
619 event->event.process.process = process; | |
171 | 620 |
172 mswindows_enqueue_dispatch_event (emacs_event); | 621 mswindows_enqueue_dispatch_event (emacs_event); |
173 } | 622 } |
174 | 623 |
175 static void | 624 static void |
305 } | 754 } |
306 previous_event = event; | 755 previous_event = event; |
307 } | 756 } |
308 return Qnil; | 757 return Qnil; |
309 } | 758 } |
310 | 759 |
311 | 760 /************************************************************************/ |
761 /* Waitable handles manipulation */ | |
762 /************************************************************************/ | |
763 static int | |
764 find_waitable_handle (HANDLE h) | |
765 { | |
766 int i; | |
767 for (i = 0; i < mswindows_waitable_count; ++i) | |
768 if (mswindows_waitable_handles[i] == h) | |
769 return i; | |
770 | |
771 return -1; | |
772 } | |
773 | |
774 static BOOL | |
775 add_waitable_handle (HANDLE h) | |
776 { | |
777 assert (find_waitable_handle (h) < 0); | |
778 if (mswindows_waitable_count == MAX_WAITABLE) | |
779 return FALSE; | |
780 | |
781 mswindows_waitable_handles [mswindows_waitable_count++] = h; | |
782 return TRUE; | |
783 } | |
784 | |
785 static void | |
786 remove_waitable_handle (HANDLE h) | |
787 { | |
788 int ix = find_waitable_handle (h); | |
789 if (ix < 0) | |
790 return; | |
791 | |
792 mswindows_waitable_handles [ix] = | |
793 mswindows_waitable_handles [--mswindows_waitable_count]; | |
794 } | |
795 | |
796 | |
312 /************************************************************************/ | 797 /************************************************************************/ |
313 /* Event pump */ | 798 /* Event pump */ |
314 /************************************************************************/ | 799 /************************************************************************/ |
315 | 800 |
316 static Lisp_Object | 801 static Lisp_Object |
453 * | 938 * |
454 * To detect this, we use a counter of active timers, and allow | 939 * To detect this, we use a counter of active timers, and allow |
455 * fetching WM_TIMER messages. Instead of trying to fetch a WM_TIMER | 940 * fetching WM_TIMER messages. Instead of trying to fetch a WM_TIMER |
456 * which will never come when there are no pending timers, which leads | 941 * which will never come when there are no pending timers, which leads |
457 * to deadlock, we simply signal an error. | 942 * to deadlock, we simply signal an error. |
458 * | |
459 * The implementation does not honor user_p by design. | |
460 */ | 943 */ |
461 static void | 944 static void |
462 mswindows_need_event_in_modal_loop (int user_p, int badly_p) | 945 mswindows_need_event_in_modal_loop (int badly_p) |
463 { | 946 { |
464 MSG msg; | 947 MSG msg; |
465 | 948 |
466 /* Check if already have one */ | 949 /* Check if already have one */ |
467 if (!NILP (mswindows_u_dispatch_event_queue) | 950 if (!NILP (mswindows_u_dispatch_event_queue) |
487 | 970 |
488 /* | 971 /* |
489 * This drains the event queue and fills up two internal queues until | 972 * This drains the event queue and fills up two internal queues until |
490 * an event of a type specified by USER_P is retrieved. | 973 * an event of a type specified by USER_P is retrieved. |
491 * | 974 * |
492 * If user_p, then the function drains until the first user event, or | |
493 * the first non-user event if there no user events. Otherwise, If | |
494 * not user_p, it does not give preference to user events. | |
495 * | |
496 * If badly_p, then the function does not return until an event is | |
497 * available. | |
498 * | |
499 * The code does not rely on MsgWaitForMultipleObjects preference for | |
500 * messages over waitable handles. | |
501 * | 975 * |
502 * Used by emacs_mswindows_event_pending_p and emacs_mswindows_next_event | 976 * Used by emacs_mswindows_event_pending_p and emacs_mswindows_next_event |
503 */ | 977 */ |
504 static void | 978 static void |
505 mswindows_need_event (int user_p, int badly_p) | 979 mswindows_need_event (int badly_p) |
506 { | 980 { |
507 int active; | 981 int active; |
508 | 982 |
509 if (mswindows_in_modal_loop) | 983 if (mswindows_in_modal_loop) |
510 { | 984 { |
511 mswindows_need_event_in_modal_loop (user_p, badly_p); | 985 mswindows_need_event_in_modal_loop (badly_p); |
512 return; | 986 return; |
513 } | 987 } |
514 | 988 |
515 /* Have to drain Windows message queue first, otherwise, we may miss | 989 /* Have to drain Windows message queue first, otherwise, we may miss |
516 quit char when called from quit_p */ | 990 quit char when called from quit_p */ |
517 mswindows_drain_windows_queue (); | 991 mswindows_drain_windows_queue (); |
518 | 992 |
519 while (NILP (mswindows_u_dispatch_event_queue) && | 993 while (NILP (mswindows_u_dispatch_event_queue) |
520 (user_p || NILP (mswindows_s_dispatch_event_queue))) | 994 && NILP (mswindows_s_dispatch_event_queue)) |
521 { | 995 { |
522 /* If we already have an event, we've got someting to return - no wait! */ | 996 /* Now try getting a message or process event */ |
523 if (!NILP (mswindows_u_dispatch_event_queue) | |
524 || !NILP (mswindows_s_dispatch_event_queue)) | |
525 badly_p = 0; | |
526 | |
527 /* Now try getting a message */ | |
528 active = MsgWaitForMultipleObjects (mswindows_waitable_count, | 997 active = MsgWaitForMultipleObjects (mswindows_waitable_count, |
529 mswindows_waitable, | 998 mswindows_waitable_handles, |
530 FALSE, badly_p ? INFINITE : 0, | 999 FALSE, badly_p ? INFINITE : 0, |
531 QS_ALLINPUT); | 1000 QS_ALLINPUT); |
532 | 1001 |
533 /* This will assert if handle being waited for becomes abandoned. | 1002 /* This will assert if handle being waited for becomes abandoned. |
534 Not the case currently tho */ | 1003 Not the case currently tho */ |
546 /* Got your message, thanks */ | 1015 /* Got your message, thanks */ |
547 mswindows_drain_windows_queue (); | 1016 mswindows_drain_windows_queue (); |
548 } | 1017 } |
549 else | 1018 else |
550 { | 1019 { |
551 /* XXX FIXME: We should do some kind of round-robin scheme to ensure fairness */ | 1020 int ix = active - WAIT_OBJECT_0; |
552 int waitable = active - WAIT_OBJECT_0; | 1021 /* First, try to find which process' ouptut has signaled */ |
553 assert(0); /* #### */ | 1022 struct Lisp_Process *p = |
1023 get_process_from_usid (HANDLE_TO_USID (mswindows_waitable_handles[ix])); | |
1024 if (p != NULL) | |
1025 { | |
1026 /* Found a signaled process input handle */ | |
1027 mswindows_enqueue_process_event (p); | |
1028 } | |
1029 else | |
1030 { | |
1031 /* None. This means that the process handle itself has signaled. | |
1032 Remove the handle from the wait vector, and make status_ntoify | |
1033 note the exited process */ | |
1034 CloseHandle (mswindows_waitable_handles[ix]); | |
1035 mswindows_waitable_handles [ix] = | |
1036 mswindows_waitable_handles [--mswindows_waitable_count]; | |
1037 kick_status_notify (); | |
1038 } | |
554 } | 1039 } |
555 } /* while */ | 1040 } /* while */ |
556 | |
557 return; | |
558 } | 1041 } |
559 | 1042 |
560 /************************************************************************/ | 1043 /************************************************************************/ |
561 /* Event generators */ | 1044 /* Event generators */ |
562 /************************************************************************/ | 1045 /************************************************************************/ |
1006 GetClientRect(hwnd, &rect); | 1489 GetClientRect(hwnd, &rect); |
1007 FRAME_PIXWIDTH(frame) = rect.right; | 1490 FRAME_PIXWIDTH(frame) = rect.right; |
1008 FRAME_PIXHEIGHT(frame) = rect.bottom; | 1491 FRAME_PIXHEIGHT(frame) = rect.bottom; |
1009 | 1492 |
1010 pixel_to_real_char_size (frame, rect.right, rect.bottom, | 1493 pixel_to_real_char_size (frame, rect.right, rect.bottom, |
1011 &MSWINDOWS_FRAME_CHARWIDTH (frame), | 1494 &FRAME_MSWINDOWS_CHARWIDTH (frame), |
1012 &MSWINDOWS_FRAME_CHARHEIGHT (frame)); | 1495 &FRAME_MSWINDOWS_CHARHEIGHT (frame)); |
1013 | 1496 |
1014 pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows); | 1497 pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows); |
1015 change_frame_size (frame, rows, columns, 1); | 1498 change_frame_size (frame, rows, columns, 1); |
1016 | 1499 |
1017 /* If we are inside frame creation, we have to apply geometric | 1500 /* If we are inside frame creation, we have to apply geometric |
1427 * Find the console that matches the supplied mswindows window handle | 1910 * Find the console that matches the supplied mswindows window handle |
1428 */ | 1911 */ |
1429 Lisp_Object | 1912 Lisp_Object |
1430 mswindows_find_console (HWND hwnd) | 1913 mswindows_find_console (HWND hwnd) |
1431 { | 1914 { |
1432 Lisp_Object concons; | 1915 /* We only support one console */ |
1433 | 1916 return XCAR (Vconsole_list); |
1434 CONSOLE_LOOP (concons) | |
1435 { | |
1436 Lisp_Object console = XCAR (concons); | |
1437 /* We only support one console so this must be it */ | |
1438 return console; | |
1439 } | |
1440 | |
1441 return Qnil; | |
1442 } | 1917 } |
1443 | 1918 |
1444 /* | 1919 /* |
1445 * Find the frame that matches the supplied mswindows window handle | 1920 * Find the frame that matches the supplied mswindows window handle |
1446 */ | 1921 */ |
1510 * emacs_mswindows_next_event() would not block. | 1985 * emacs_mswindows_next_event() would not block. |
1511 */ | 1986 */ |
1512 static int | 1987 static int |
1513 emacs_mswindows_event_pending_p (int user_p) | 1988 emacs_mswindows_event_pending_p (int user_p) |
1514 { | 1989 { |
1515 mswindows_need_event (user_p, 0); | 1990 mswindows_need_event (0); |
1516 | |
1517 return (!NILP (mswindows_u_dispatch_event_queue) | 1991 return (!NILP (mswindows_u_dispatch_event_queue) |
1518 || (!user_p && !NILP (mswindows_s_dispatch_event_queue))); | 1992 || (!user_p && !NILP (mswindows_s_dispatch_event_queue))); |
1519 } | 1993 } |
1520 | 1994 |
1521 /* | 1995 /* |
1524 static void | 1998 static void |
1525 emacs_mswindows_next_event (struct Lisp_Event *emacs_event) | 1999 emacs_mswindows_next_event (struct Lisp_Event *emacs_event) |
1526 { | 2000 { |
1527 Lisp_Object event, event2; | 2001 Lisp_Object event, event2; |
1528 | 2002 |
1529 /* Give strong preference to user events */ | 2003 mswindows_need_event (1); |
1530 mswindows_need_event (1, 1); | 2004 |
1531 | |
1532 /* XXX Copied from event-Xt.c */ | |
1533 event = mswindows_dequeue_dispatch_event (!NILP(mswindows_u_dispatch_event_queue)); | 2005 event = mswindows_dequeue_dispatch_event (!NILP(mswindows_u_dispatch_event_queue)); |
1534 XSETEVENT (event2, emacs_event); | 2006 XSETEVENT (event2, emacs_event); |
1535 Fcopy_event (event, event2); | 2007 Fcopy_event (event, event2); |
1536 Fdeallocate_event (event); | 2008 Fdeallocate_event (event); |
1537 } | 2009 } |
1591 default: | 2063 default: |
1592 assert(0); | 2064 assert(0); |
1593 } | 2065 } |
1594 } | 2066 } |
1595 | 2067 |
2068 static HANDLE | |
2069 get_process_input_waitable (struct Lisp_Process *process) | |
2070 { | |
2071 Lisp_Object instr, outstr; | |
2072 get_process_streams (process, &instr, &outstr); | |
2073 assert (!NILP (instr)); | |
2074 return get_ntpipe_input_stream_waitable (XLSTREAM (instr)); | |
2075 } | |
2076 | |
2077 static HANDLE | |
2078 get_process_handle (struct Lisp_Process *p) | |
2079 { | |
2080 /* #### The guess is that cygwin uses the same algorithm for | |
2081 computing pids: negate if less than zero, '95 case */ | |
2082 Lisp_Object process, pid; | |
2083 XSETPROCESS (process, p); | |
2084 pid = Fprocess_id (process); | |
2085 if (INTP (pid)) | |
2086 { | |
2087 HANDLE hproc; | |
2088 int ipid = XINT (pid); | |
2089 if (ipid < 0) | |
2090 ipid = -ipid; | |
2091 hproc = OpenProcess (SYNCHRONIZE, FALSE, ipid); | |
2092 /* #### This is WRONG! The process migh have ended before we got here. */ | |
2093 /* assert (hproc != NULL); */ | |
2094 /* Instead, we fake "a signaled hadle", which will trigger | |
2095 immediately upon entering the message loop */ | |
2096 if (hproc = NULL) | |
2097 hproc = CreateEvent (NULL, TRUE, TRUE, NULL); | |
2098 return hproc; | |
2099 } | |
2100 else | |
2101 return NULL; | |
2102 } | |
2103 | |
1596 static void | 2104 static void |
1597 emacs_mswindows_select_process (struct Lisp_Process *process) | 2105 emacs_mswindows_select_process (struct Lisp_Process *process) |
1598 { | 2106 { |
2107 HANDLE hev = get_process_input_waitable (process); | |
2108 HANDLE hprocess; | |
2109 | |
2110 if (!add_waitable_handle (hev)) | |
2111 error ("Too many active processes"); | |
2112 | |
2113 hprocess = get_process_handle (process); | |
2114 if (hprocess) | |
2115 { | |
2116 if (!add_waitable_handle (hprocess)) | |
2117 { | |
2118 remove_waitable_handle (hev); | |
2119 CloseHandle (hprocess); | |
2120 error ("Too many active processes"); | |
2121 } | |
2122 } | |
1599 } | 2123 } |
1600 | 2124 |
1601 static void | 2125 static void |
1602 emacs_mswindows_unselect_process (struct Lisp_Process *process) | 2126 emacs_mswindows_unselect_process (struct Lisp_Process *process) |
1603 { | 2127 { |
2128 /* Process handle is removed in the event loop as soon | |
2129 as it is signaled, so don't bother here about it */ | |
2130 HANDLE hev = get_process_input_waitable (process); | |
2131 remove_waitable_handle (hev); | |
1604 } | 2132 } |
1605 | 2133 |
1606 static void | 2134 static void |
1607 emacs_mswindows_select_console (struct console *con) | 2135 emacs_mswindows_select_console (struct console *con) |
1608 { | 2136 { |
1614 } | 2142 } |
1615 | 2143 |
1616 static void | 2144 static void |
1617 emacs_mswindows_quit_p (void) | 2145 emacs_mswindows_quit_p (void) |
1618 { | 2146 { |
1619 mswindows_need_event (1, 0); | 2147 /* Drain windows queue. This sets up number of quit |
2148 characters in in the queue */ | |
2149 mswindows_drain_windows_queue (); | |
1620 | 2150 |
1621 if (mswindows_quit_chars_count > 0) | 2151 if (mswindows_quit_chars_count > 0) |
1622 { | 2152 { |
1623 /* Yes there's a hidden one... Throw it away */ | 2153 /* Yes there's a hidden one... Throw it away */ |
1624 struct Lisp_Event match_against; | 2154 struct Lisp_Event match_against; |
1635 | 2165 |
1636 Fdeallocate_event(emacs_event); | 2166 Fdeallocate_event(emacs_event); |
1637 --mswindows_quit_chars_count; | 2167 --mswindows_quit_chars_count; |
1638 } | 2168 } |
1639 } | 2169 } |
2170 | |
2171 USID | |
2172 emacs_mswindows_create_stream_pair (void* inhandle, void* outhandle, | |
2173 Lisp_Object* instream, | |
2174 Lisp_Object* outstream, | |
2175 int flags) | |
2176 { | |
2177 /* Handles for streams */ | |
2178 HANDLE hin, hout; | |
2179 /* fds. These just stored along with the streams, and are closed in | |
2180 delete stream pair method, because we need to handle fake unices | |
2181 here. */ | |
2182 int fdi, fdo; | |
2183 | |
2184 /* Decode inhandle and outhandle. Their meaning depends on | |
2185 the process implementation being used. */ | |
2186 #if defined (HAVE_WIN32_PROCESSES) | |
2187 /* We're passed in Windows handles. That's what we like most... */ | |
2188 hin = (HANDLE) inhandle; | |
2189 hout = (HANDLE) outhandle; | |
2190 fdi = fdo = -1; | |
2191 #elif defined (HAVE_UNIX_PROCESSES) | |
2192 /* We are passed UNIX fds. This must be Cygwin. | |
2193 Fetch os handles */ | |
2194 hin = inhandle >= 0 ? get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE; | |
2195 hout = outhandle >= 0 ? get_sfhandle ((int)outhandle) : INVALID_HANDLE_VALUE; | |
2196 #else | |
2197 #error "So, WHICH kind of processes do you want?" | |
2198 #endif | |
2199 | |
2200 *instream = (hin != INVALID_HANDLE_VALUE | |
2201 ? make_ntpipe_input_stream (hin, fdi) | |
2202 : Qnil); | |
2203 | |
2204 *outstream = (hout != INVALID_HANDLE_VALUE | |
2205 ? make_ntpipe_output_stream (hout, fdo) | |
2206 : Qnil); | |
2207 | |
2208 return (NILP (*instream) ? USID_ERROR | |
2209 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (*instream)))); | |
2210 } | |
2211 | |
2212 USID | |
2213 emacs_mswindows_delete_stream_pair (Lisp_Object instream, | |
2214 Lisp_Object outstream) | |
2215 { | |
2216 /* Oh nothing special here for Win32 at all */ | |
2217 #if defined (HAVE_UNIX_PROCESSES) | |
2218 int in = (NILP(instream) ? -1 | |
2219 : get_ntpipe_input_stream_param (XLSTREAM (instream))); | |
2220 int out = (NILP(outstream) ? -1 | |
2221 : get_ntpipe_output_stream_param (XLSTREAM (outstream))); | |
2222 | |
2223 if (in >= 0) | |
2224 close (in); | |
2225 if (out != in && out >= 0) | |
2226 close (out); | |
2227 #endif | |
2228 | |
2229 return (NILP (instream) ? USID_DONTHASH | |
2230 : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream)))); | |
2231 } | |
2232 | |
1640 | 2233 |
1641 #ifndef HAVE_X_WINDOWS | 2234 #ifndef HAVE_X_WINDOWS |
1642 /* This is called from GC when a process object is about to be freed. | 2235 /* This is called from GC when a process object is about to be freed. |
1643 If we've still got pointers to it in this file, we're gonna lose hard. | 2236 If we've still got pointers to it in this file, we're gonna lose hard. |
1644 */ | 2237 */ |
1688 mswindows_event_stream->select_console_cb = emacs_mswindows_select_console; | 2281 mswindows_event_stream->select_console_cb = emacs_mswindows_select_console; |
1689 mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console; | 2282 mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console; |
1690 mswindows_event_stream->select_process_cb = emacs_mswindows_select_process; | 2283 mswindows_event_stream->select_process_cb = emacs_mswindows_select_process; |
1691 mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process; | 2284 mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process; |
1692 mswindows_event_stream->quit_p_cb = emacs_mswindows_quit_p; | 2285 mswindows_event_stream->quit_p_cb = emacs_mswindows_quit_p; |
2286 mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair; | |
2287 mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair; | |
1693 | 2288 |
1694 DEFVAR_BOOL ("mswindows-dynamic-frame-resize", &mswindows_dynamic_frame_resize /* | 2289 DEFVAR_BOOL ("mswindows-dynamic-frame-resize", &mswindows_dynamic_frame_resize /* |
1695 *Controls redrawing frame contents during mouse-drag or keyboard resize | 2290 *Controls redrawing frame contents during mouse-drag or keyboard resize |
1696 operation. When non-nil, the frame is redrawn while being resized. When | 2291 operation. When non-nil, the frame is redrawn while being resized. When |
1697 nil, frame is not redrawn, and exposed areas are filled with default | 2292 nil, frame is not redrawn, and exposed areas are filled with default |
1741 syms_of_event_mswindows (void) | 2336 syms_of_event_mswindows (void) |
1742 { | 2337 { |
1743 } | 2338 } |
1744 | 2339 |
1745 void | 2340 void |
2341 lstream_type_create_mswindows_selectable (void) | |
2342 { | |
2343 init_slurp_stream (); | |
2344 init_shove_stream (); | |
2345 } | |
2346 | |
2347 void | |
1746 init_event_mswindows_late (void) | 2348 init_event_mswindows_late (void) |
1747 { | 2349 { |
1748 event_stream = mswindows_event_stream; | 2350 event_stream = mswindows_event_stream; |
1749 | 2351 |
1750 mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE); | 2352 mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE); |