Mercurial > hg > xemacs-beta
diff 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 |
line wrap: on
line diff
--- a/src/event-msw.c Mon Aug 13 10:18:22 2007 +0200 +++ b/src/event-msw.c Mon Aug 13 10:19:09 2007 +0200 @@ -33,8 +33,17 @@ #include <config.h> #include "lisp.h" +#include "console-msw.h" + +#ifdef HAVE_SCROLLBARS +# include "scrollbar-msw.h" +#endif + +#ifdef HAVE_MENUBARS +# include "menubar-msw.h" +#endif + #include "device.h" -#include "console-msw.h" #include "emacsfns.h" #include "events.h" #include "frame.h" @@ -44,7 +53,35 @@ #include "syswait.h" #include "systime.h" -#include "event-msw.h" +#include "events-mod.h" + +#ifdef HAVE_MENUBARS +#define ADJR_MENUFLAG TRUE +#else +#define ADJR_MENUFLAG FALSE +#endif + +/* Fake key modifier which is attached to a quit char event. + Removed upon dequeueing an event */ +#define FAKE_MOD_QUIT 0x80 + +/* Timer ID used for button2 emulation */ +#define BUTTON_2_TIMER_ID 1 + +/* Drag and drop event data types (subset of types in offix-types.h) */ +#define DndFile 2 +#define DndFiles 3 +#define DndText 4 + + +static Lisp_Object mswindows_find_frame (HWND hwnd); +static Lisp_Object mswindows_find_console (HWND hwnd); +static Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods); +static int mswindows_modifier_state (BYTE* keymap, int has_AltGr); +static void mswindows_set_chord_timer (HWND hwnd); +static int mswindows_button2_near_enough (POINTS p1, POINTS p2); +static int mswindows_current_layout_has_AltGr (void); + static struct event_stream *mswindows_event_stream; @@ -57,19 +94,14 @@ 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. - * Apart from the dispatch queue semaphore, all of these handles may be waited - * on multiple times in emacs_mswindows_next_event before being processed and so - * must be manual-reset events. - */ +/* The number of things we can wait on */ +#define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1) + +/* List of mswindows waitable handles. */ static HANDLE mswindows_waitable[MAX_WAITABLE]; -/* 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 +/* Incremented in WM_[SYS]KEYDOWN handler in the mswindows_wnd_proc() Decremented in mswindows_dequeue_dispatch_event() */ int mswindows_quit_chars_count = 0; @@ -96,10 +128,15 @@ { return (sevt->event_type == key_press_event || sevt->event_type == button_press_event - || sevt->event_type == button_release_event); + || sevt->event_type == button_release_event + || sevt->event_type == dnd_drop_event); } -/* +/************************************************************************/ +/* Dispatch queue management */ +/************************************************************************/ + +/* * Add an emacs event to the proper dispatch queue */ void @@ -117,6 +154,69 @@ PostMessage (NULL, XM_BUMPQUEUE, 0, 0); } +void +mswindows_enqueue_magic_event (HWND hwnd, UINT message) +{ + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + struct Lisp_Event* event = XEVENT (emacs_event); + + event->channel = mswindows_find_frame (hwnd); + event->timestamp = GetMessageTime(); + event->event_type = magic_event; + EVENT_MSWINDOWS_MAGIC_TYPE (event) = message; + + mswindows_enqueue_dispatch_event (emacs_event); +} + +static void +mswindows_enqueue_mouse_button_event (HWND hwnd, UINT message, POINTS where, DWORD when) +{ + + /* We always use last message time, because mouse button + events may get delayed, and XEmacs double click + recognition will fail */ + + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + struct Lisp_Event* event = XEVENT(emacs_event); + + event->channel = mswindows_find_frame(hwnd); + event->timestamp = when; + event->event.button.button = + (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 : + ((message==WM_RBUTTONDOWN || message==WM_RBUTTONUP) ? 3 : 2); + event->event.button.x = where.x; + event->event.button.y = where.y; + event->event.button.modifiers = mswindows_modifier_state (NULL, 0); + + if (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN || + message==WM_RBUTTONDOWN) + { + event->event_type = button_press_event; + SetCapture (hwnd); + } + else + { + event->event_type = button_release_event; + ReleaseCapture (); + } + + mswindows_enqueue_dispatch_event (emacs_event); +} + +static void +mswindows_enqueue_keypress_event (HWND hwnd, Lisp_Object keysym, int mods) +{ + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + struct Lisp_Event* event = XEVENT(emacs_event); + + event->channel = mswindows_find_console(hwnd); + event->timestamp = GetMessageTime(); + event->event_type = key_press_event; + event->event.key.keysym = keysym; + event->event.key.modifiers = mods; + mswindows_enqueue_dispatch_event (emacs_event); +} + /* * Remove and return the first emacs event on the dispatch queue. * Give a preference to user events over non-user ones. @@ -204,6 +304,11 @@ return Qnil; } + +/************************************************************************/ +/* Event pump */ +/************************************************************************/ + static Lisp_Object mswindows_modal_loop_error_handler (Lisp_Object cons_sig_data, Lisp_Object u_n_u_s_e_d) @@ -312,98 +417,16 @@ /* This function can call lisp */ Lisp_Object result = Qt; - + struct gcpro gcpro1; + GCPRO1 (result); + if (NILP(mswindows_error_caught_in_modal_loop)) result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil); + UNGCPRO; return result; } -/* - * Find a free waitable slot - */ -#if 0 /* NOTUSED */ -static int -mswindows_find_free_waitable(void) -{ - int i; - for (i=0; i<mswindows_waitable_count; i++) - if (mswindows_waitable_info[i].type == mswindows_waitable_type_none) - return i; - assert (mswindows_waitable_count < MAX_WAITABLE); - return mswindows_waitable_count++; -} -#endif -/* - * Create a new waitable using the type and data passed in by the info structure - * Returns a pointer to the info associated with the assigned waitable object - */ -mswindows_waitable_info_type * -mswindows_add_waitable(mswindows_waitable_info_type *info) -{ - int waitable; - - 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; -#if 0 - InitializeCriticalSection(&mswindows_dispatch_crit); -#endif - assert (mswindows_waitable[0] = CreateSemaphore (NULL, 0, 0x7fffffff, NULL)); - return mswindows_waitable_info+0; - - default: - assert(0); - } - mswindows_waitable_info[waitable].type = info->type; - return mswindows_waitable_info+waitable; -} - -/* - * Remove a waitable using the type and data passed in by the info structure. - */ -void -mswindows_remove_waitable(mswindows_waitable_info_type *info) -{ - int waitable; - - switch (info->type) - { - - default: - assert(0); - } - - CloseHandle(mswindows_waitable[waitable]); - mswindows_waitable[waitable] = 0; - mswindows_waitable_info[waitable].type = mswindows_waitable_type_none; - if (waitable == mswindows_waitable_count-1) - --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); - - if (KillTimer (NULL, id_timer)) - --mswindows_pending_timers_count; - - 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 () @@ -523,20 +546,841 @@ { /* 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); - } + assert(0); /* #### */ } } /* while */ return; } +/************************************************************************/ +/* Event generators */ +/************************************************************************/ + +/* + * 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); + + if (KillTimer (NULL, id_timer)) + --mswindows_pending_timers_count; + + event->channel = Qnil; + event->timestamp = dwtime; + event->event_type = timeout_event; + event->event.timeout.interval_id = id_timer; + + mswindows_enqueue_dispatch_event (emacs_event); +} + +/* + * Callback procedure for dde messages + */ +HDDEDATA CALLBACK +mswindows_dde_callback (UINT uType, UINT uFmt, HCONV hconv, + HSZ hszTopic, HSZ hszItem, HDDEDATA hdata, + DWORD dwData1, DWORD dwData2) +{ + switch (uType) + { + case XTYP_CONNECT: + if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)) + return (HDDEDATA)TRUE; + return (HDDEDATA)FALSE; + + case XTYP_WILDCONNECT: + { + /* We only support one {service,topic} pair */ + HSZPAIR pairs[2] = { + { mswindows_dde_service, mswindows_dde_topic_system }, { 0, 0 } }; + + if (!(hszItem || DdeCmpStringHandles (hszItem, mswindows_dde_service)) && + !(hszTopic || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))); + return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs, + sizeof (pairs), 0L, 0, uFmt, 0)); + } + return (HDDEDATA)NULL; + + case XTYP_EXECUTE: + if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)) + { + DWORD len = DdeGetData (hdata, NULL, 0, 0); + char *cmd = alloca (len+1); + char *end; + Lisp_Object l_dndlist; + Lisp_Object emacs_event = Fmake_event (Qnil, Qnil); + struct Lisp_Event *event = XEVENT (emacs_event); + + DdeGetData (hdata, cmd, len, 0); + cmd[len] = '\0'; + DdeFreeDataHandle (hdata); + + /* Check syntax & that it's an [Open("foo")] command */ + /* #### Ought to be generalised and accept some other commands */ + if (*cmd == '[') + cmd++; + if (strnicmp (cmd, MSWINDOWS_DDE_ITEM_OPEN, + strlen (MSWINDOWS_DDE_ITEM_OPEN))) + return DDE_FNOTPROCESSED; + cmd += strlen (MSWINDOWS_DDE_ITEM_OPEN); + while (*cmd==' ') + cmd++; + if (*cmd!='(' || *(cmd+1)!='\"') + return DDE_FNOTPROCESSED; + end = (cmd+=2); + while (*end && *end!='\"') + end++; + if (!*end) + return DDE_FNOTPROCESSED; + *end = '\0'; + if (*(++end)!=')') + return DDE_FNOTPROCESSED; + if (*(++end)==']') + end++; + if (*end) + return DDE_FNOTPROCESSED; + + l_dndlist = make_ext_string (cmd, strlen(cmd), FORMAT_FILENAME); + + event->channel = Qnil; + event->timestamp = GetTickCount(); + event->event_type = dnd_drop_event; + event->event.dnd_drop.button = 0; + event->event.dnd_drop.modifiers = 0; + event->event.dnd_drop.x = -1; + event->event.dnd_drop.y = -1; + event->event.dnd_drop.data = Fcons (make_int (DndFile), + Fcons (l_dndlist, Qnil)); + mswindows_enqueue_dispatch_event (emacs_event); + + return (HDDEDATA) DDE_FACK; + } + DdeFreeDataHandle (hdata); + return (HDDEDATA) DDE_FNOTPROCESSED; + + default: + return (HDDEDATA) NULL; + } + +} + +/* + * The windows procedure for the window class XEMACS_CLASS + */ +LRESULT WINAPI +mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + /* Note: Remember to initialise emacs_event and event before use. + This code calls code that can GC. You must GCPRO before calling such code. */ + Lisp_Object emacs_event = Qnil; + Lisp_Object fobj = Qnil; + + struct Lisp_Event *event; + struct frame *frame; + struct mswindows_frame* msframe; + + switch (message) + { + case WM_ERASEBKGND: + /* Erase background only during non-dynamic sizing */ + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + if (msframe->sizing && !mswindows_dynamic_frame_resize) + goto defproc; + return 1; + + case WM_CLOSE: + fobj = mswindows_find_frame (hwnd); + enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt)); + mswindows_enqueue_magic_event (hwnd, XM_BUMPQUEUE); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + BYTE keymap[256]; + int has_AltGr = mswindows_current_layout_has_AltGr (); + int mods; + Lisp_Object keysym; + + GetKeyboardState (keymap); + mods = mswindows_modifier_state (keymap, has_AltGr); + + /* Handle those keys that TranslateMessage won't generate a WM_CHAR for */ + if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam, mods))) + mswindows_enqueue_keypress_event (hwnd, keysym, mods); + else + { + int quit_ch = CONSOLE_QUIT_CHAR (XCONSOLE (mswindows_find_console (hwnd))); + BYTE keymap_orig[256]; + MSG msg = { hwnd, message, wParam, lParam, GetMessageTime(), (GetMessagePos()) }; + memcpy (keymap_orig, keymap, 256); + + /* Clear control and alt modifiers out of the keymap */ + keymap [VK_RCONTROL] = 0; + keymap [VK_LMENU] = 0; + if (!has_AltGr || !(keymap [VK_LCONTROL] & 0x80) || !(keymap [VK_RMENU] & 0x80)) + { + keymap [VK_LCONTROL] = 0; + keymap [VK_CONTROL] = 0; + keymap [VK_RMENU] = 0; + keymap [VK_MENU] = 0; + } + SetKeyboardState (keymap); + + /* Have some WM_[SYS]CHARS in the queue */ + TranslateMessage (&msg); + + while (PeekMessage (&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE) + ||PeekMessage (&msg, hwnd, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE)) + { + int ch = msg.wParam; + /* CH is a character code for the key: + 'C' for Shift+C and Ctrl+Shift+C + 'c' for c and Ctrl+c */ + + /* #### If locale is not C, US or other latin-1, + isalpha() maybe not what do we mean */ + + /* XEmacs doesn't seem to like Shift on non-alpha keys */ + if (!isalpha(ch)) + mods &= ~MOD_SHIFT; + + /* Un-capitalise alpha control keys */ + if ((mods & MOD_CONTROL) && isalpha(ch)) + ch |= ('A' ^ 'a'); + + /* If a quit char with no modifiers other than control and + shift, then mark it with a fake modifier, which is removed + upon dequeueing the event */ + /* #### This might also not withstand localization, if + quit character is not a latin-1 symbol */ + if (((quit_ch < ' ' && (mods & MOD_CONTROL) && quit_ch + 'a' - 1 == ch) + || (quit_ch >= ' ' && !(mods & MOD_CONTROL) && quit_ch == ch)) + && ((mods & ~(MOD_CONTROL | MOD_SHIFT)) == 0)) + { + mods |= FAKE_MOD_QUIT; + ++mswindows_quit_chars_count; + } + + mswindows_enqueue_keypress_event (hwnd, make_char(ch), mods); + } /* while */ + SetKeyboardState (keymap_orig); + } /* else */ + } + goto defproc; + + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + /* Real middle mouse button has nothing to do with emulated one: + if one wants to exercise fingers playing chords on the mouse, + he is allowed to do that! */ + mswindows_enqueue_mouse_button_event (hwnd, message, + MAKEPOINTS (lParam), GetMessageTime()); + break; + + case WM_LBUTTONUP: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + msframe->last_click_time = GetMessageTime(); + + KillTimer (hwnd, BUTTON_2_TIMER_ID); + msframe->button2_need_lbutton = 0; + if (msframe->ignore_next_lbutton_up) + { + msframe->ignore_next_lbutton_up = 0; + } + else if (msframe->button2_is_down) + { + msframe->button2_is_down = 0; + msframe->ignore_next_rbutton_up = 1; + mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, + MAKEPOINTS (lParam), GetMessageTime()); + } + else + { + if (msframe->button2_need_rbutton) + { + msframe->button2_need_rbutton = 0; + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, + MAKEPOINTS (lParam), GetMessageTime()); + } + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONUP, + MAKEPOINTS (lParam), GetMessageTime()); + } + break; + + case WM_RBUTTONUP: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + msframe->last_click_time = GetMessageTime(); + + KillTimer (hwnd, BUTTON_2_TIMER_ID); + msframe->button2_need_rbutton = 0; + if (msframe->ignore_next_rbutton_up) + { + msframe->ignore_next_rbutton_up = 0; + } + else if (msframe->button2_is_down) + { + msframe->button2_is_down = 0; + msframe->ignore_next_lbutton_up = 1; + mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONUP, + MAKEPOINTS (lParam), GetMessageTime()); + } + else + { + if (msframe->button2_need_lbutton) + { + msframe->button2_need_lbutton = 0; + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, + MAKEPOINTS (lParam), GetMessageTime()); + } + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONUP, + MAKEPOINTS (lParam), GetMessageTime()); + } + break; + + case WM_LBUTTONDOWN: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + + if (msframe->button2_need_lbutton) + { + KillTimer (hwnd, BUTTON_2_TIMER_ID); + msframe->button2_need_lbutton = 0; + msframe->button2_need_rbutton = 0; + if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam))) + { + mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, + MAKEPOINTS (lParam), GetMessageTime()); + msframe->button2_is_down = 1; + } + else + { + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, + msframe->last_click_point, msframe->last_click_time); + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, + MAKEPOINTS (lParam), GetMessageTime()); + } + } + else + { + mswindows_set_chord_timer (hwnd); + msframe->button2_need_rbutton = 1; + msframe->last_click_point = MAKEPOINTS (lParam); + } + msframe->last_click_time = GetMessageTime(); + break; + + case WM_RBUTTONDOWN: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + + if (msframe->button2_need_rbutton) + { + KillTimer (hwnd, BUTTON_2_TIMER_ID); + msframe->button2_need_lbutton = 0; + msframe->button2_need_rbutton = 0; + if (mswindows_button2_near_enough (msframe->last_click_point, MAKEPOINTS (lParam))) + { + mswindows_enqueue_mouse_button_event (hwnd, WM_MBUTTONDOWN, + MAKEPOINTS (lParam), GetMessageTime()); + msframe->button2_is_down = 1; + } + else + { + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, + msframe->last_click_point, msframe->last_click_time); + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, + MAKEPOINTS (lParam), GetMessageTime()); + } + } + else + { + mswindows_set_chord_timer (hwnd); + msframe->button2_need_lbutton = 1; + msframe->last_click_point = MAKEPOINTS (lParam); + } + msframe->last_click_time = GetMessageTime(); + break; + + case WM_TIMER: + if (wParam == BUTTON_2_TIMER_ID) + { + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + KillTimer (hwnd, BUTTON_2_TIMER_ID); + + if (msframe->button2_need_lbutton) + { + msframe->button2_need_lbutton = 0; + mswindows_enqueue_mouse_button_event (hwnd, WM_RBUTTONDOWN, + msframe->last_click_point, msframe->last_click_time); + } + else if (msframe->button2_need_rbutton) + { + msframe->button2_need_rbutton = 0; + mswindows_enqueue_mouse_button_event (hwnd, WM_LBUTTONDOWN, + msframe->last_click_point, msframe->last_click_time); + } + } + else + assert ("Spurious timer fired" == 0); + break; + + case WM_MOUSEMOVE: + /* Optimization: don't report mouse movement while size is changind */ + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + if (!msframe->sizing) + { + /* When waiting for the second mouse button to finish + button2 emulation, and have moved too far, just pretend + as if timer has expired. This impoves drag-select feedback */ + if ((msframe->button2_need_lbutton || msframe->button2_need_rbutton) + && !mswindows_button2_near_enough (msframe->last_click_point, + MAKEPOINTS (lParam))) + { + KillTimer (hwnd, BUTTON_2_TIMER_ID); + SendMessage (hwnd, WM_TIMER, BUTTON_2_TIMER_ID, 0); + } + + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + event->channel = mswindows_find_frame(hwnd); + event->timestamp = GetMessageTime(); + event->event_type = pointer_motion_event; + event->event.motion.x = MAKEPOINTS(lParam).x; + event->event.motion.y = MAKEPOINTS(lParam).y; + event->event.motion.modifiers = mswindows_modifier_state (NULL, 0); + + mswindows_enqueue_dispatch_event (emacs_event); + } + break; + + case WM_PAINT: + { + PAINTSTRUCT paintStruct; + + frame = XFRAME (mswindows_find_frame (hwnd)); + + BeginPaint (hwnd, &paintStruct); + mswindows_redraw_exposed_area (frame, + paintStruct.rcPaint.left, paintStruct.rcPaint.top, + paintStruct.rcPaint.right, paintStruct.rcPaint.bottom); + EndPaint (hwnd, &paintStruct); + } + break; + + case WM_SIZE: + /* We only care about this message if our size has really changed */ + if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED) + { + RECT rect; + int columns, rows; + + fobj = mswindows_find_frame (hwnd); + frame = XFRAME (fobj); + msframe = FRAME_MSWINDOWS_DATA (frame); + + /* We cannot handle frame map and unmap hooks right in + this routine, because these may throw. We queue + magic events to run these hooks instead - kkm */ + + if (wParam==SIZE_MINIMIZED) + { + /* Iconified */ + FRAME_VISIBLE_P (frame) = 0; + mswindows_enqueue_magic_event (hwnd, XM_UNMAPFRAME); + Fframe_iconified_p (fobj); + } + else + { + int was_visible = FRAME_VISIBLE_P (frame); + if (!msframe->sizing && !was_visible) + mswindows_enqueue_magic_event (hwnd, XM_MAPFRAME); + + GetClientRect(hwnd, &rect); + FRAME_VISIBLE_P(frame) = 1; + FRAME_PIXWIDTH(frame) = rect.right; + FRAME_PIXHEIGHT(frame) = rect.bottom; + pixel_to_char_size (frame, rect.right, rect.bottom, &columns, &rows); + change_frame_size (frame, rows, columns, 1); + + if (msframe->sizing && mswindows_dynamic_frame_resize) + redisplay (); + } + } + break; + + /* Misc magic events which only require that the frame be identified */ + case WM_SETFOCUS: + case WM_KILLFOCUS: + mswindows_enqueue_magic_event (hwnd, message); + break; + + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS *wp = (LPWINDOWPOS) lParam; + WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) }; + GetWindowPlacement(hwnd, &wpl); + + /* Only interested if size is changing and we're not being iconified */ + if ((wpl.showCmd != SW_SHOWMINIMIZED) && !(wp->flags & SWP_NOSIZE)) + { + RECT ncsize = { 0, 0, 0, 0 }; + int pixwidth, pixheight; + AdjustWindowRectEx (&ncsize, GetWindowLong (hwnd, GWL_STYLE), + GetMenu(hwnd) != NULL, + GetWindowLong (hwnd, GWL_EXSTYLE)); + + round_size_to_char (XFRAME (mswindows_find_frame (hwnd)), + wp->cx - (ncsize.right - ncsize.left), + wp->cy - (ncsize.bottom - ncsize.top), + &pixwidth, &pixheight); + + /* Convert client sizes to window sizes */ + pixwidth += (ncsize.right - ncsize.left); + pixheight += (ncsize.bottom - ncsize.top); + + if (wpl.showCmd != SW_SHOWMAXIMIZED) + { + /* Adjust so that the bottom or right doesn't move if it's + * the top or left that's being changed */ + RECT rect; + GetWindowRect (hwnd, &rect); + + if (rect.left != wp->x) + wp->x += wp->cx - pixwidth; + if (rect.top != wp->y) + wp->y += wp->cy - pixheight; + } + + wp->cx = pixwidth; + wp->cy = pixheight; + } + } + break; + + case WM_ENTERSIZEMOVE: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + msframe->sizing = 1; + return 0; + + case WM_EXITSIZEMOVE: + msframe = FRAME_MSWINDOWS_DATA (XFRAME (mswindows_find_frame (hwnd))); + msframe->sizing = 0; + /* Queue noop event */ + mswindows_enqueue_magic_event (hwnd, XM_BUMPQUEUE); + return 0; + +#ifdef HAVE_SCROLLBARS + case WM_VSCROLL: + case WM_HSCROLL: + { + /* Direction of scroll is determined by scrollbar instance. */ + int code = (int) LOWORD(wParam); + int pos = (short int) HIWORD(wParam); + HWND hwndScrollBar = (HWND) lParam; + struct gcpro gcpro1, gcpro2; + + mswindows_handle_scrollbar_event (hwndScrollBar, code, pos); + GCPRO2 (emacs_event, fobj); + if (UNBOUNDP(mswindows_pump_outstanding_events())) /* Can GC */ + { + /* Error during event pumping - cancel scroll */ + SendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0); + } + UNGCPRO; + break; + } +#endif + +#ifdef HAVE_MENUBARS + case WM_INITMENU: + if (UNBOUNDP (mswindows_handle_wm_initmenu ( + (HMENU) wParam, + XFRAME (mswindows_find_frame (hwnd))))) + SendMessage (hwnd, WM_CANCELMODE, 0, 0); + break; + + case WM_INITMENUPOPUP: + if (!HIWORD(lParam)) + { + if (UNBOUNDP (mswindows_handle_wm_initmenupopup ( + (HMENU) wParam, + XFRAME (mswindows_find_frame (hwnd))))) + SendMessage (hwnd, WM_CANCELMODE, 0, 0); + } + break; + + case WM_EXITMENULOOP: + if (UNBOUNDP (mswindows_handle_wm_exitmenuloop ( + XFRAME (mswindows_find_frame (hwnd))))) + SendMessage (hwnd, WM_CANCELMODE, 0, 0); + break; + +#endif /* HAVE_MENUBARS */ + + case WM_COMMAND: + { + WORD id = LOWORD (wParam); + frame = XFRAME (mswindows_find_frame (hwnd)); + +#ifdef HAVE_MENUBARS + if (!NILP (mswindows_handle_wm_command (frame, id))) + break; +#endif + +#ifdef HAVE_TOOLBARS + O Toolbar Implementor, this place may have something for you!; +#endif + + /* Bite me - a spurious command. No abort(), for safety */ + /* #### Perhaps, this message should be changed */ + error ("Cannot decode command. Tell kkm he's a parallelogramm, if you know" + " what does that mean!"); + } + break; + + case WM_DROPFILES: /* implementation ripped-off from event-Xt.c */ + { + UINT filecount, i, len; + POINT point; + char filename[MAX_PATH]; + Lisp_Object l_type, l_dndlist = Qnil, l_item; + + emacs_event = Fmake_event (Qnil, Qnil); + event = XEVENT(emacs_event); + + if (!DragQueryPoint ((HANDLE) wParam, &point)) + point.x = point.y = -1; /* outside client area */ + + filecount = DragQueryFile ((HANDLE) wParam, -1, NULL, 0); + if (filecount == 1) + { + l_type = make_int (DndFile); + len = DragQueryFile ((HANDLE) wParam, 0, filename, MAX_PATH); + l_dndlist = make_ext_string (filename, len, FORMAT_FILENAME); + } + else + { + l_type = make_int (DndFiles); + for (i=0; i<filecount; i++) + { + len = DragQueryFile ((HANDLE) wParam, i, filename, MAX_PATH); + l_item = make_ext_string (filename, len, FORMAT_FILENAME); + l_dndlist = Fcons (l_item, l_dndlist); /* reverse order */ + } + } + DragFinish ((HANDLE) wParam); + + event->channel = mswindows_find_frame(hwnd); + event->timestamp = GetMessageTime(); + event->event_type = dnd_drop_event; + event->event.dnd_drop.button = 1; /* #### Should try harder */ + event->event.dnd_drop.modifiers = mswindows_modifier_state (NULL, 0); + event->event.dnd_drop.x = point.x; + event->event.dnd_drop.y = point.y; + event->event.dnd_drop.data = Fcons (l_type, Fcons (l_dndlist, Qnil)); + + mswindows_enqueue_dispatch_event (emacs_event); + } + break; + + defproc: + default: + return DefWindowProc (hwnd, message, wParam, lParam); + } + return (0); +} + + +/************************************************************************/ +/* keyboard, mouse & other helpers for the windows procedure */ +/************************************************************************/ +static void +mswindows_set_chord_timer (HWND hwnd) +{ + int interval; + + /* We get half system threshold as it seems to + long before drag-selection is shown */ + if (mswindows_button2_chord_time <= 0) + interval = GetDoubleClickTime () / 2; + else + interval = mswindows_button2_chord_time; + + SetTimer (hwnd, BUTTON_2_TIMER_ID, interval, 0); +} + +static int +mswindows_button2_near_enough (POINTS p1, POINTS p2) +{ + int dx, dy; + if (mswindows_button2_max_skew_x <= 0) + dx = GetSystemMetrics (SM_CXDOUBLECLK) / 2; + else + dx = mswindows_button2_max_skew_x; + + if (mswindows_button2_max_skew_y <= 0) + dy = GetSystemMetrics (SM_CYDOUBLECLK) / 2; + else + dy = mswindows_button2_max_skew_y; + + return abs (p1.x - p2.x) < dx && abs (p1.y- p2.y)< dy; +} + +static int +mswindows_current_layout_has_AltGr (void) +{ + /* This simple caching mechanism saves 10% of CPU + time when a key typed at autorepeat rate of 30 cps! */ + static HKL last_hkl = 0; + static int last_hkl_has_AltGr; + + HKL current_hkl = GetKeyboardLayout (0); + if (current_hkl != last_hkl) + { + TCHAR c; + last_hkl_has_AltGr = 0; + /* In this loop, we query whether a character requires + AltGr to be down to generate it. If at least such one + found, this means that the layout does regard AltGr */ + for (c = ' '; c <= 0xFFU && c != 0 && !last_hkl_has_AltGr; ++c) + if (HIBYTE (VkKeyScan (c)) == 6) + last_hkl_has_AltGr = 1; + last_hkl = current_hkl; + } + return last_hkl_has_AltGr; +} + + +/* Returns the state of the modifier keys in the format expected by the + * Lisp_Event key_data, button_data and motion_data modifiers member */ +int mswindows_modifier_state (BYTE* keymap, int has_AltGr) +{ + int mods = 0; + + if (keymap == NULL) + { + keymap = (BYTE*) alloca(256); + GetKeyboardState (keymap); + has_AltGr = mswindows_current_layout_has_AltGr (); + } + + if (has_AltGr && (keymap [VK_LCONTROL] & 0x80) && (keymap [VK_RMENU] & 0x80)) + { + mods |= (keymap [VK_LMENU] & 0x80) ? MOD_META : 0; + mods |= (keymap [VK_RCONTROL] & 0x80) ? MOD_CONTROL : 0; + } + else + { + mods |= (keymap [VK_MENU] & 0x80) ? MOD_META : 0; + mods |= (keymap [VK_CONTROL] & 0x80) ? MOD_CONTROL : 0; + } + + mods |= (keymap [VK_SHIFT] & 0x80) ? MOD_SHIFT : 0; + + return mods; +} + +/* + * Translate a mswindows virtual key to a keysym. + * Only returns non-Qnil for keys that don't generate WM_CHAR messages + * or whose ASCII codes (like space) xemacs doesn't like. + * Virtual key values are defined in winresrc.h + * XXX I'm not sure that KEYSYM("name") is the best thing to use here. + */ +Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods) +{ + switch (mswindows_key) + { + /* First the predefined ones */ + case VK_BACK: return QKbackspace; + case VK_TAB: return QKtab; + case '\n': return QKlinefeed; /* No VK_LINEFEED in winresrc.h */ + case VK_RETURN: return QKreturn; + case VK_ESCAPE: return QKescape; + case VK_SPACE: return QKspace; + case VK_DELETE: return QKdelete; + + /* The rest */ + case VK_CLEAR: return KEYSYM ("clear"); /* Should do ^L ? */ + case VK_PRIOR: return KEYSYM ("prior"); + case VK_NEXT: return KEYSYM ("next"); + case VK_END: return KEYSYM ("end"); + case VK_HOME: return KEYSYM ("home"); + case VK_LEFT: return KEYSYM ("left"); + case VK_UP: return KEYSYM ("up"); + case VK_RIGHT: return KEYSYM ("right"); + case VK_DOWN: return KEYSYM ("down"); + case VK_SELECT: return KEYSYM ("select"); + case VK_PRINT: return KEYSYM ("print"); + case VK_EXECUTE: return KEYSYM ("execute"); + case VK_SNAPSHOT: return KEYSYM ("print"); + case VK_INSERT: return KEYSYM ("insert"); + case VK_HELP: return KEYSYM ("help"); +#if 0 /* XXX What are these supposed to do? */ + case VK_LWIN return KEYSYM (""); + case VK_RWIN return KEYSYM (""); +#endif + case VK_APPS: return KEYSYM ("menu"); + case VK_F1: return KEYSYM ("f1"); + case VK_F2: return KEYSYM ("f2"); + case VK_F3: return KEYSYM ("f3"); + case VK_F4: return KEYSYM ("f4"); + case VK_F5: return KEYSYM ("f5"); + case VK_F6: return KEYSYM ("f6"); + case VK_F7: return KEYSYM ("f7"); + case VK_F8: return KEYSYM ("f8"); + case VK_F9: return KEYSYM ("f9"); + case VK_F10: return KEYSYM ("f10"); + case VK_F11: return KEYSYM ("f11"); + case VK_F12: return KEYSYM ("f12"); + case VK_F13: return KEYSYM ("f13"); + case VK_F14: return KEYSYM ("f14"); + case VK_F15: return KEYSYM ("f15"); + case VK_F16: return KEYSYM ("f16"); + case VK_F17: return KEYSYM ("f17"); + case VK_F18: return KEYSYM ("f18"); + case VK_F19: return KEYSYM ("f19"); + case VK_F20: return KEYSYM ("f20"); + case VK_F21: return KEYSYM ("f21"); + case VK_F22: return KEYSYM ("f22"); + case VK_F23: return KEYSYM ("f23"); + case VK_F24: return KEYSYM ("f24"); + } + return Qnil; +} + +/* + * Find the console that matches the supplied mswindows window handle + */ +Lisp_Object +mswindows_find_console (HWND hwnd) +{ + Lisp_Object concons; + + CONSOLE_LOOP (concons) + { + Lisp_Object console = XCAR (concons); + /* We only support one console so this must be it */ + return console; + } + + return Qnil; +} + +/* + * Find the frame that matches the supplied mswindows window handle + */ +static Lisp_Object +mswindows_find_frame (HWND hwnd) +{ + return (Lisp_Object) GetWindowLong (hwnd, XWL_FRAMEOBJ); +} + + /************************************************************************/ /* methods */ @@ -554,7 +1398,8 @@ if (milliseconds < 1) milliseconds = 1; ++mswindows_pending_timers_count; - return SetTimer (NULL, 0, milliseconds, mswindows_wm_timer_callback); + return SetTimer (NULL, 0, milliseconds, + (TIMERPROC) mswindows_wm_timer_callback); } static void @@ -617,11 +1462,6 @@ static void emacs_mswindows_handle_magic_event (struct Lisp_Event *emacs_event) { -#if 0 - stderr_out("magic %x, (%d,%d), (%d,%d)\n", - EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event), - rect->left, rect->top, rect->right, rect->bottom); -#endif switch (EVENT_MSWINDOWS_MAGIC_TYPE(emacs_event)) { case WM_SETFOCUS: @@ -662,7 +1502,7 @@ } break; - /* XXX What about Enter & Leave */ + /* #### What about Enter & Leave */ #if 0 va_run_hook_with_args (in_p ? Qmouse_enter_frame_hook : Qmouse_leave_frame_hook, 1, frame);