/* 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.
/* 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;
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;
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;
#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;
{
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
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);
return modifiers[EMACS_LMENU];
case VK_RMENU:
return modifiers[EMACS_RMENU];
- case VK_CAPITAL:
- return (GetKeyState (vkey) & 0x1);
default:
break;
}
{
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);
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:
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)
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:
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(). */
if (c == quit_char)
Vquit_flag = Qt;
}
+#endif
+
leave_crit ();
break;
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;
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;
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 ();
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 ();
memset (lplogfont, 0, sizeof (*lplogfont));
-#if 0
+#if 1
lplogfont->lfOutPrecision = OUT_DEFAULT_PRECIS;
lplogfont->lfClipPrecision = CLIP_DEFAULT_PRECIS;
lplogfont->lfQuality = DEFAULT_QUALITY;
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");
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\
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,