Mercurial > hg > xemacs-beta
diff src/event-msw.c @ 223:2c611d1463a6 r20-4b10
Import from CVS: tag r20-4b10
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:10:54 +0200 |
parents | 262b8bb4a523 |
children | 12579d965149 |
line wrap: on
line diff
--- a/src/event-msw.c Mon Aug 13 10:10:03 2007 +0200 +++ b/src/event-msw.c Mon Aug 13 10:10:54 2007 +0200 @@ -46,8 +46,15 @@ #include "event-msw.h" static struct event_stream *mswindows_event_stream; -static Lisp_Object mswindows_dispatch_event_queue, mswindows_dispatch_event_queue_tail; -CRITICAL_SECTION mswindows_dispatch_crit; + +/* + * Two separate queues, for efficiency, one (_u_) for user events, and + * another (_s_) for non-user ones. We always return events out of the + * first one until it is empty and only then proceed with the second + * one. + */ +static Lisp_Object mswindows_u_dispatch_event_queue, mswindows_u_dispatch_event_queue_tail; +static Lisp_Object mswindows_s_dispatch_event_queue, mswindows_s_dispatch_event_queue_tail; /* * List of mswindows waitable handles. @@ -60,74 +67,132 @@ /* random emacs info associated with each of the wait handles */ static mswindows_waitable_info_type mswindows_waitable_info[MAX_WAITABLE]; +/* Count of quit chars currently in the queue */ +/* Incremented in WM_CHAR handler in msw-proc.c + Decremented in mswindows_dequeue_dispatch_event() */ +int mswindows_quit_chars_count = 0; + +/* These are Lisp integers; see DEFVARS in this file for description. */ +int mswindows_dynamic_frame_resize; +int mswindows_num_mouse_buttons; +int mswindows_button2_max_skew_x; +int mswindows_button2_max_skew_y; +int mswindows_button2_chord_time; + /* Number of wait handles */ static mswindows_waitable_count=0; +static int +mswindows_user_event_p (struct Lisp_Event* sevt) +{ + return (sevt->event_type == key_press_event + || sevt->event_type == button_press_event + || sevt->event_type == button_release_event + || sevt->event_type == pointer_motion_event); +} + /* - * Add an emacs event to the dispatch queue and increment the semaphore + * Add an emacs event to the proper dispatch queue */ void mswindows_enqueue_dispatch_event (Lisp_Object event) { - enqueue_event (event, &mswindows_dispatch_event_queue, - &mswindows_dispatch_event_queue_tail); - ReleaseSemaphore(mswindows_waitable[0], 1, NULL); + int user_p = mswindows_user_event_p (XEVENT(event)); + enqueue_event (event, + user_p ? &mswindows_u_dispatch_event_queue : + &mswindows_s_dispatch_event_queue, + user_p ? &mswindows_u_dispatch_event_queue_tail : + &mswindows_s_dispatch_event_queue_tail); + + /* This one does not go to window procedure, hence does not + generate XM_BUMPQUEUE magic event! */ + PostMessage (NULL, XM_BUMPQUEUE, 0, 0); } /* - * Remove and return the first emacs event on the dispatch queue. Don't - * decrement the queue's semaphore because it will be decremented by being - * waited on. + * Remove and return the first emacs event on the dispatch queue. + * Give a preference to user events over non-user ones. */ static Lisp_Object -mswindows_dequeue_dispatch_event (void) +mswindows_dequeue_dispatch_event () { Lisp_Object event; - event = dequeue_event (&mswindows_dispatch_event_queue, - &mswindows_dispatch_event_queue_tail); + struct Lisp_Event* sevt; + + assert (!NILP(mswindows_u_dispatch_event_queue) || + !NILP(mswindows_s_dispatch_event_queue)); + + event = dequeue_event ( + NILP(mswindows_u_dispatch_event_queue) ? + &mswindows_s_dispatch_event_queue : + &mswindows_u_dispatch_event_queue, + NILP(mswindows_u_dispatch_event_queue) ? + &mswindows_s_dispatch_event_queue_tail : + &mswindows_u_dispatch_event_queue_tail); + + sevt = XEVENT(event); + if (sevt->event_type == key_press_event + && (sevt->event.key.modifiers & FAKE_MOD_QUIT)) + { + sevt->event.key.modifiers &= ~FAKE_MOD_QUIT; + --mswindows_quit_chars_count; + } + return event; } /* * Remove and return the first emacs event on the dispatch queue that matches - * the supplied event and decrement the queue's semaphore. - * Only supports timeout events. + * the supplied event + * Timeout event matches if interval_id equals to that of the given event. + * Keypress event matches if logical AND between modifiers bitmask of the + * event in the queue and that of the given event is non-zero + * For all other event types, this function asserts. */ + Lisp_Object -mswindows_cancel_dispatch_event (Lisp_Object match_event) +mswindows_cancel_dispatch_event (struct Lisp_Event* match) { Lisp_Object event; Lisp_Object previous_event=Qnil; - struct Lisp_Event *match = XEVENT(match_event); + int user_p = mswindows_user_event_p (match); + Lisp_Object* head = user_p ? &mswindows_u_dispatch_event_queue : + &mswindows_s_dispatch_event_queue; + Lisp_Object* tail = user_p ? &mswindows_u_dispatch_event_queue_tail : + &mswindows_s_dispatch_event_queue_tail; - assert (match->event_type == timeout_event); + assert (match->event_type == timeout_event + || match->event_type == key_press_event); - EVENT_CHAIN_LOOP (event, mswindows_dispatch_event_queue) - if (XEVENT_TYPE (event) == match->event_type) - { - /* We only handle timeouts */ - if (XEVENT(event)->event.timeout.interval_id == - match->event.timeout.interval_id) - { - if (NILP (previous_event)) - dequeue_event (&mswindows_dispatch_event_queue, - &mswindows_dispatch_event_queue_tail); - else - { - XSET_EVENT_NEXT (previous_event, XEVENT_NEXT (event)); - if (EQ (mswindows_dispatch_event_queue_tail, event)) - mswindows_dispatch_event_queue_tail = previous_event; - } - - /* Decrement the dispatch queue counter */ - WaitForSingleObject(mswindows_waitable[0], INFINITE); - return event; - } - } - else + EVENT_CHAIN_LOOP (event, *head) + { + int found = 1; + if (XEVENT_TYPE (event) != match->event_type) + found = 0; + if (found && match->event_type == timeout_event + && (XEVENT(event)->event.timeout.interval_id != + match->event.timeout.interval_id)) + found = 0; + if (found && match->event_type == key_press_event + && ((XEVENT(event)->event.key.modifiers & + match->event.key.modifiers) == 0)) + found = 0; + + if (found) + { + if (NILP (previous_event)) + dequeue_event (head, tail); + else + { + XSET_EVENT_NEXT (previous_event, XEVENT_NEXT (event)); + if (EQ (*tail, event)) + *tail = previous_event; + } + + return event; + } previous_event = event; - - return Qnil; + } } /* @@ -156,26 +221,14 @@ switch (info->type) { case mswindows_waitable_type_dispatch: + assert (0); /* kkm - should not get here */ /* Can only have one waitable for the dispatch queue, and it's the first one */ assert (mswindows_waitable_count++ == 0); waitable=0; - InitializeCriticalSection(&mswindows_dispatch_crit); +// InitializeCriticalSection(&mswindows_dispatch_crit); assert (mswindows_waitable[0] = CreateSemaphore (NULL, 0, 0x7fffffff, NULL)); return mswindows_waitable_info+0; -#if 0 /* Windows95 doesn't support WaitableTimers */ - case mswindows_waitable_type_timeout: - { - LARGE_INTEGER due; - due.QuadPart = 10000 * (LONGLONG) info->data.timeout.milliseconds; - waitable = mswindows_find_free_waitable(); - mswindows_waitable[waitable] = CreateWaitableTimer(NULL, TRUE, NULL); - SetWaitableTimer(mswindows_waitable[waitable], &due, 0, NULL, NULL, FALSE); - mswindows_waitable_info[waitable].data.timeout.id = waitable; - } - break; -#endif - default: assert(0); } @@ -193,12 +246,6 @@ switch (info->type) { -#if 0 - case mswindows_waitable_type_timeout: - waitable = info->data.timeout.id; - CancelWaitableTimeout(mswindows_waitable[waitable]); - break; -#endif default: assert(0); @@ -211,6 +258,106 @@ --mswindows_waitable_count; } +/* + * Callback procedure for synchronous timer messages + */ +static void CALLBACK +mswindows_wm_timer_callback (HWND hwnd, UINT umsg, UINT id_timer, DWORD dwtime) +{ + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + struct Lisp_Event *event = XEVENT (emacs_event); + + KillTimer (NULL, id_timer); + + event->channel = Qnil; + event->timestamp = dwtime; + event->event_type = timeout_event; + event->event.timeout.interval_id = id_timer; + + mswindows_enqueue_dispatch_event (emacs_event); +} + +static void +mswindows_drain_windows_queue () +{ + MSG msg; + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + DispatchMessage (&msg); +} + +/* + * This drains the event queue and fills up two internal queues until + * an event of a type specified by USER_P is retrieved. + * + * If user_p, then the function drains until the first user event, or + * the first non-user event if there no user events. Otherwise, If + * not user_p, it does not give preference to user events. + * + * If badly_p, then the function does not return until an event is + * available. + * + * The code does not rely on MsgWaitForMultipleObjects preference for + * messages over waitable handles. + * + * Used by emacs_mswindows_event_pending_p and emacs_mswindows_next_event + */ +static void +mswindows_need_event (int user_p, int badly_p) +{ + int active; + + /* Have to drain Windows message queue first, otherwise, we may miss + quit char when called from quit_p */ + mswindows_drain_windows_queue (); + + while (NILP (mswindows_u_dispatch_event_queue) && + (user_p || NILP (mswindows_s_dispatch_event_queue))) + { + /* If we already have an event, we've got someting to return - no wait! */ + if (!NILP (mswindows_u_dispatch_event_queue) + || !NILP (mswindows_s_dispatch_event_queue)) + badly_p = 0; + + /* Now try getting a message */ + active = MsgWaitForMultipleObjects (mswindows_waitable_count, + mswindows_waitable, + FALSE, badly_p ? INFINITE : 0, + QS_ALLINPUT); + + /* This will assert if handle being waited for becomes abandoned. + Not the case currently tho */ + assert ((!badly_p && active == WAIT_TIMEOUT) || + (active >= WAIT_OBJECT_0 && + active <= WAIT_OBJECT_0 + mswindows_waitable_count)); + + if (active == WAIT_TIMEOUT) + { + /* No luck trying - just return what we've already got */ + return; + } + else if (active == WAIT_OBJECT_0 + mswindows_waitable_count) + { + /* Got your message, thanks */ + mswindows_drain_windows_queue (); + } + else + { + /* XXX FIXME: We should do some kind of round-robin scheme to ensure fairness */ + int waitable = active - WAIT_OBJECT_0; + mswindows_waitable_info_type *info = mswindows_waitable_info + waitable; + + switch (info->type) + { + /* XXX FIXME: Should enque subprocess event here so that it is not lost */ + default: + assert(0); + } + } + } /* while */ + + return; +} + /************************************************************************/ /* methods */ @@ -219,27 +366,32 @@ static int emacs_mswindows_add_timeout (EMACS_TIME thyme) { + int milliseconds; EMACS_TIME current_time; - int milliseconds; - int id; - mswindows_request_type request; - EMACS_GET_TIME (current_time); EMACS_SUB_TIME (thyme, thyme, current_time); - milliseconds = EMACS_SECS (thyme) * 1000 + EMACS_USECS (thyme) / 1000; + milliseconds = EMACS_SECS (thyme) * 1000 + + (EMACS_USECS (thyme) + 500) / 1000; if (milliseconds < 1) milliseconds = 1; - request.thing1 = (void *) milliseconds; - id = mswindows_make_request(WM_XEMACS_SETTIMER, 0, &request); - assert(id); /* XXX */ - return id; + return SetTimer (NULL, 0, milliseconds, mswindows_wm_timer_callback); } static void emacs_mswindows_remove_timeout (int id) { - mswindows_request_type request = { (void *) id }; - mswindows_make_request(WM_XEMACS_KILLTIMER, 0, &request); + struct Lisp_Event match_against; + Lisp_Object emacs_event; + + KillTimer (NULL, id); + + /* If there is a dispatch event generated by this + timeout in the queue, we have to remove it too. */ + match_against.event_type = timeout_event; + match_against.event.timeout.interval_id = id; + emacs_event = mswindows_cancel_dispatch_event (&match_against); + if (!NILP (emacs_event)) + Fdeallocate_event(emacs_event); } /* If `user_p' is false, then return whether there are any win32, timeout, @@ -254,97 +406,36 @@ static int emacs_mswindows_event_pending_p (int user_p) { - if (user_p) - { - /* Iterate over the dispatch queue looking for user-events */ - int found = 0; - Lisp_Object event; + mswindows_need_event (user_p, 0); - EnterCriticalSection (&mswindows_dispatch_crit); - EVENT_CHAIN_LOOP (event, mswindows_dispatch_event_queue) - if (command_event_p (event)) - found = 1; - LeaveCriticalSection (&mswindows_dispatch_crit); - return found; - } - else - { - /* Check for any kind of input, including the dispatch queue */ -#if 0 - /* Want do do the following, but it's not clear whether this would - * cause the waitables to become unsignalled */ - return (WaitForMultipleObjects (mswindows_waitable_count, - mswindows_waitable, FALSE, 0) - != WAIT_TIMEOUT); -#else - return !NILP (mswindows_dispatch_event_queue); -#endif - } -} - -static struct console * -find_console_from_fd (int fd) -{ - return 0; + return (!NILP (mswindows_u_dispatch_event_queue) + || (!user_p && !NILP (mswindows_s_dispatch_event_queue))); } /* * Return the next event - * We return windows events off the dispatch event queue in preference to other events */ static void emacs_mswindows_next_event (struct Lisp_Event *emacs_event) { - DWORD active; - active = WaitForMultipleObjects (mswindows_waitable_count, mswindows_waitable, - FALSE, INFINITE); - assert(active >= WAIT_OBJECT_0 && active <= WAIT_OBJECT_0 + mswindows_waitable_count - 1); - - /* Windows events on the dispatch event queue */ - if (active == WAIT_OBJECT_0) - { - /* XXX Copied from event-Xt.c */ - Lisp_Object event, event2; + Lisp_Object event, event2; + + /* Give strong preference to user events */ + mswindows_need_event (1, 1); - EnterCriticalSection (&mswindows_dispatch_crit); - XSETEVENT (event2, emacs_event); - event = mswindows_dequeue_dispatch_event (); - Fcopy_event (event, event2); - Fdeallocate_event (event); - LeaveCriticalSection (&mswindows_dispatch_crit); - } - else - { - /* XXX FIXME: We should do some kind of round-robin scheme to ensure fairness */ - int waitable = active - WAIT_OBJECT_0; - mswindows_waitable_info_type *info = mswindows_waitable_info + waitable; - - switch (info->type) - { - case mswindows_waitable_type_timeout: - emacs_event->channel = Qnil; - emacs_event->event_type = timeout_event; - emacs_event->event.timeout.interval_id = info->data.timeout.id; - mswindows_remove_waitable(info); - break; - - default: - assert(0); - } - } + /* XXX Copied from event-Xt.c */ + event = mswindows_dequeue_dispatch_event (!NILP(mswindows_u_dispatch_event_queue)); + XSETEVENT (event2, emacs_event); + Fcopy_event (event, event2); + Fdeallocate_event (event); } /* * Handle a magic event off the dispatch queue. - * XXX split into seperate functions for clarity. */ static void emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event) { - RECT *rect = &EVENT_MSWINDOWS_MAGIC_DATA(emacs_event); - struct frame *f = XFRAME (EVENT_CHANNEL (emacs_event)); - Lisp_Object frame = Qnil; - XSETFRAME (frame, f); #if 0 stderr_out("magic %x, (%d,%d), (%d,%d)\n", EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event), @@ -355,8 +446,11 @@ case WM_SETFOCUS: case WM_KILLFOCUS: { + Lisp_Object frame = EVENT_CHANNEL (emacs_event); + struct frame *f = XFRAME (frame); int in_p = (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) == WM_SETFOCUS); Lisp_Object conser; + /* struct gcpro gcpro1; */ /* Clear sticky modifiers here (if we had any) */ @@ -368,53 +462,31 @@ I Don't know why */ emacs_handle_focus_change_final (conser); /* UNGCPRO; */ + } break; - /* XXX What about Enter & Leave */ + case XM_BUMPQUEUE: + /* This is a nice event, when we're in need to queue *something* */ + break; + + case XM_MAPFRAME: + case XM_UNMAPFRAME: + { + Lisp_Object frame = EVENT_CHANNEL (emacs_event); + va_run_hook_with_args (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event) + == XM_MAPFRAME ? + Qmap_frame_hook : Qunmap_frame_hook, + 1, frame); + } + break; + + /* XXX What about Enter & Leave */ #if 0 va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook : Qmouse_leave_frame_hook, 1, frame); - break; #endif - case WM_SIZE: - if ((rect->left & rect->top & rect->right & rect->bottom) == -1) - { - /* Iconified */ - FRAME_VISIBLE_P (f) = 0; - va_run_hook_with_args (Qunmap_frame_hook, 1, frame); - Fframe_iconified_p (frame); - } - else - { - /* If we're uniconified, our size may or may not have changed */ - int columns, rows; - int was_visible = FRAME_VISIBLE_P (f); - - FRAME_VISIBLE_P (f) = 1; - FRAME_PIXWIDTH(f) = rect->right; - FRAME_PIXHEIGHT(f) = rect->bottom; - - pixel_to_char_size (f, rect->right, rect->bottom, &columns, &rows); - change_frame_size (f, rows, columns, 0); -/* MARK_FRAME_WINDOWS_STRUCTURE_CHANGED (f); /* XXX Too extreme? */ - - if (!was_visible) - va_run_hook_with_args (Qmap_frame_hook, 1, frame); - - } - break; - - case WM_PAINT: - mswindows_redraw_exposed_area(f, rect->left, rect->top, - rect->right, rect->bottom); - break; - - case WM_CLOSE: - enqueue_misc_user_event (frame, Qeval, list3 (Qdelete_frame, frame, Qt)); - break; - default: assert(0); } @@ -443,6 +515,26 @@ static void emacs_mswindows_quit_p (void) { + mswindows_need_event (1, 0); + + if (mswindows_quit_chars_count > 0) + { + /* Yes there's a hidden one... Throw it away */ + struct Lisp_Event match_against; + Lisp_Object emacs_event; + + match_against.event_type = key_press_event; + match_against.event.key.modifiers = FAKE_MOD_QUIT; + + emacs_event = mswindows_cancel_dispatch_event (&match_against); + assert (!NILP (emacs_event)); + + Vquit_flag = (XEVENT(emacs_event)->event.key.modifiers & MOD_SHIFT + ? Qcritical : Qt); + + Fdeallocate_event(emacs_event); + --mswindows_quit_chars_count; + } } /* This is called from GC when a process object is about to be freed. @@ -474,22 +566,71 @@ void vars_of_event_mswindows (void) { - mswindows_dispatch_event_queue = Qnil; - staticpro (&mswindows_dispatch_event_queue); - mswindows_dispatch_event_queue_tail = Qnil; + mswindows_u_dispatch_event_queue = Qnil; + staticpro (&mswindows_u_dispatch_event_queue); + mswindows_u_dispatch_event_queue_tail = Qnil; + + mswindows_s_dispatch_event_queue = Qnil; + staticpro (&mswindows_s_dispatch_event_queue); + mswindows_s_dispatch_event_queue_tail = Qnil; mswindows_event_stream = xnew (struct event_stream); mswindows_event_stream->event_pending_p = emacs_mswindows_event_pending_p; - mswindows_event_stream->next_event_cb = emacs_mswindows_next_event; + mswindows_event_stream->next_event_cb = emacs_mswindows_next_event; mswindows_event_stream->handle_magic_event_cb = emacs_mswindows_handle_magic_event; mswindows_event_stream->add_timeout_cb = emacs_mswindows_add_timeout; mswindows_event_stream->remove_timeout_cb = emacs_mswindows_remove_timeout; mswindows_event_stream->select_console_cb = emacs_mswindows_select_console; - mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console; + mswindows_event_stream->unselect_console_cb = emacs_mswindows_unselect_console; mswindows_event_stream->select_process_cb = emacs_mswindows_select_process; - mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process; + mswindows_event_stream->unselect_process_cb = emacs_mswindows_unselect_process; mswindows_event_stream->quit_p_cb = emacs_mswindows_quit_p; + + DEFVAR_BOOL ("w32-dynamic-frame-resize", &mswindows_dynamic_frame_resize /* +*Controls redrawing frame contents during mouse-drag or keyboard resize +operation. When non-nil, the frame is redrawn while being resized. When +nil, frame is not redrawn, and exposed areas are filled with default +MDI application background color. Note that this option only has effect +if "Show window contents while dragging" is on in system Display/Plus! +settings. +Default is t on fast machines, nil on slow. +*/ ); + +/* The description copied verbatim from nt-emacs. (C) Geoff Voelker */ + DEFVAR_INT ("w32-mouse-button-tolerance", &mswindows_button2_chord_time /* +*Analogue of double click interval for faking middle mouse events. +The value is the minimum time in milliseconds that must elapse between +left/right button down events before they are considered distinct events. +If both mouse buttons are depressed within this interval, a middle mouse +button down event is generated instead. +If negative or zero, currently set system default is used instead. +*/ ); + +/* The description copied verbatim from nt-emacs. (C) Geoff Voelker */ + DEFVAR_INT ("w32-num-mouse-buttons", &mswindows_num_mouse_buttons /* +Number of physical mouse buttons. +*/ ); + + DEFVAR_INT ("w32-mouse-button-max-skew-x", &mswindows_button2_max_skew_x /* +*Maximum horizontal distance in pixels between points in which left and +right button clicks occured for them to be translated into single +middle button event. Clicks must occur in time not longer than defined +by the variable w32-mouse-button-tolerance. +If negative or zero, currently set system default is used instead. +*/ ); + + DEFVAR_INT ("w32-mouse-button-max-skew-y", &mswindows_button2_max_skew_y /* +*Maximum vertical distance in pixels between points in which left and +right button clicks occured for them to be translated into single +middle button event. Clicks must occur in time not longer than defined +by the variable w32-mouse-button-tolerance. +If negative or zero, currently set system default is used instead. +*/ ); + + mswindows_button2_max_skew_x = 0; + mswindows_button2_max_skew_y = 0; + mswindows_button2_chord_time = 0; } void @@ -501,4 +642,7 @@ init_event_mswindows_late (void) { event_stream = mswindows_event_stream; + + mswindows_dynamic_frame_resize = !GetSystemMetrics (SM_SLOWMACHINE); + mswindows_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS); }