comparison src/event-msw.c @ 249:83b3d10dcba9 r20-5b23

Import from CVS: tag r20-5b23
author cvs
date Mon, 13 Aug 2007 10:19:09 +0200
parents 51092a27c943
children 677f6a0ee643
comparison
equal deleted inserted replaced
248:ad40ac2754d8 249:83b3d10dcba9
31 */ 31 */
32 32
33 #include <config.h> 33 #include <config.h>
34 #include "lisp.h" 34 #include "lisp.h"
35 35
36 #include "console-msw.h"
37
38 #ifdef HAVE_SCROLLBARS
39 # include "scrollbar-msw.h"
40 #endif
41
42 #ifdef HAVE_MENUBARS
43 # include "menubar-msw.h"
44 #endif
45
36 #include "device.h" 46 #include "device.h"
37 #include "console-msw.h"
38 #include "emacsfns.h" 47 #include "emacsfns.h"
39 #include "events.h" 48 #include "events.h"
40 #include "frame.h" 49 #include "frame.h"
41 #include "process.h" 50 #include "process.h"
42 #include "redisplay.h" 51 #include "redisplay.h"
43 #include "sysproc.h" 52 #include "sysproc.h"
44 #include "syswait.h" 53 #include "syswait.h"
45 #include "systime.h" 54 #include "systime.h"
46 55
47 #include "event-msw.h" 56 #include "events-mod.h"
57
58 #ifdef HAVE_MENUBARS
59 #define ADJR_MENUFLAG TRUE
60 #else
61 #define ADJR_MENUFLAG FALSE
62 #endif
63
64 /* Fake key modifier which is attached to a quit char event.
65 Removed upon dequeueing an event */
66 #define FAKE_MOD_QUIT 0x80
67
68 /* Timer ID used for button2 emulation */
69 #define BUTTON_2_TIMER_ID 1
70
71 /* Drag and drop event data types (subset of types in offix-types.h) */
72 #define DndFile 2
73 #define DndFiles 3
74 #define DndText 4
75
76
77 static Lisp_Object mswindows_find_frame (HWND hwnd);
78 static Lisp_Object mswindows_find_console (HWND hwnd);
79 static Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods);
80 static int mswindows_modifier_state (BYTE* keymap, int has_AltGr);
81 static void mswindows_set_chord_timer (HWND hwnd);
82 static int mswindows_button2_near_enough (POINTS p1, POINTS p2);
83 static int mswindows_current_layout_has_AltGr (void);
84
48 85
49 static struct event_stream *mswindows_event_stream; 86 static struct event_stream *mswindows_event_stream;
50 87
51 /* 88 /*
52 * Two separate queues, for efficiency, one (_u_) for user events, and 89 * Two separate queues, for efficiency, one (_u_) for user events, and
55 * one. 92 * one.
56 */ 93 */
57 static Lisp_Object mswindows_u_dispatch_event_queue, mswindows_u_dispatch_event_queue_tail; 94 static Lisp_Object mswindows_u_dispatch_event_queue, mswindows_u_dispatch_event_queue_tail;
58 static Lisp_Object mswindows_s_dispatch_event_queue, mswindows_s_dispatch_event_queue_tail; 95 static Lisp_Object mswindows_s_dispatch_event_queue, mswindows_s_dispatch_event_queue_tail;
59 96
60 /* 97 /* The number of things we can wait on */
61 * List of mswindows waitable handles. 98 #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1)
62 * Apart from the dispatch queue semaphore, all of these handles may be waited 99
63 * on multiple times in emacs_mswindows_next_event before being processed and so 100 /* List of mswindows waitable handles. */
64 * must be manual-reset events.
65 */
66 static HANDLE mswindows_waitable[MAX_WAITABLE]; 101 static HANDLE mswindows_waitable[MAX_WAITABLE];
67 102
68 /* random emacs info associated with each of the wait handles */
69 static mswindows_waitable_info_type mswindows_waitable_info[MAX_WAITABLE];
70
71 /* Count of quit chars currently in the queue */ 103 /* Count of quit chars currently in the queue */
72 /* Incremented in WM_CHAR handler in msw-proc.c 104 /* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc()
73 Decremented in mswindows_dequeue_dispatch_event() */ 105 Decremented in mswindows_dequeue_dispatch_event() */
74 int mswindows_quit_chars_count = 0; 106 int mswindows_quit_chars_count = 0;
75 107
76 /* These are Lisp integers; see DEFVARS in this file for description. */ 108 /* These are Lisp integers; see DEFVARS in this file for description. */
77 int mswindows_dynamic_frame_resize; 109 int mswindows_dynamic_frame_resize;
94 static int 126 static int
95 mswindows_user_event_p (struct Lisp_Event* sevt) 127 mswindows_user_event_p (struct Lisp_Event* sevt)
96 { 128 {
97 return (sevt->event_type == key_press_event 129 return (sevt->event_type == key_press_event
98 || sevt->event_type == button_press_event 130 || sevt->event_type == button_press_event
99 || sevt->event_type == button_release_event); 131 || sevt->event_type == button_release_event
100 } 132 || sevt->event_type == dnd_drop_event);
101 133 }
102 /* 134
135 /************************************************************************/
136 /* Dispatch queue management */
137 /************************************************************************/
138
139 /*
103 * Add an emacs event to the proper dispatch queue 140 * Add an emacs event to the proper dispatch queue
104 */ 141 */
105 void 142 void
106 mswindows_enqueue_dispatch_event (Lisp_Object event) 143 mswindows_enqueue_dispatch_event (Lisp_Object event)
107 { 144 {
113 &mswindows_s_dispatch_event_queue_tail); 150 &mswindows_s_dispatch_event_queue_tail);
114 151
115 /* This one does not go to window procedure, hence does not 152 /* This one does not go to window procedure, hence does not
116 generate XM_BUMPQUEUE magic event! */ 153 generate XM_BUMPQUEUE magic event! */
117 PostMessage (NULL, XM_BUMPQUEUE, 0, 0); 154 PostMessage (NULL, XM_BUMPQUEUE, 0, 0);
155 }
156
157 void
158 mswindows_enqueue_magic_event (HWND hwnd, UINT message)
159 {
160 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
161 struct Lisp_Event* event = XEVENT (emacs_event);
162
163 event->channel = mswindows_find_frame (hwnd);
164 event->timestamp = GetMessageTime();
165 event->event_type = magic_event;
166 EVENT_MSWINDOWS_MAGIC_TYPE (event) = message;
167
168 mswindows_enqueue_dispatch_event (emacs_event);
169 }
170
171 static void
172 mswindows_enqueue_mouse_button_event (HWND hwnd, UINT message, POINTS where, DWORD when)
173 {
174
175 /* We always use last message time, because mouse button
176 events may get delayed, and XEmacs double click
177 recognition will fail */
178
179 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
180 struct Lisp_Event* event = XEVENT(emacs_event);
181
182 event->channel = mswindows_find_frame(hwnd);
183 event->timestamp = when;
184 event->event.button.button =
185 (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 :
186 ((message==WM_RBUTTONDOWN || message==WM_RBUTTONUP) ? 3 : 2);
187 event->event.button.x = where.x;
188 event->event.button.y = where.y;
189 event->event.button.modifiers = mswindows_modifier_state (NULL, 0);
190
191 if (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN ||
192 message==WM_RBUTTONDOWN)
193 {
194 event->event_type = button_press_event;
195 SetCapture (hwnd);
196 }
197 else
198 {
199 event->event_type = button_release_event;
200 ReleaseCapture ();
201 }
202
203 mswindows_enqueue_dispatch_event (emacs_event);
204 }
205
206 static void
207 mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods)
208 {
209 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
210 struct Lisp_Event* event = XEVENT(emacs_event);
211
212 event->channel = mswindows_find_console(hwnd);
213 event->timestamp = GetMessageTime();
214 event->event_type = key_press_event;
215 event->event.key.keysym = keysym;
216 event->event.key.modifiers = mods;
217 mswindows_enqueue_dispatch_event (emacs_event);
118 } 218 }
119 219
120 /* 220 /*
121 * Remove and return the first emacs event on the dispatch queue. 221 * Remove and return the first emacs event on the dispatch queue.
122 * Give a preference to user events over non-user ones. 222 * Give a preference to user events over non-user ones.
201 } 301 }
202 previous_event = event; 302 previous_event = event;
203 } 303 }
204 return Qnil; 304 return Qnil;
205 } 305 }
306
307
308 /************************************************************************/
309 /* Event pump */
310 /************************************************************************/
206 311
207 static Lisp_Object 312 static Lisp_Object
208 mswindows_modal_loop_error_handler (Lisp_Object cons_sig_data, 313 mswindows_modal_loop_error_handler (Lisp_Object cons_sig_data,
209 Lisp_Object u_n_u_s_e_d) 314 Lisp_Object u_n_u_s_e_d)
210 { 315 {
310 mswindows_pump_outstanding_events (void) 415 mswindows_pump_outstanding_events (void)
311 { 416 {
312 /* This function can call lisp */ 417 /* This function can call lisp */
313 418
314 Lisp_Object result = Qt; 419 Lisp_Object result = Qt;
315 420 struct gcpro gcpro1;
421 GCPRO1 (result);
422
316 if (NILP(mswindows_error_caught_in_modal_loop)) 423 if (NILP(mswindows_error_caught_in_modal_loop))
317 result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil); 424 result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil);
425 UNGCPRO;
318 return result; 426 return result;
319 } 427 }
320 428
321 /* 429
322 * Find a free waitable slot
323 */
324 #if 0 /* NOTUSED */
325 static int
326 mswindows_find_free_waitable(void)
327 {
328 int i;
329 for (i=0; i<mswindows_waitable_count; i++)
330 if (mswindows_waitable_info[i].type == mswindows_waitable_type_none)
331 return i;
332 assert (mswindows_waitable_count < MAX_WAITABLE);
333 return mswindows_waitable_count++;
334 }
335 #endif
336
337 /*
338 * Create a new waitable using the type and data passed in by the info structure
339 * Returns a pointer to the info associated with the assigned waitable object
340 */
341 mswindows_waitable_info_type *
342 mswindows_add_waitable(mswindows_waitable_info_type *info)
343 {
344 int waitable;
345
346 switch (info->type)
347 {
348 case mswindows_waitable_type_dispatch:
349 assert (0); /* kkm - should not get here */
350 /* Can only have one waitable for the dispatch queue, and it's the first one */
351 assert (mswindows_waitable_count++ == 0);
352 waitable=0;
353 #if 0
354 InitializeCriticalSection(&mswindows_dispatch_crit);
355 #endif
356 assert (mswindows_waitable[0] = CreateSemaphore (NULL, 0, 0x7fffffff, NULL));
357 return mswindows_waitable_info+0;
358
359 default:
360 assert(0);
361 }
362 mswindows_waitable_info[waitable].type = info->type;
363 return mswindows_waitable_info+waitable;
364 }
365
366 /*
367 * Remove a waitable using the type and data passed in by the info structure.
368 */
369 void
370 mswindows_remove_waitable(mswindows_waitable_info_type *info)
371 {
372 int waitable;
373
374 switch (info->type)
375 {
376
377 default:
378 assert(0);
379 }
380
381 CloseHandle(mswindows_waitable[waitable]);
382 mswindows_waitable[waitable] = 0;
383 mswindows_waitable_info[waitable].type = mswindows_waitable_type_none;
384 if (waitable == mswindows_waitable_count-1)
385 --mswindows_waitable_count;
386 }
387
388 /*
389 * Callback procedure for synchronous timer messages
390 */
391 static void CALLBACK
392 mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime)
393 {
394 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
395 struct Lisp_Event *event = XEVENT (emacs_event);
396
397 if (KillTimer (NULL, id_timer))
398 --mswindows_pending_timers_count;
399
400 event->channel = Qnil;
401 event->timestamp = dwtime;
402 event->event_type = timeout_event;
403 event->event.timeout.interval_id = id_timer;
404
405 mswindows_enqueue_dispatch_event (emacs_event);
406 }
407 430
408 static void 431 static void
409 mswindows_drain_windows_queue () 432 mswindows_drain_windows_queue ()
410 { 433 {
411 MSG msg; 434 MSG msg;
521 } 544 }
522 else 545 else
523 { 546 {
524 /* XXX FIXME: We should do some kind of round-robin scheme to ensure fairness */ 547 /* XXX FIXME: We should do some kind of round-robin scheme to ensure fairness */
525 int waitable = active - WAIT_OBJECT_0; 548 int waitable = active - WAIT_OBJECT_0;
526 mswindows_waitable_info_type *info = mswindows_waitable_info + waitable; 549 assert(0); /* #### */
527 550 }
528 switch (info->type) 551 } /* while */
552
553 return;
554 }
555
556 /************************************************************************/
557 /* Event generators */
558 /************************************************************************/
559
560 /*
561 * Callback procedure for synchronous timer messages
562 */
563 static void CALLBACK
564 mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime)
565 {
566 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
567 struct Lisp_Event *event = XEVENT (emacs_event);
568
569 if (KillTimer (NULL, id_timer))
570 --mswindows_pending_timers_count;
571
572 event->channel = Qnil;
573 event->timestamp = dwtime;
574 event->event_type = timeout_event;
575 event->event.timeout.interval_id = id_timer;
576
577 mswindows_enqueue_dispatch_event (emacs_event);
578 }
579
580 /*
581 * Callback procedure for dde messages
582 */
583 HDDEDATA CALLBACK
584 mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv,
585 HSZ hszTopic, HSZ hszItem, HDDEDATA hdata,
586 DWORD dwData1, DWORD dwData2)
587 {
588 switch (uType)
589 {
590 case XTYP_CONNECT:
591 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
592 return (HDDEDATA)TRUE;
593 return (HDDEDATA)FALSE;
594
595 case XTYP_WILDCONNECT:
596 {
597 /* We only support one {service,topic} pair */
598 HSZPAIR pairs[2] = {
599 { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } };
600
601 if (!(hszItem || DdeCmpStringHandles (hszItem, mswindows_dde_service)) &&
602 !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)));
603 return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs,
604 sizeof (pairs), 0L, 0, uFmt, 0));
605 }
606 return (HDDEDATA)NULL;
607
608 case XTYP_EXECUTE:
609 if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
610 {
611 DWORD len = DdeGetData (hdata, NULL, 0, 0);
612 char *cmd = alloca (len+1);
613 char *end;
614 Lisp_Object l_dndlist;
615 Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
616 struct Lisp_Event *event = XEVENT (emacs_event);
617
618 DdeGetData (hdata, cmd, len, 0);
619 cmd[len] = '\0';
620 DdeFreeDataHandle (hdata);
621
622 /* Check syntax & that it's an [Open("foo")] command */
623 /* #### Ought to be generalised and accept some other commands */
624 if (*cmd == '[')
625 cmd++;
626 if (strnicmp (cmd, MSWINDOWS_DDE_ITEM_OPEN,
627 strlen (MSWINDOWS_DDE_ITEM_OPEN)))
628 return DDE_FNOTPROCESSED;
629 cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN);
630 while (*cmd==' ')
631 cmd++;
632 if (*cmd!='(' || *(cmd+1)!='\"')
633 return DDE_FNOTPROCESSED;
634 end = (cmd+=2);
635 while (*end && *end!='\"')
636 end++;
637 if (!*end)
638 return DDE_FNOTPROCESSED;
639 *end = '\0';
640 if (*(++end)!=')')
641 return DDE_FNOTPROCESSED;
642 if (*(++end)==']')
643 end++;
644 if (*end)
645 return DDE_FNOTPROCESSED;
646
647 l_dndlist = make_ext_string (cmd, strlen(cmd), FORMAT_FILENAME);
648
649 event->channel = Qnil;
650 event->timestamp = GetTickCount();
651 event->event_type = dnd_drop_event;
652 event->event.dnd_drop.button = 0;
653 event->event.dnd_drop.modifiers = 0;
654 event->event.dnd_drop.x = -1;
655 event->event.dnd_drop.y = -1;
656 event->event.dnd_drop.data = Fcons (make_int (DndFile),
657 Fcons (l_dndlist, Qnil));
658 mswindows_enqueue_dispatch_event (emacs_event);
659
660 return (HDDEDATA) DDE_FACK;
661 }
662 DdeFreeDataHandle (hdata);
663 return (HDDEDATA) DDE_FNOTPROCESSED;
664
665 default:
666 return (HDDEDATA) NULL;
667 }
668
669 }
670
671 /*
672 * The windows procedure for the window class XEMACS_CLASS
673 */
674 LRESULT WINAPI
675 mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
676 {
677 /* Note: Remember to initialise emacs_event and event before use.
678 This code calls code that can GC. You must GCPRO before calling such code. */
679 Lisp_Object emacs_event = Qnil;
680 Lisp_Object fobj = Qnil;
681
682 struct Lisp_Event *event;
683 struct frame *frame;
684 struct mswindows_frame* msframe;
685
686 switch (message)
687 {
688 case WM_ERASEBKGND:
689 /* Erase background only during non-dynamic sizing */
690 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
691 if (msframe->sizing && !mswindows_dynamic_frame_resize)
692 goto defproc;
693 return 1;
694
695 case WM_CLOSE:
696 fobj = mswindows_find_frame (hwnd);
697 enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt));
698 mswindows_enqueue_magic_event (hwnd, XM_BUMPQUEUE);
699 break;
700
701 case WM_KEYDOWN:
702 case WM_SYSKEYDOWN:
703 {
704 BYTE keymap[256];
705 int has_AltGr = mswindows_current_layout_has_AltGr ();
706 int mods;
707 Lisp_Object keysym;
708
709 GetKeyboardState (keymap);
710 mods = mswindows_modifier_state (keymap, has_AltGr);
711
712 /* Handle those keys that TranslateMessage won't generate a WM_CHAR for */
713 if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam, mods)))
714 mswindows_enqueue_keypress_event (hwnd, keysym, mods);
715 else
716 {
717 int quit_ch = CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd)));
718 BYTE keymap_orig[256];
719 MSG msg = { hwnd, message, wParam, lParam, GetMessageTime(), (GetMessagePos()) };
720 memcpy (keymap_orig, keymap, 256);
721
722 /* Clear control and alt modifiers out of the keymap */
723 keymap [VK_RCONTROL] = 0;
724 keymap [VK_LMENU] = 0;
725 if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80) || !(keymap [VK_RMENU] & 0x80))
726 {
727 keymap [VK_LCONTROL] = 0;
728 keymap [VK_CONTROL] = 0;
729 keymap [VK_RMENU] = 0;
730 keymap [VK_MENU] = 0;
731 }
732 SetKeyboardState (keymap);
733
734 /* Have some WM_[SYS]CHARS in the queue */
735 TranslateMessage (&msg);
736
737 while (PeekMessage (&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)
738 ||PeekMessage (&msg, hwnd, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE))
739 {
740 int ch = msg.wParam;
741 /* CH is a character code for the key:
742 'C' for Shift+C and Ctrl+Shift+C
743 'c' for c and Ctrl+c */
744
745 /* #### If locale is not C, US or other latin-1,
746 isalpha() maybe not what do we mean */
747
748 /* XEmacs doesn't seem to like Shift on non-alpha keys */
749 if (!isalpha(ch))
750 mods &= ~MOD_SHIFT;
751
752 /* Un-capitalise alpha control keys */
753 if ((mods & MOD_CONTROL) && isalpha(ch))
754 ch |= ('A' ^ 'a');
755
756 /* If a quit char with no modifiers other than control and
757 shift, then mark it with a fake modifier, which is removed
758 upon dequeueing the event */
759 /* #### This might also not withstand localization, if
760 quit character is not a latin-1 symbol */
761 if (((quit_ch < ' ' && (mods & MOD_CONTROL) && quit_ch + 'a' - 1 == ch)
762 || (quit_ch >= ' ' && !(mods & MOD_CONTROL) && quit_ch == ch))
763 && ((mods & ~(MOD_CONTROL | MOD_SHIFT)) == 0))
764 {
765 mods |= FAKE_MOD_QUIT;
766 ++mswindows_quit_chars_count;
767 }
768
769 mswindows_enqueue_keypress_event (hwnd, make_char(ch), mods);
770 } /* while */
771 SetKeyboardState (keymap_orig);
772 } /* else */
773 }
774 goto defproc;
775
776 case WM_MBUTTONDOWN:
777 case WM_MBUTTONUP:
778 /* Real middle mouse button has nothing to do with emulated one:
779 if one wants to exercise fingers playing chords on the mouse,
780 he is allowed to do that! */
781 mswindows_enqueue_mouse_button_event (hwnd, message,
782 MAKEPOINTS (lParam), GetMessageTime());
783 break;
784
785 case WM_LBUTTONUP:
786 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
787 msframe->last_click_time = GetMessageTime();
788
789 KillTimer (hwnd, BUTTON_2_TIMER_ID);
790 msframe->button2_need_lbutton = 0;
791 if (msframe->ignore_next_lbutton_up)
792 {
793 msframe->ignore_next_lbutton_up = 0;
794 }
795 else if (msframe->button2_is_down)
796 {
797 msframe->button2_is_down = 0;
798 msframe->ignore_next_rbutton_up = 1;
799 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
800 MAKEPOINTS (lParam), GetMessageTime());
801 }
802 else
803 {
804 if (msframe->button2_need_rbutton)
529 { 805 {
530 /* XXX FIXME: Should enque subprocess event here so that it is not lost */ 806 msframe->button2_need_rbutton = 0;
531 default: 807 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
532 assert(0); 808 MAKEPOINTS (lParam), GetMessageTime());
809 }
810 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP,
811 MAKEPOINTS (lParam), GetMessageTime());
812 }
813 break;
814
815 case WM_RBUTTONUP:
816 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
817 msframe->last_click_time = GetMessageTime();
818
819 KillTimer (hwnd, BUTTON_2_TIMER_ID);
820 msframe->button2_need_rbutton = 0;
821 if (msframe->ignore_next_rbutton_up)
822 {
823 msframe->ignore_next_rbutton_up = 0;
824 }
825 else if (msframe->button2_is_down)
826 {
827 msframe->button2_is_down = 0;
828 msframe->ignore_next_lbutton_up = 1;
829 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP,
830 MAKEPOINTS (lParam), GetMessageTime());
831 }
832 else
833 {
834 if (msframe->button2_need_lbutton)
835 {
836 msframe->button2_need_lbutton = 0;
837 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
838 MAKEPOINTS (lParam), GetMessageTime());
839 }
840 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP,
841 MAKEPOINTS (lParam), GetMessageTime());
842 }
843 break;
844
845 case WM_LBUTTONDOWN:
846 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
847
848 if (msframe->button2_need_lbutton)
849 {
850 KillTimer (hwnd, BUTTON_2_TIMER_ID);
851 msframe->button2_need_lbutton = 0;
852 msframe->button2_need_rbutton = 0;
853 if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
854 {
855 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
856 MAKEPOINTS (lParam), GetMessageTime());
857 msframe->button2_is_down = 1;
858 }
859 else
860 {
861 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
862 msframe->last_click_point, msframe->last_click_time);
863 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
864 MAKEPOINTS (lParam), GetMessageTime());
533 } 865 }
534 } 866 }
535 } /* while */ 867 else
536 868 {
537 return; 869 mswindows_set_chord_timer (hwnd);
538 } 870 msframe->button2_need_rbutton = 1;
871 msframe->last_click_point = MAKEPOINTS (lParam);
872 }
873 msframe->last_click_time = GetMessageTime();
874 break;
875
876 case WM_RBUTTONDOWN:
877 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
878
879 if (msframe->button2_need_rbutton)
880 {
881 KillTimer (hwnd, BUTTON_2_TIMER_ID);
882 msframe->button2_need_lbutton = 0;
883 msframe->button2_need_rbutton = 0;
884 if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam)))
885 {
886 mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN,
887 MAKEPOINTS (lParam), GetMessageTime());
888 msframe->button2_is_down = 1;
889 }
890 else
891 {
892 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
893 msframe->last_click_point, msframe->last_click_time);
894 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
895 MAKEPOINTS (lParam), GetMessageTime());
896 }
897 }
898 else
899 {
900 mswindows_set_chord_timer (hwnd);
901 msframe->button2_need_lbutton = 1;
902 msframe->last_click_point = MAKEPOINTS (lParam);
903 }
904 msframe->last_click_time = GetMessageTime();
905 break;
906
907 case WM_TIMER:
908 if (wParam == BUTTON_2_TIMER_ID)
909 {
910 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
911 KillTimer (hwnd, BUTTON_2_TIMER_ID);
912
913 if (msframe->button2_need_lbutton)
914 {
915 msframe->button2_need_lbutton = 0;
916 mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN,
917 msframe->last_click_point, msframe->last_click_time);
918 }
919 else if (msframe->button2_need_rbutton)
920 {
921 msframe->button2_need_rbutton = 0;
922 mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN,
923 msframe->last_click_point, msframe->last_click_time);
924 }
925 }
926 else
927 assert ("Spurious timer fired" == 0);
928 break;
929
930 case WM_MOUSEMOVE:
931 /* Optimization: don't report mouse movement while size is changind */
932 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
933 if (!msframe->sizing)
934 {
935 /* When waiting for the second mouse button to finish
936 button2 emulation, and have moved too far, just pretend
937 as if timer has expired. This impoves drag-select feedback */
938 if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton)
939 && !mswindows_button2_near_enough (msframe->last_click_point,
940 MAKEPOINTS (lParam)))
941 {
942 KillTimer (hwnd, BUTTON_2_TIMER_ID);
943 SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0);
944 }
945
946 emacs_event = Fmake_event (Qnil, Qnil);
947 event = XEVENT(emacs_event);
948
949 event->channel = mswindows_find_frame(hwnd);
950 event->timestamp = GetMessageTime();
951 event->event_type = pointer_motion_event;
952 event->event.motion.x = MAKEPOINTS(lParam).x;
953 event->event.motion.y = MAKEPOINTS(lParam).y;
954 event->event.motion.modifiers = mswindows_modifier_state (NULL, 0);
955
956 mswindows_enqueue_dispatch_event (emacs_event);
957 }
958 break;
959
960 case WM_PAINT:
961 {
962 PAINTSTRUCT paintStruct;
963
964 frame = XFRAME (mswindows_find_frame (hwnd));
965
966 BeginPaint (hwnd, &paintStruct);
967 mswindows_redraw_exposed_area (frame,
968 paintStruct.rcPaint.left, paintStruct.rcPaint.top,
969 paintStruct.rcPaint.right, paintStruct.rcPaint.bottom);
970 EndPaint (hwnd, &paintStruct);
971 }
972 break;
973
974 case WM_SIZE:
975 /* We only care about this message if our size has really changed */
976 if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
977 {
978 RECT rect;
979 int columns, rows;
980
981 fobj = mswindows_find_frame (hwnd);
982 frame = XFRAME (fobj);
983 msframe = FRAME_MSWINDOWS_DATA (frame);
984
985 /* We cannot handle frame map and unmap hooks right in
986 this routine, because these may throw. We queue
987 magic events to run these hooks instead - kkm */
988
989 if (wParam==SIZE_MINIMIZED)
990 {
991 /* Iconified */
992 FRAME_VISIBLE_P (frame) = 0;
993 mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME);
994 Fframe_iconified_p (fobj);
995 }
996 else
997 {
998 int was_visible = FRAME_VISIBLE_P (frame);
999 if (!msframe->sizing && !was_visible)
1000 mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME);
1001
1002 GetClientRect(hwnd, &rect);
1003 FRAME_VISIBLE_P(frame) = 1;
1004 FRAME_PIXWIDTH(frame) = rect.right;
1005 FRAME_PIXHEIGHT(frame) = rect.bottom;
1006 pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows);
1007 change_frame_size (frame, rows, columns, 1);
1008
1009 if (msframe->sizing && mswindows_dynamic_frame_resize)
1010 redisplay ();
1011 }
1012 }
1013 break;
1014
1015 /* Misc magic events which only require that the frame be identified */
1016 case WM_SETFOCUS:
1017 case WM_KILLFOCUS:
1018 mswindows_enqueue_magic_event (hwnd, message);
1019 break;
1020
1021 case WM_WINDOWPOSCHANGING:
1022 {
1023 WINDOWPOS *wp = (LPWINDOWPOS) lParam;
1024 WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) };
1025 GetWindowPlacement(hwnd, &wpl);
1026
1027 /* Only interested if size is changing and we're not being iconified */
1028 if ((wpl.showCmd != SW_SHOWMINIMIZED) && !(wp->flags & SWP_NOSIZE))
1029 {
1030 RECT ncsize = { 0, 0, 0, 0 };
1031 int pixwidth, pixheight;
1032 AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE),
1033 GetMenu(hwnd) != NULL,
1034 GetWindowLong (hwnd, GWL_EXSTYLE));
1035
1036 round_size_to_char (XFRAME (mswindows_find_frame (hwnd)),
1037 wp->cx - (ncsize.right - ncsize.left),
1038 wp->cy - (ncsize.bottom - ncsize.top),
1039 &pixwidth, &pixheight);
1040
1041 /* Convert client sizes to window sizes */
1042 pixwidth += (ncsize.right - ncsize.left);
1043 pixheight += (ncsize.bottom - ncsize.top);
1044
1045 if (wpl.showCmd != SW_SHOWMAXIMIZED)
1046 {
1047 /* Adjust so that the bottom or right doesn't move if it's
1048 * the top or left that's being changed */
1049 RECT rect;
1050 GetWindowRect (hwnd, &rect);
1051
1052 if (rect.left != wp->x)
1053 wp->x += wp->cx - pixwidth;
1054 if (rect.top != wp->y)
1055 wp->y += wp->cy - pixheight;
1056 }
1057
1058 wp->cx = pixwidth;
1059 wp->cy = pixheight;
1060 }
1061 }
1062 break;
1063
1064 case WM_ENTERSIZEMOVE:
1065 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1066 msframe->sizing = 1;
1067 return 0;
1068
1069 case WM_EXITSIZEMOVE:
1070 msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd)));
1071 msframe->sizing = 0;
1072 /* Queue noop event */
1073 mswindows_enqueue_magic_event (hwnd, XM_BUMPQUEUE);
1074 return 0;
1075
1076 #ifdef HAVE_SCROLLBARS
1077 case WM_VSCROLL:
1078 case WM_HSCROLL:
1079 {
1080 /* Direction of scroll is determined by scrollbar instance. */
1081 int code = (int) LOWORD(wParam);
1082 int pos = (short int) HIWORD(wParam);
1083 HWND hwndScrollBar = (HWND) lParam;
1084 struct gcpro gcpro1, gcpro2;
1085
1086 mswindows_handle_scrollbar_event (hwndScrollBar, code, pos);
1087 GCPRO2 (emacs_event, fobj);
1088 if (UNBOUNDP(mswindows_pump_outstanding_events())) /* Can GC */
1089 {
1090 /* Error during event pumping - cancel scroll */
1091 SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
1092 }
1093 UNGCPRO;
1094 break;
1095 }
1096 #endif
1097
1098 #ifdef HAVE_MENUBARS
1099 case WM_INITMENU:
1100 if (UNBOUNDP (mswindows_handle_wm_initmenu (
1101 (HMENU) wParam,
1102 XFRAME (mswindows_find_frame (hwnd)))))
1103 SendMessage (hwnd, WM_CANCELMODE, 0, 0);
1104 break;
1105
1106 case WM_INITMENUPOPUP:
1107 if (!HIWORD(lParam))
1108 {
1109 if (UNBOUNDP (mswindows_handle_wm_initmenupopup (
1110 (HMENU) wParam,
1111 XFRAME (mswindows_find_frame (hwnd)))))
1112 SendMessage (hwnd, WM_CANCELMODE, 0, 0);
1113 }
1114 break;
1115
1116 case WM_EXITMENULOOP:
1117 if (UNBOUNDP (mswindows_handle_wm_exitmenuloop (
1118 XFRAME (mswindows_find_frame (hwnd)))))
1119 SendMessage (hwnd, WM_CANCELMODE, 0, 0);
1120 break;
1121
1122 #endif /* HAVE_MENUBARS */
1123
1124 case WM_COMMAND:
1125 {
1126 WORD id = LOWORD (wParam);
1127 frame = XFRAME (mswindows_find_frame (hwnd));
1128
1129 #ifdef HAVE_MENUBARS
1130 if (!NILP (mswindows_handle_wm_command (frame, id)))
1131 break;
1132 #endif
1133
1134 #ifdef HAVE_TOOLBARS
1135 O Toolbar Implementor, this place may have something for you!;
1136 #endif
1137
1138 /* Bite me - a spurious command. No abort(), for safety */
1139 /* #### Perhaps, this message should be changed */
1140 error ("Cannot decode command. Tell kkm he's a parallelogramm, if you know"
1141 " what does that mean!");
1142 }
1143 break;
1144
1145 case WM_DROPFILES: /* implementation ripped-off from event-Xt.c */
1146 {
1147 UINT filecount, i, len;
1148 POINT point;
1149 char filename[MAX_PATH];
1150 Lisp_Object l_type, l_dndlist = Qnil, l_item;
1151
1152 emacs_event = Fmake_event (Qnil, Qnil);
1153 event = XEVENT(emacs_event);
1154
1155 if (!DragQueryPoint ((HANDLE) wParam, &point))
1156 point.x = point.y = -1; /* outside client area */
1157
1158 filecount = DragQueryFile ((HANDLE) wParam, -1, NULL, 0);
1159 if (filecount == 1)
1160 {
1161 l_type = make_int (DndFile);
1162 len = DragQueryFile ((HANDLE) wParam, 0, filename, MAX_PATH);
1163 l_dndlist = make_ext_string (filename, len, FORMAT_FILENAME);
1164 }
1165 else
1166 {
1167 l_type = make_int (DndFiles);
1168 for (i=0; i<filecount; i++)
1169 {
1170 len = DragQueryFile ((HANDLE) wParam, i, filename, MAX_PATH);
1171 l_item = make_ext_string (filename, len, FORMAT_FILENAME);
1172 l_dndlist = Fcons (l_item, l_dndlist); /* reverse order */
1173 }
1174 }
1175 DragFinish ((HANDLE) wParam);
1176
1177 event->channel = mswindows_find_frame(hwnd);
1178 event->timestamp = GetMessageTime();
1179 event->event_type = dnd_drop_event;
1180 event->event.dnd_drop.button = 1; /* #### Should try harder */
1181 event->event.dnd_drop.modifiers = mswindows_modifier_state (NULL, 0);
1182 event->event.dnd_drop.x = point.x;
1183 event->event.dnd_drop.y = point.y;
1184 event->event.dnd_drop.data = Fcons (l_type, Fcons (l_dndlist, Qnil));
1185
1186 mswindows_enqueue_dispatch_event (emacs_event);
1187 }
1188 break;
1189
1190 defproc:
1191 default:
1192 return DefWindowProc (hwnd, message, wParam, lParam);
1193 }
1194 return (0);
1195 }
1196
1197
1198 /************************************************************************/
1199 /* keyboard, mouse & other helpers for the windows procedure */
1200 /************************************************************************/
1201 static void
1202 mswindows_set_chord_timer (HWND hwnd)
1203 {
1204 int interval;
1205
1206 /* We get half system threshold as it seems to
1207 long before drag-selection is shown */
1208 if (mswindows_button2_chord_time <= 0)
1209 interval = GetDoubleClickTime () / 2;
1210 else
1211 interval = mswindows_button2_chord_time;
1212
1213 SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0);
1214 }
1215
1216 static int
1217 mswindows_button2_near_enough (POINTS p1, POINTS p2)
1218 {
1219 int dx, dy;
1220 if (mswindows_button2_max_skew_x <= 0)
1221 dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2;
1222 else
1223 dx = mswindows_button2_max_skew_x;
1224
1225 if (mswindows_button2_max_skew_y <= 0)
1226 dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2;
1227 else
1228 dy = mswindows_button2_max_skew_y;
1229
1230 return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy;
1231 }
1232
1233 static int
1234 mswindows_current_layout_has_AltGr (void)
1235 {
1236 /* This simple caching mechanism saves 10% of CPU
1237 time when a key typed at autorepeat rate of 30 cps! */
1238 static HKL last_hkl = 0;
1239 static int last_hkl_has_AltGr;
1240
1241 HKL current_hkl = GetKeyboardLayout (0);
1242 if (current_hkl != last_hkl)
1243 {
1244 TCHAR c;
1245 last_hkl_has_AltGr = 0;
1246 /* In this loop, we query whether a character requires
1247 AltGr to be down to generate it. If at least such one
1248 found, this means that the layout does regard AltGr */
1249 for (c = ' '; c <= 0xFFU && c != 0 && !last_hkl_has_AltGr; ++c)
1250 if (HIBYTE (VkKeyScan (c)) == 6)
1251 last_hkl_has_AltGr = 1;
1252 last_hkl = current_hkl;
1253 }
1254 return last_hkl_has_AltGr;
1255 }
1256
1257
1258 /* Returns the state of the modifier keys in the format expected by the
1259 * Lisp_Event key_data, button_data and motion_data modifiers member */
1260 int mswindows_modifier_state (BYTE* keymap, int has_AltGr)
1261 {
1262 int mods = 0;
1263
1264 if (keymap == NULL)
1265 {
1266 keymap = (BYTE*) alloca(256);
1267 GetKeyboardState (keymap);
1268 has_AltGr = mswindows_current_layout_has_AltGr ();
1269 }
1270
1271 if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80))
1272 {
1273 mods |= (keymap [VK_LMENU] & 0x80) ? MOD_META : 0;
1274 mods |= (keymap [VK_RCONTROL] & 0x80) ? MOD_CONTROL : 0;
1275 }
1276 else
1277 {
1278 mods |= (keymap [VK_MENU] & 0x80) ? MOD_META : 0;
1279 mods |= (keymap [VK_CONTROL] & 0x80) ? MOD_CONTROL : 0;
1280 }
1281
1282 mods |= (keymap [VK_SHIFT] & 0x80) ? MOD_SHIFT : 0;
1283
1284 return mods;
1285 }
1286
1287 /*
1288 * Translate a mswindows virtual key to a keysym.
1289 * Only returns non-Qnil for keys that don't generate WM_CHAR messages
1290 * or whose ASCII codes (like space) xemacs doesn't like.
1291 * Virtual key values are defined in winresrc.h
1292 * XXX I'm not sure that KEYSYM("name") is the best thing to use here.
1293 */
1294 Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods)
1295 {
1296 switch (mswindows_key)
1297 {
1298 /* First the predefined ones */
1299 case VK_BACK: return QKbackspace;
1300 case VK_TAB: return QKtab;
1301 case '\n': return QKlinefeed; /* No VK_LINEFEED in winresrc.h */
1302 case VK_RETURN: return QKreturn;
1303 case VK_ESCAPE: return QKescape;
1304 case VK_SPACE: return QKspace;
1305 case VK_DELETE: return QKdelete;
1306
1307 /* The rest */
1308 case VK_CLEAR: return KEYSYM ("clear"); /* Should do ^L ? */
1309 case VK_PRIOR: return KEYSYM ("prior");
1310 case VK_NEXT: return KEYSYM ("next");
1311 case VK_END: return KEYSYM ("end");
1312 case VK_HOME: return KEYSYM ("home");
1313 case VK_LEFT: return KEYSYM ("left");
1314 case VK_UP: return KEYSYM ("up");
1315 case VK_RIGHT: return KEYSYM ("right");
1316 case VK_DOWN: return KEYSYM ("down");
1317 case VK_SELECT: return KEYSYM ("select");
1318 case VK_PRINT: return KEYSYM ("print");
1319 case VK_EXECUTE: return KEYSYM ("execute");
1320 case VK_SNAPSHOT: return KEYSYM ("print");
1321 case VK_INSERT: return KEYSYM ("insert");
1322 case VK_HELP: return KEYSYM ("help");
1323 #if 0 /* XXX What are these supposed to do? */
1324 case VK_LWIN return KEYSYM ("");
1325 case VK_RWIN return KEYSYM ("");
1326 #endif
1327 case VK_APPS: return KEYSYM ("menu");
1328 case VK_F1: return KEYSYM ("f1");
1329 case VK_F2: return KEYSYM ("f2");
1330 case VK_F3: return KEYSYM ("f3");
1331 case VK_F4: return KEYSYM ("f4");
1332 case VK_F5: return KEYSYM ("f5");
1333 case VK_F6: return KEYSYM ("f6");
1334 case VK_F7: return KEYSYM ("f7");
1335 case VK_F8: return KEYSYM ("f8");
1336 case VK_F9: return KEYSYM ("f9");
1337 case VK_F10: return KEYSYM ("f10");
1338 case VK_F11: return KEYSYM ("f11");
1339 case VK_F12: return KEYSYM ("f12");
1340 case VK_F13: return KEYSYM ("f13");
1341 case VK_F14: return KEYSYM ("f14");
1342 case VK_F15: return KEYSYM ("f15");
1343 case VK_F16: return KEYSYM ("f16");
1344 case VK_F17: return KEYSYM ("f17");
1345 case VK_F18: return KEYSYM ("f18");
1346 case VK_F19: return KEYSYM ("f19");
1347 case VK_F20: return KEYSYM ("f20");
1348 case VK_F21: return KEYSYM ("f21");
1349 case VK_F22: return KEYSYM ("f22");
1350 case VK_F23: return KEYSYM ("f23");
1351 case VK_F24: return KEYSYM ("f24");
1352 }
1353 return Qnil;
1354 }
1355
1356 /*
1357 * Find the console that matches the supplied mswindows window handle
1358 */
1359 Lisp_Object
1360 mswindows_find_console (HWND hwnd)
1361 {
1362 Lisp_Object concons;
1363
1364 CONSOLE_LOOP (concons)
1365 {
1366 Lisp_Object console = XCAR (concons);
1367 /* We only support one console so this must be it */
1368 return console;
1369 }
1370
1371 return Qnil;
1372 }
1373
1374 /*
1375 * Find the frame that matches the supplied mswindows window handle
1376 */
1377 static Lisp_Object
1378 mswindows_find_frame (HWND hwnd)
1379 {
1380 return (Lisp_Object) GetWindowLong (hwnd, XWL_FRAMEOBJ);
1381 }
1382
539 1383
540 1384
541 /************************************************************************/ 1385 /************************************************************************/
542 /* methods */ 1386 /* methods */
543 /************************************************************************/ 1387 /************************************************************************/
552 milliseconds = EMACS_SECS (thyme) * 1000 + 1396 milliseconds = EMACS_SECS (thyme) * 1000 +
553 (EMACS_USECS (thyme) + 500) / 1000; 1397 (EMACS_USECS (thyme) + 500) / 1000;
554 if (milliseconds < 1) 1398 if (milliseconds < 1)
555 milliseconds = 1; 1399 milliseconds = 1;
556 ++mswindows_pending_timers_count; 1400 ++mswindows_pending_timers_count;
557 return SetTimer (NULL, 0, milliseconds, mswindows_wm_timer_callback); 1401 return SetTimer (NULL, 0, milliseconds,
1402 (TIMERPROC) mswindows_wm_timer_callback);
558 } 1403 }
559 1404
560 static void 1405 static void
561 emacs_mswindows_remove_timeout (int id) 1406 emacs_mswindows_remove_timeout (int id)
562 { 1407 {
615 * Handle a magic event off the dispatch queue. 1460 * Handle a magic event off the dispatch queue.
616 */ 1461 */
617 static void 1462 static void
618 emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event) 1463 emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event)
619 { 1464 {
620 #if 0
621 stderr_out("magic %x, (%d,%d), (%d,%d)\n",
622 EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event),
623 rect->left, rect->top, rect->right, rect->bottom);
624 #endif
625 switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)) 1465 switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event))
626 { 1466 {
627 case WM_SETFOCUS: 1467 case WM_SETFOCUS:
628 case WM_KILLFOCUS: 1468 case WM_KILLFOCUS:
629 { 1469 {
660 Qmap_frame_hook : Qunmap_frame_hook, 1500 Qmap_frame_hook : Qunmap_frame_hook,
661 1, frame); 1501 1, frame);
662 } 1502 }
663 break; 1503 break;
664 1504
665 /* XXX What about Enter & Leave */ 1505 /* #### What about Enter & Leave */
666 #if 0 1506 #if 0
667 va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook : 1507 va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook :
668 Qmouse_leave_frame_hook, 1, frame); 1508 Qmouse_leave_frame_hook, 1, frame);
669 #endif 1509 #endif
670 1510