X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/3cb20f4a4934e0eae4cfae7ea1ee0009b743584e..296744455fa643f346c6239a9333bebb3842aaf6:/src/w32fns.c diff --git a/src/w32fns.c b/src/w32fns.c index f34c215227..4753caf796 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -1,5 +1,5 @@ /* Functions for the Win32 window system. - Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation. + Copyright (C) 1989, 92, 93, 94, 95, 1996 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -49,6 +49,10 @@ Lisp_Object Vwin32_color_map; /* Non nil if alt key presses are passed on to Windows. */ Lisp_Object Vwin32_pass_alt_to_system; +/* Non nil if alt key is translated to meta_modifier, nil if it is translated + to alt_modifier. */ +Lisp_Object Vwin32_alt_is_meta; + /* Non nil if left window, right window, and application key events are passed on to Windows. */ Lisp_Object Vwin32_pass_optional_keys_to_system; @@ -64,6 +68,10 @@ Lisp_Object Vwin32_enable_palette; be converted to a middle button down event. */ Lisp_Object Vwin32_mouse_button_tolerance; +/* Minimum interval between mouse movement (and scroll bar drag) + events that are passed on to the event loop. */ +Lisp_Object Vwin32_mouse_move_interval; + /* The name we're using in resource queries. */ Lisp_Object Vx_resource_name; @@ -126,7 +134,6 @@ Lisp_Object Qborder_width; Lisp_Object Qbox; Lisp_Object Qcursor_color; Lisp_Object Qcursor_type; -Lisp_Object Qfont; Lisp_Object Qforeground_color; Lisp_Object Qgeometry; Lisp_Object Qicon_left; @@ -157,8 +164,13 @@ Lisp_Object Qdisplay; #define RMOUSE 4 static int button_state = 0; -static Win32Msg saved_mouse_msg; -static unsigned timer_id; /* non-zero when timer is active */ +static Win32Msg saved_mouse_button_msg; +static unsigned mouse_button_timer; /* non-zero when timer is active */ +static Win32Msg saved_mouse_move_msg; +static unsigned mouse_move_timer; + +#define MOUSE_BUTTON_ID 1 +#define MOUSE_MOVE_ID 2 /* The below are defined in frame.c. */ extern Lisp_Object Qheight, Qminibuffer, Qname, Qonly, Qwidth; @@ -2641,7 +2653,8 @@ win32_get_modifiers () { return (((GetKeyState (VK_SHIFT)&0x8000) ? shift_modifier : 0) | ((GetKeyState (VK_CONTROL)&0x8000) ? ctrl_modifier : 0) | - ((GetKeyState (VK_MENU)&0x8000) ? meta_modifier : 0)); + ((GetKeyState (VK_MENU)&0x8000) ? + ((NILP (Vwin32_alt_is_meta)) ? alt_modifier : meta_modifier) : 0)); } void @@ -2746,14 +2759,52 @@ record_keyup (unsigned int wparam, unsigned int lparam) static void reset_modifiers () { + SHORT ctrl, alt; + if (!modifiers_recorded) return; - bzero (modifiers, sizeof (modifiers)); + + ctrl = GetAsyncKeyState (VK_CONTROL); + alt = GetAsyncKeyState (VK_MENU); + + if (ctrl == 0 || alt == 0) + /* Emacs doesn't have keyboard focus. Do nothing. */ + return; + + if (!(ctrl & 0x08000)) + /* Clear any recorded control modifier state. */ + modifiers[EMACS_RCONTROL] = modifiers[EMACS_LCONTROL] = 0; + + if (!(alt & 0x08000)) + /* Clear any recorded alt modifier state. */ + modifiers[EMACS_RMENU] = modifiers[EMACS_LMENU] = 0; + + /* Otherwise, leave the modifier state as it was when Emacs lost + keyboard focus. */ +} + +/* Synchronize modifier state with what is reported with the current + keystroke. Even if we cannot distinguish between left and right + modifier keys, we know that, if no modifiers are set, then neither + the left or right modifier should be set. */ +static void +sync_modifiers () +{ + if (!modifiers_recorded) + return; + + if (!(GetKeyState (VK_CONTROL) & 0x8000)) + modifiers[EMACS_RCONTROL] = modifiers[EMACS_LCONTROL] = 0; + + if (!(GetKeyState (VK_MENU) & 0x8000)) + modifiers[EMACS_RMENU] = modifiers[EMACS_LMENU] = 0; } static int modifier_set (int vkey) { + if (vkey == VK_CAPITAL) + return (GetKeyState (vkey) & 0x1); if (!modifiers_recorded) return (GetKeyState (vkey) & 0x8000); @@ -2767,8 +2818,6 @@ modifier_set (int vkey) return modifiers[EMACS_LMENU]; case VK_RMENU: return modifiers[EMACS_RMENU]; - case VK_CAPITAL: - return (GetKeyState (vkey) & 0x1); default: break; } @@ -2841,14 +2890,6 @@ win_msg_worker (dw) { switch (msg.message) { - case WM_TIMER: - if (saved_mouse_msg.msg.hwnd) - { - post_msg (&saved_mouse_msg); - saved_mouse_msg.msg.hwnd = 0; - } - timer_id = 0; - break; case WM_EMACS_CREATEWINDOW: win32_createwindow ((struct frame *) msg.wParam); PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0); @@ -2888,21 +2929,43 @@ win32_wnd_proc (hwnd, msg, wParam, lParam) LRESULT ret = 1; struct win32_display_info *dpyinfo = &one_win32_display_info; Win32Msg wmsg; - + int windows_translate; + + /* Note that it is okay to call x_window_to_frame, even though we are + not running in the main lisp thread, because frame deletion + requires the lisp thread to synchronize with this thread. Thus, if + a frame struct is returned, it can be used without concern that the + lisp thread might make it disappear while we are using it. + + NB. Walking the frame list in this thread is safe (as long as + writes of Lisp_Object slots are atomic, which they are on Windows). + Although delete-frame can destructively modify the frame list while + we are walking it, a garbage collection cannot occur until after + delete-frame has synchronized with this thread. + + It is also safe to use functions that make GDI calls, such as + win32_clear_rect, because these functions must obtain a DC handle + from the frame struct using get_frame_dc which is thread-aware. */ + switch (msg) { case WM_ERASEBKGND: - enter_crit (); - GetUpdateRect (hwnd, &wmsg.rect, FALSE); - leave_crit (); - my_post_msg (&wmsg, hwnd, msg, wParam, lParam); + f = x_window_to_frame (dpyinfo, hwnd); + if (f) + { + GetUpdateRect (hwnd, &wmsg.rect, FALSE); + win32_clear_rect (f, NULL, &wmsg.rect); + } return 1; case WM_PALETTECHANGED: /* ignore our own changes */ if ((HWND)wParam != hwnd) { - /* simply notify main thread it may need to update frames */ - my_post_msg (&wmsg, hwnd, msg, wParam, lParam); + f = x_window_to_frame (dpyinfo, hwnd); + if (f) + /* get_frame_dc will realize our palette and force all + frames to be redrawn if needed. */ + release_frame_dc (f, get_frame_dc (f)); } return 0; case WM_PAINT: @@ -2927,28 +2990,34 @@ win32_wnd_proc (hwnd, msg, wParam, lParam) case WM_KEYDOWN: case WM_SYSKEYDOWN: + /* Synchronize modifiers with current keystroke. */ + sync_modifiers (); + record_keydown (wParam, lParam); wParam = map_keypad_keys (wParam, lParam); - + + windows_translate = 0; switch (wParam) { case VK_LWIN: case VK_RWIN: case VK_APPS: /* More support for these keys will likely be necessary. */ if (!NILP (Vwin32_pass_optional_keys_to_system)) - goto dflt; + windows_translate = 1; break; case VK_MENU: if (NILP (Vwin32_pass_alt_to_system)) return 0; - else - goto dflt; + windows_translate = 1; + break; case VK_CONTROL: case VK_CAPITAL: case VK_SHIFT: - /* Pass on to Windows. */ - goto dflt; + case VK_NUMLOCK: + case VK_SCROLL: + windows_translate = 1; + break; default: /* If not defined as a function key, change it to a WM_CHAR message. */ if (lispy_function_keys[wParam] == 0) @@ -2956,6 +3025,15 @@ win32_wnd_proc (hwnd, msg, wParam, lParam) break; } + if (windows_translate) + { + MSG winmsg = { hwnd, msg, wParam, lParam, 0, {0,0} }; + + winmsg.time = GetMessageTime (); + TranslateMessage (&winmsg); + goto dflt; + } + /* Fall through */ case WM_SYSCHAR: @@ -2965,6 +3043,7 @@ win32_wnd_proc (hwnd, msg, wParam, lParam) enter_crit (); my_post_msg (&wmsg, hwnd, msg, wParam, lParam); +#if 1 /* Detect quit_char and set quit-flag directly. Note that we dow this *after* posting the message to ensure the main thread will be woken up if blocked in sys_select(). */ @@ -2976,6 +3055,8 @@ win32_wnd_proc (hwnd, msg, wParam, lParam) if (c == quit_char) Vquit_flag = Qt; } +#endif + leave_crit (); break; @@ -3000,10 +3081,10 @@ win32_wnd_proc (hwnd, msg, wParam, lParam) if (button_state & other) { - if (timer_id) + if (mouse_button_timer) { - KillTimer (NULL, timer_id); - timer_id = 0; + KillTimer (hwnd, mouse_button_timer); + mouse_button_timer = 0; /* Generate middle mouse event instead. */ msg = WM_MBUTTONDOWN; @@ -3020,25 +3101,25 @@ win32_wnd_proc (hwnd, msg, wParam, lParam) else { /* Flush out saved message. */ - post_msg (&saved_mouse_msg); + post_msg (&saved_mouse_button_msg); } wmsg.dwModifiers = win32_get_modifiers (); my_post_msg (&wmsg, hwnd, msg, wParam, lParam); /* Clear message buffer. */ - saved_mouse_msg.msg.hwnd = 0; + saved_mouse_button_msg.msg.hwnd = 0; } else { /* Hold onto message for now. */ - timer_id = - SetTimer (NULL, 0, XINT (Vwin32_mouse_button_tolerance), NULL); - saved_mouse_msg.msg.hwnd = hwnd; - saved_mouse_msg.msg.message = msg; - saved_mouse_msg.msg.wParam = wParam; - saved_mouse_msg.msg.lParam = lParam; - saved_mouse_msg.msg.time = GetMessageTime (); - saved_mouse_msg.dwModifiers = win32_get_modifiers (); + mouse_button_timer = + SetTimer (hwnd, MOUSE_BUTTON_ID, XINT (Vwin32_mouse_button_tolerance), NULL); + saved_mouse_button_msg.msg.hwnd = hwnd; + saved_mouse_button_msg.msg.message = msg; + saved_mouse_button_msg.msg.wParam = wParam; + saved_mouse_button_msg.msg.lParam = lParam; + saved_mouse_button_msg.msg.time = GetMessageTime (); + saved_mouse_button_msg.dwModifiers = win32_get_modifiers (); } } return 0; @@ -3073,18 +3154,18 @@ win32_wnd_proc (hwnd, msg, wParam, lParam) else { /* Flush out saved message if necessary. */ - if (saved_mouse_msg.msg.hwnd) + if (saved_mouse_button_msg.msg.hwnd) { - post_msg (&saved_mouse_msg); + post_msg (&saved_mouse_button_msg); } } wmsg.dwModifiers = win32_get_modifiers (); my_post_msg (&wmsg, hwnd, msg, wParam, lParam); /* Always clear message buffer and cancel timer. */ - saved_mouse_msg.msg.hwnd = 0; - KillTimer (NULL, timer_id); - timer_id = 0; + saved_mouse_button_msg.msg.hwnd = 0; + KillTimer (hwnd, mouse_button_timer); + mouse_button_timer = 0; if (button_state == 0) ReleaseCapture (); @@ -3108,33 +3189,73 @@ win32_wnd_proc (hwnd, msg, wParam, lParam) my_post_msg (&wmsg, hwnd, msg, wParam, lParam); return 0; -#if 0 + case WM_VSCROLL: case WM_MOUSEMOVE: - /* Flush out saved message if necessary. */ - if (saved_mouse_msg.msg.hwnd) + if (XINT (Vwin32_mouse_move_interval) <= 0 + || (msg == WM_MOUSEMOVE && button_state == 0)) + { + wmsg.dwModifiers = win32_get_modifiers (); + my_post_msg (&wmsg, hwnd, msg, wParam, lParam); + return 0; + } + + /* Hang onto mouse move and scroll messages for a bit, to avoid + sending such events to Emacs faster than it can process them. + If we get more events before the timer from the first message + expires, we just replace the first message. */ + + if (saved_mouse_move_msg.msg.hwnd == 0) + mouse_move_timer = + SetTimer (hwnd, MOUSE_MOVE_ID, XINT (Vwin32_mouse_move_interval), NULL); + + /* Hold onto message for now. */ + saved_mouse_move_msg.msg.hwnd = hwnd; + saved_mouse_move_msg.msg.message = msg; + saved_mouse_move_msg.msg.wParam = wParam; + saved_mouse_move_msg.msg.lParam = lParam; + saved_mouse_move_msg.msg.time = GetMessageTime (); + saved_mouse_move_msg.dwModifiers = win32_get_modifiers (); + + return 0; + + case WM_TIMER: + /* Flush out saved messages if necessary. */ + if (wParam == mouse_button_timer) { - wmsg = saved_mouse_msg; - my_post_msg (&wmsg, wmsg.msg.hwnd, wmsg.msg.message, - wmsg.msg.wParam, wmsg.msg.lParam); + if (saved_mouse_button_msg.msg.hwnd) + { + post_msg (&saved_mouse_button_msg); + saved_mouse_button_msg.msg.hwnd = 0; + } + KillTimer (hwnd, mouse_button_timer); + mouse_button_timer = 0; + } + else if (wParam == mouse_move_timer) + { + if (saved_mouse_move_msg.msg.hwnd) + { + post_msg (&saved_mouse_move_msg); + saved_mouse_move_msg.msg.hwnd = 0; + } + KillTimer (hwnd, mouse_move_timer); + mouse_move_timer = 0; } - wmsg.dwModifiers = win32_get_modifiers (); - my_post_msg (&wmsg, hwnd, msg, wParam, lParam); - - /* Always clear message buffer and cancel timer. */ - saved_mouse_msg.msg.hwnd = 0; - KillTimer (NULL, timer_id); - timer_id = 0; - return 0; -#endif + + case WM_NCACTIVATE: + /* Windows doesn't send us focus messages when putting up and + taking down a system popup dialog as for Ctrl-Alt-Del on Win95. + The only indication we get that something happened is receiving + this message afterwards. So this is a good time to reset our + keyboard modifiers' state. */ + reset_modifiers (); + goto dflt; case WM_SETFOCUS: reset_modifiers (); - case WM_MOUSEMOVE: + case WM_KILLFOCUS: case WM_MOVE: case WM_SIZE: - case WM_KILLFOCUS: - case WM_VSCROLL: case WM_SYSCOMMAND: case WM_COMMAND: wmsg.dwModifiers = win32_get_modifiers (); @@ -3892,7 +4013,7 @@ x_to_win32_font (lpxstr, lplogfont) memset (lplogfont, 0, sizeof (*lplogfont)); -#if 0 +#if 1 lplogfont->lfOutPrecision = OUT_DEFAULT_PRECIS; lplogfont->lfClipPrecision = CLIP_DEFAULT_PRECIS; lplogfont->lfQuality = DEFAULT_QUALITY; @@ -4836,8 +4957,6 @@ syms_of_win32fns () staticpro (&Qcursor_color); Qcursor_type = intern ("cursor-type"); staticpro (&Qcursor_type); - Qfont = intern ("font"); - staticpro (&Qfont); Qforeground_color = intern ("foreground-color"); staticpro (&Qforeground_color); Qgeometry = intern ("geometry"); @@ -4901,6 +5020,11 @@ When non-nil, for example, alt pressed and released and then space will\n\ open the System menu. When nil, Emacs silently swallows alt key events."); Vwin32_pass_alt_to_system = Qnil; + DEFVAR_LISP ("win32-alt-is-meta", &Vwin32_alt_is_meta, + "Non-nil if the alt key is to be considered the same as the meta key.\n\ +When nil, Emacs will translate the alt key to the Alt modifier, and not Meta."); + Vwin32_alt_is_meta = Qt; + DEFVAR_LISP ("win32-pass-optional-keys-to-system", &Vwin32_pass_optional_keys_to_system, "Non-nil if the 'optional' keys (left window, right window,\n\ @@ -4924,6 +5048,14 @@ If both mouse buttons are depressed within this interval, a middle mouse\n\ button down event is generated instead."); XSETINT (Vwin32_mouse_button_tolerance, GetDoubleClickTime () / 2); + DEFVAR_INT ("win32-mouse-move-interval", + &Vwin32_mouse_move_interval, + "Minimum interval between mouse move events.\n\ +The value is the minimum time in milliseconds that must elapse between\n\ +successive mouse move (or scroll bar drag) events before they are\n\ +reported as lisp events."); + XSETINT (Vwin32_mouse_move_interval, 50); + init_x_parm_symbols (); DEFVAR_LISP ("x-bitmap-file-path", &Vx_bitmap_file_path,