X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/52beda922d2cb523a03661bf74b8678c8b45e440..0a2aedfe6d650e825a50f25f972bac20d669f5cb:/src/w32fns.c diff --git a/src/w32fns.c b/src/w32fns.c index 99fd3ba33f..20e09d8a46 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -1,13 +1,13 @@ /* Graphical user interface functions for the Microsoft Windows API. -Copyright (C) 1989, 1992-2015 Free Software Foundation, Inc. +Copyright (C) 1989, 1992-2016 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -20,6 +20,9 @@ along with GNU Emacs. If not, see . */ /* Added by Kevin Gallo */ #include +/* Override API version to get the latest functionality. */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 #include #include @@ -35,24 +38,15 @@ along with GNU Emacs. If not, see . */ #include "w32term.h" #include "frame.h" #include "window.h" -#include "character.h" #include "buffer.h" -#include "intervals.h" -#include "dispextern.h" #include "keyboard.h" #include "blockinput.h" -#include "epaths.h" -#include "charset.h" #include "coding.h" -#include "ccl.h" -#include "fontset.h" -#include "systime.h" -#include "termhooks.h" #include "w32common.h" +#include "w32inevt.h" #ifdef WINDOWSNT -#include "w32heap.h" #include #endif /* WINDOWSNT */ @@ -62,11 +56,12 @@ along with GNU Emacs. If not, see . */ #include "w32.h" #endif -#include "bitmaps/gray.xbm" - +#include +#include #include #include #include +#include #include #include #include @@ -75,22 +70,14 @@ along with GNU Emacs. If not, see . */ #include #include -#include "font.h" -#include "w32font.h" - #ifndef FOF_NO_CONNECTED_ELEMENTS #define FOF_NO_CONNECTED_ELEMENTS 0x2000 #endif -void syms_of_w32fns (void); -void globals_of_w32fns (void); - -extern void free_frame_menubar (struct frame *); extern int w32_console_toggle_lock_key (int, Lisp_Object); extern void w32_menu_display_help (HWND, HMENU, UINT, UINT); extern void w32_free_menu_strings (HWND); extern const char *map_w32_filename (const char *, const char **); -extern char * w32_strerror (int error_no); #ifndef IDC_HAND #define IDC_HAND MAKEINTRESOURCE(32649) @@ -199,11 +186,7 @@ MonitorFromWindow_Proc monitor_from_window_fn = NULL; EnumDisplayMonitors_Proc enum_display_monitors_fn = NULL; GetTitleBarInfo_Proc get_title_bar_info_fn = NULL; -#ifdef NTGUI_UNICODE -#define unicode_append_menu AppendMenuW -#else /* !NTGUI_UNICODE */ extern AppendMenuW_Proc unicode_append_menu; -#endif /* NTGUI_UNICODE */ /* Flag to selectively ignore WM_IME_CHAR messages. */ static int ignore_ime_char = 0; @@ -230,7 +213,6 @@ static HWND w32_visible_system_caret_hwnd; static int w32_unicode_gui; /* From w32menu.c */ -extern HMENU current_popup_menu; int menubar_in_use = 0; /* From w32uniscribe.c */ @@ -265,6 +247,38 @@ HINSTANCE hinst = NULL; static unsigned int sound_type = 0xFFFFFFFF; #define MB_EMACS_SILENT (0xFFFFFFFF - 1) +/* Special virtual key code for indicating "any" key. */ +#define VK_ANY 0xFF + +#ifndef WM_WTSSESSION_CHANGE +/* 32-bit MinGW does not define these constants. */ +# define WM_WTSSESSION_CHANGE 0x02B1 +# define WTS_SESSION_LOCK 0x7 +#endif + +/* Keyboard hook state data. */ +static struct +{ + int hook_count; /* counter, if several windows are created */ + HHOOK hook; /* hook handle */ + HWND console; /* console window handle */ + + int lwindown; /* Left Windows key currently pressed (and hooked) */ + int rwindown; /* Right Windows key currently pressed (and hooked) */ + int winsdown; /* Number of handled keys currently pressed */ + int send_win_up; /* Pass through the keyup for this Windows key press? */ + int suppress_lone; /* Suppress simulated Windows keydown-keyup for this press? */ + int winseen; /* Windows keys seen during this press? */ + + char alt_hooked[256]; /* hook Alt+[this key]? */ + char lwin_hooked[256]; /* hook left Win+[this key]? */ + char rwin_hooked[256]; /* hook right Win+[this key]? */ +} kbdhook; +typedef HWND (WINAPI *GetConsoleWindow_Proc) (void); + +/* stdin, from w32console.c */ +extern HANDLE keyboard_handle; + /* Let the user specify a display with a frame. nil stands for the selected frame--or, if that is not a w32 frame, the first display on the list. */ @@ -341,10 +355,7 @@ void x_set_cursor_type (struct frame *, Lisp_Object, Lisp_Object); void x_set_icon_type (struct frame *, Lisp_Object, Lisp_Object); void x_set_icon_name (struct frame *, Lisp_Object, Lisp_Object); void x_explicitly_set_name (struct frame *, Lisp_Object, Lisp_Object); -void x_set_menu_bar_lines (struct frame *, Lisp_Object, Lisp_Object); void x_set_title (struct frame *, Lisp_Object, Lisp_Object); -void x_set_tool_bar_lines (struct frame *, Lisp_Object, Lisp_Object); -void x_set_internal_border_width (struct frame *f, Lisp_Object, Lisp_Object); /* Store the screen positions of frame F into XPTR and YPTR. @@ -467,7 +478,7 @@ if the entry is new. */) /* The default colors for the w32 color map */ typedef struct colormap_t { - char *name; + const char *name; COLORREF colorref; } colormap_t; @@ -759,7 +770,7 @@ w32_color_map_lookup (const char *colorname) tem = XCAR (elt); - if (lstrcmpi (SDATA (tem), colorname) == 0) + if (lstrcmpi (SSDATA (tem), colorname) == 0) { ret = Fcdr (elt); break; @@ -802,10 +813,10 @@ add_system_logical_colors_to_map (Lisp_Object *system_colors) strcpy (full_name_buffer, SYSTEM_COLOR_PREFIX); while (RegEnumValueA (colors_key, index, name_buffer, &name_size, - NULL, NULL, color_buffer, &color_size) + NULL, NULL, (LPBYTE)color_buffer, &color_size) == ERROR_SUCCESS) { - int r, g, b; + unsigned r, g, b; if (sscanf (color_buffer, " %u %u %u", &r, &g, &b) == 3) *system_colors = Fcons (Fcons (build_string (full_name_buffer), make_number (RGB (r, g, b))), @@ -1220,16 +1231,16 @@ w32_defined_color (struct frame *f, const char *color, XColor *color_def, If F is not a color screen, return DEF (default) regardless of what ARG says. */ -int +static int x_decode_color (struct frame *f, Lisp_Object arg, int def) { XColor cdef; CHECK_STRING (arg); - if (strcmp (SDATA (arg), "black") == 0) + if (strcmp (SSDATA (arg), "black") == 0) return BLACK_PIX_DEFAULT (f); - else if (strcmp (SDATA (arg), "white") == 0) + else if (strcmp (SSDATA (arg), "white") == 0) return WHITE_PIX_DEFAULT (f); if ((FRAME_DISPLAY_INFO (f)->n_planes * FRAME_DISPLAY_INFO (f)->n_cbits) == 1) @@ -1237,7 +1248,7 @@ x_decode_color (struct frame *f, Lisp_Object arg, int def) /* w32_defined_color is responsible for coping with failures by looking for a near-miss. */ - if (w32_defined_color (f, SDATA (arg), &cdef, true)) + if (w32_defined_color (f, SSDATA (arg), &cdef, true)) return cdef.pixel; /* defined_color failed; return an ultimate default. */ @@ -1299,8 +1310,10 @@ x_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) void x_set_mouse_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { +#if 0 Cursor cursor, nontext_cursor, mode_cursor, hand_cursor; int count; +#endif int mask_color; if (!EQ (Qnil, arg)) @@ -1501,7 +1514,7 @@ x_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) Note that this does not fully take effect if done before F has a window. */ -void +static void x_set_border_pixel (struct frame *f, int pix) { @@ -1612,7 +1625,7 @@ x_set_icon_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval) #endif } -void +static void x_clear_under_internal_border (struct frame *f) { int border = FRAME_INTERNAL_BORDER_WIDTH (f); @@ -1645,7 +1658,7 @@ x_set_internal_border_width (struct frame *f, Lisp_Object arg, Lisp_Object oldva if (border != FRAME_INTERNAL_BORDER_WIDTH (f)) { - FRAME_INTERNAL_BORDER_WIDTH (f) = border; + f->internal_border_width = border; if (FRAME_X_WINDOW (f) != 0) { @@ -1678,10 +1691,7 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) FRAME_MENU_BAR_LINES (f) = 0; FRAME_MENU_BAR_HEIGHT (f) = 0; if (nlines) - { - FRAME_EXTERNAL_MENU_BAR (f) = 1; - windows_or_buffers_changed = 23; - } + FRAME_EXTERNAL_MENU_BAR (f) = 1; else { if (FRAME_EXTERNAL_MENU_BAR (f) == 1) @@ -1733,11 +1743,9 @@ x_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) void x_change_tool_bar_height (struct frame *f, int height) { - Lisp_Object frame; int unit = FRAME_LINE_HEIGHT (f); int old_height = FRAME_TOOL_BAR_HEIGHT (f); int lines = (height + unit - 1) / unit; - int old_text_height = FRAME_TEXT_HEIGHT (f); Lisp_Object fullscreen; /* Make sure we redisplay all windows in this frame. */ @@ -1761,14 +1769,24 @@ x_change_tool_bar_height (struct frame *f, int height) /* Recalculate toolbar height. */ f->n_tool_bar_rows = 0; + if (old_height == 0 + && (!f->after_make_frame + || NILP (frame_inhibit_implied_resize) + || (CONSP (frame_inhibit_implied_resize) + && NILP (Fmemq (Qtool_bar_lines, frame_inhibit_implied_resize))))) + f->tool_bar_redisplayed = f->tool_bar_resized = false; adjust_frame_size (f, -1, -1, - ((NILP (fullscreen = get_frame_param (f, Qfullscreen)) - || EQ (fullscreen, Qfullwidth)) ? 1 + ((!f->tool_bar_resized + && (NILP (fullscreen = + get_frame_param (f, Qfullscreen)) + || EQ (fullscreen, Qfullwidth))) ? 1 : (old_height == 0 || height == 0) ? 2 : 4), false, Qtool_bar_lines); + f->tool_bar_resized = f->tool_bar_redisplayed; + /* adjust_frame_size might not have done anything, garbage frame here. */ adjust_frame_glyphs (f); @@ -1833,7 +1851,7 @@ w32_set_title_bar_text (struct frame *f, Lisp_Object name) suggesting a new name, which lisp code should override; if F->explicit_name is set, ignore the new name; otherwise, set it. */ -void +static void x_set_name (struct frame *f, Lisp_Object name, bool explicit) { /* Make sure that requests from lisp code override requests from @@ -1856,7 +1874,7 @@ x_set_name (struct frame *f, Lisp_Object name, bool explicit) /* Check for no change needed in this very common case before we do any consing. */ if (!strcmp (FRAME_DISPLAY_INFO (f)->w32_id_name, - SDATA (f->name))) + SSDATA (f->name))) return; name = build_string (FRAME_DISPLAY_INFO (f)->w32_id_name); } @@ -1938,6 +1956,8 @@ x_set_scroll_bar_default_height (struct frame *f) /* Subroutines for creating a frame. */ +Cursor w32_load_cursor (LPCTSTR); + Cursor w32_load_cursor (LPCTSTR name) { @@ -2081,6 +2101,352 @@ my_post_msg (W32Msg * wmsg, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) post_msg (wmsg); } +#ifdef WINDOWSNT +/* The Windows keyboard hook callback. */ +static LRESULT CALLBACK +funhook (int code, WPARAM w, LPARAM l) +{ + INPUT inputs[2]; + HWND focus = GetFocus (); + int console = 0; + KBDLLHOOKSTRUCT const *hs = (KBDLLHOOKSTRUCT*)l; + + if (code < 0 || (hs->flags & LLKHF_INJECTED)) + return CallNextHookEx (0, code, w, l); + + /* The keyboard hook sees keyboard input on all processes (except + elevated ones, when Emacs itself is not elevated). As such, + care must be taken to only filter out keyboard input when Emacs + itself is on the foreground. + + GetFocus returns a non-NULL window if another application is active, + and always for a console Emacs process. For a console Emacs, determine + focus by checking if the current foreground window is the process's + console window. */ + if (focus == NULL && kbdhook.console != NULL) + { + if (GetForegroundWindow () == kbdhook.console) + { + focus = kbdhook.console; + console = 1; + } + } + + /* First, check hooks for the left and right Windows keys. */ + if (hs->vkCode == VK_LWIN || hs->vkCode == VK_RWIN) + { + if (focus != NULL && (w == WM_KEYDOWN || w == WM_SYSKEYDOWN)) + { + /* The key is being pressed in an Emacs window. */ + if (hs->vkCode == VK_LWIN && !kbdhook.lwindown) + { + kbdhook.lwindown = 1; + kbdhook.winseen = 1; + kbdhook.winsdown++; + } + else if (hs->vkCode == VK_RWIN && !kbdhook.rwindown) + { + kbdhook.rwindown = 1; + kbdhook.winseen = 1; + kbdhook.winsdown++; + } + /* Returning 1 here drops the keypress without further processing. + If the keypress was allowed to go through, the normal Windows + hotkeys would take over. */ + return 1; + } + else if (kbdhook.winsdown > 0 && (w == WM_KEYUP || w == WM_SYSKEYUP)) + { + /* A key that has been captured earlier is being released now. */ + if (hs->vkCode == VK_LWIN && kbdhook.lwindown) + { + kbdhook.lwindown = 0; + kbdhook.winsdown--; + } + else if (hs->vkCode == VK_RWIN && kbdhook.rwindown) + { + kbdhook.rwindown = 0; + kbdhook.winsdown--; + } + if (kbdhook.winsdown == 0 && kbdhook.winseen) + { + if (!kbdhook.suppress_lone) + { + /* The Windows key was pressed, then released, + without any other key pressed simultaneously. + Normally, this opens the Start menu, but the user + can prevent this by setting the + w32-pass-[lr]window-to-system variable to + NIL. */ + if ((hs->vkCode == VK_LWIN && !NILP (Vw32_pass_lwindow_to_system)) || + (hs->vkCode == VK_RWIN && !NILP (Vw32_pass_rwindow_to_system))) + { + /* Not prevented - Simulate the keypress to the system. */ + memset (inputs, 0, sizeof (inputs)); + inputs[0].type = INPUT_KEYBOARD; + inputs[0].ki.wVk = hs->vkCode; + inputs[0].ki.wScan = hs->vkCode; + inputs[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY; + inputs[0].ki.time = 0; + inputs[1].type = INPUT_KEYBOARD; + inputs[1].ki.wVk = hs->vkCode; + inputs[1].ki.wScan = hs->vkCode; + inputs[1].ki.dwFlags + = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; + inputs[1].ki.time = 0; + SendInput (2, inputs, sizeof (INPUT)); + } + else if (focus != NULL) + { + /* When not passed to system, must simulate privately to Emacs. */ + PostMessage (focus, WM_SYSKEYDOWN, hs->vkCode, 0); + PostMessage (focus, WM_SYSKEYUP, hs->vkCode, 0); + } + } + } + if (kbdhook.winsdown == 0) + { + /* No Windows keys pressed anymore - clear the state flags. */ + kbdhook.suppress_lone = 0; + kbdhook.winseen = 0; + } + if (!kbdhook.send_win_up) + { + /* Swallow this release message, as not to confuse + applications who did not get to see the original + WM_KEYDOWN message either. */ + return 1; + } + kbdhook.send_win_up = 0; + } + } + else if (kbdhook.winsdown > 0) + { + /* Some other key was pressed while a captured Win key is down. + This is either an Emacs registered hotkey combination, or a + system hotkey. */ + if ((kbdhook.lwindown && kbdhook.lwin_hooked[hs->vkCode]) || + (kbdhook.rwindown && kbdhook.rwin_hooked[hs->vkCode])) + { + /* Hooked Win-x combination, do not pass the keypress to Windows. */ + kbdhook.suppress_lone = 1; + } + else if (!kbdhook.suppress_lone) + { + /* Unhooked S-x combination; simulate the combination now + (will be seen by the system). */ + memset (inputs, 0, sizeof (inputs)); + inputs[0].type = INPUT_KEYBOARD; + inputs[0].ki.wVk = kbdhook.lwindown ? VK_LWIN : VK_RWIN; + inputs[0].ki.wScan = kbdhook.lwindown ? VK_LWIN : VK_RWIN; + inputs[0].ki.dwFlags = KEYEVENTF_EXTENDEDKEY; + inputs[0].ki.time = 0; + inputs[1].type = INPUT_KEYBOARD; + inputs[1].ki.wVk = hs->vkCode; + inputs[1].ki.wScan = hs->scanCode; + inputs[1].ki.dwFlags = + (hs->flags & LLKHF_EXTENDED) ? KEYEVENTF_EXTENDEDKEY : 0; + inputs[1].ki.time = 0; + SendInput (2, inputs, sizeof (INPUT)); + /* Stop processing of this Win sequence here; the + corresponding keyup messages will come through the normal + channel when the keys are released. */ + kbdhook.suppress_lone = 1; + kbdhook.send_win_up = 1; + /* Swallow the original keypress (as we want the Win key + down message simulated above to precede this real message). */ + return 1; + } + } + + /* Next, handle the registered Alt-* combinations. */ + if ((w == WM_SYSKEYDOWN || w == WM_KEYDOWN) + && kbdhook.alt_hooked[hs->vkCode] + && focus != NULL + && (GetAsyncKeyState (VK_MENU) & 0x8000)) + { + /* Prevent the system from getting this Alt-* key - suppress the + message and post as a normal keypress to Emacs. */ + if (console) + { + INPUT_RECORD rec; + DWORD n; + rec.EventType = KEY_EVENT; + rec.Event.KeyEvent.bKeyDown = TRUE; + rec.Event.KeyEvent.wVirtualKeyCode = hs->vkCode; + rec.Event.KeyEvent.wVirtualScanCode = hs->scanCode; + rec.Event.KeyEvent.uChar.UnicodeChar = 0; + rec.Event.KeyEvent.dwControlKeyState = + ((GetAsyncKeyState (VK_LMENU) & 0x8000) ? LEFT_ALT_PRESSED : 0) + | ((GetAsyncKeyState (VK_RMENU) & 0x8000) ? RIGHT_ALT_PRESSED : 0) + | ((GetAsyncKeyState (VK_LCONTROL) & 0x8000) ? LEFT_CTRL_PRESSED : 0) + | ((GetAsyncKeyState (VK_RCONTROL) & 0x8000) ? RIGHT_CTRL_PRESSED : 0) + | ((GetAsyncKeyState (VK_SHIFT) & 0x8000) ? SHIFT_PRESSED : 0) + | ((hs->flags & LLKHF_EXTENDED) ? ENHANCED_KEY : 0); + if (w32_console_unicode_input) + WriteConsoleInputW (keyboard_handle, &rec, 1, &n); + else + WriteConsoleInputA (keyboard_handle, &rec, 1, &n); + } + else + PostMessage (focus, w, hs->vkCode, 1 | (1<<29)); + return 1; + } + + /* The normal case - pass the message through. */ + return CallNextHookEx (0, code, w, l); +} + +/* Set up the hook; can be called several times, with matching + remove_w32_kbdhook calls. */ +void +setup_w32_kbdhook (void) +{ + kbdhook.hook_count++; + + /* Hooking is only available on NT architecture systems, as + indicated by the w32_kbdhook_active variable. */ + if (kbdhook.hook_count == 1 && w32_kbdhook_active) + { + /* Get the handle of the Emacs console window. As the + GetConsoleWindow function is only available on Win2000+, a + hackish workaround described in Microsoft KB article 124103 + (https://support.microsoft.com/en-us/kb/124103) is used for + NT 4 systems. */ + GetConsoleWindow_Proc get_console = (GetConsoleWindow_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), "GetConsoleWindow"); + + if (get_console != NULL) + kbdhook.console = get_console (); + else + { + GUID guid; + wchar_t *oldTitle = malloc (1024 * sizeof(wchar_t)); + wchar_t newTitle[64]; + int i; + + CoCreateGuid (&guid); + StringFromGUID2 (&guid, newTitle, 64); + if (newTitle != NULL) + { + GetConsoleTitleW (oldTitle, 1024); + SetConsoleTitleW (newTitle); + for (i = 0; i < 25; i++) + { + Sleep (40); + kbdhook.console = FindWindowW (NULL, newTitle); + if (kbdhook.console != NULL) + break; + } + SetConsoleTitleW (oldTitle); + } + free (oldTitle); + } + + /* Set the hook. */ + kbdhook.hook = SetWindowsHookEx (WH_KEYBOARD_LL, funhook, + GetModuleHandle (NULL), 0); + } +} + +/* Remove the hook. */ +void +remove_w32_kbdhook (void) +{ + kbdhook.hook_count--; + if (kbdhook.hook_count == 0 && w32_kbdhook_active) + { + UnhookWindowsHookEx (kbdhook.hook); + kbdhook.hook = NULL; + } +} +#endif /* WINDOWSNT */ + +/* Mark a specific key combination as hooked, preventing it to be + handled by the system. */ +static void +hook_w32_key (int hook, int modifier, int vkey) +{ + char *tbl = NULL; + + switch (modifier) + { + case VK_MENU: + tbl = kbdhook.alt_hooked; + break; + case VK_LWIN: + tbl = kbdhook.lwin_hooked; + break; + case VK_RWIN: + tbl = kbdhook.rwin_hooked; + break; + } + + if (tbl != NULL && vkey >= 0 && vkey <= 255) + { + /* VK_ANY hooks all keys for this modifier */ + if (vkey == VK_ANY) + memset (tbl, (char)hook, 256); + else + tbl[vkey] = (char)hook; + /* Alt-s should go through */ + kbdhook.alt_hooked[VK_MENU] = 0; + kbdhook.alt_hooked[VK_LMENU] = 0; + kbdhook.alt_hooked[VK_RMENU] = 0; + kbdhook.alt_hooked[VK_CONTROL] = 0; + kbdhook.alt_hooked[VK_LCONTROL] = 0; + kbdhook.alt_hooked[VK_RCONTROL] = 0; + kbdhook.alt_hooked[VK_SHIFT] = 0; + kbdhook.alt_hooked[VK_LSHIFT] = 0; + kbdhook.alt_hooked[VK_RSHIFT] = 0; + } +} + +#ifdef WINDOWSNT +/* Check the current Win key pressed state. */ +int +check_w32_winkey_state (int vkey) +{ + /* The hook code handles grabbing of the Windows keys and Alt-* key + combinations reserved by the system. Handling Alt is a bit + easier, as Windows intends Alt-* shortcuts for application use in + Windows; hotkeys such as Alt-tab and Alt-escape are special + cases. Win-* hotkeys, on the other hand, are primarily meant for + system use. + + As a result, when we want Emacs to be able to grab the Win-* + keys, we must swallow all Win key presses in a low-level keyboard + hook. Unfortunately, this means that the Emacs window procedure + (and console input handler) never see the keypresses either. + Thus, to check the modifier states properly, Emacs code must use + the check_w32_winkey_state function that uses the flags directly + updated by the hook callback. */ + switch (vkey) + { + case VK_LWIN: + return kbdhook.lwindown; + case VK_RWIN: + return kbdhook.rwindown; + } + return 0; +} +#endif /* WINDOWSNT */ + +/* Reset the keyboard hook state. Locking the workstation with Win-L + leaves the Win key(s) "down" from the hook's point of view - the + keyup event is never seen. Thus, this function must be called when + the system is locked. */ +static void +reset_w32_kbdhook_state (void) +{ + kbdhook.lwindown = 0; + kbdhook.rwindown = 0; + kbdhook.winsdown = 0; + kbdhook.send_win_up = 0; + kbdhook.suppress_lone = 0; + kbdhook.winseen = 0; +} + /* GetKeyState and MapVirtualKey on Windows 95 do not actually distinguish between left and right keys as advertised. We test for this support dynamically, and set a flag when the support is absent. If @@ -2255,6 +2621,10 @@ modifier_set (int vkey) else return (GetKeyState (vkey) & 0x1); } +#ifdef WINDOWSNT + if (w32_kbdhook_active && (vkey == VK_LWIN || vkey == VK_RWIN)) + return check_w32_winkey_state (vkey); +#endif if (!modifiers_recorded) return (GetKeyState (vkey) & 0x8000); @@ -2275,6 +2645,7 @@ modifier_set (int vkey) /* Convert between the modifier bits W32 uses and the modifier bits Emacs uses. */ +unsigned int w32_key_to_modifier (int); unsigned int w32_key_to_modifier (int key) @@ -2373,6 +2744,8 @@ w32_get_key_modifiers (unsigned int wparam, unsigned int lparam) return mods; } +unsigned int map_keypad_keys (unsigned int, unsigned int); + unsigned int map_keypad_keys (unsigned int virt_key, unsigned int extended) { @@ -2397,7 +2770,9 @@ map_keypad_keys (unsigned int virt_key, unsigned int extended) /* List of special key combinations which w32 would normally capture, but Emacs should grab instead. Not directly visible to lisp, to simplify synchronization. Each item is an integer encoding a virtual - key code and modifier combination to capture. */ + key code and modifier combination to capture. + Note: This code is not used if keyboard hooks are active + (Windows 2000 and later). */ static Lisp_Object w32_grabbed_keys; #define HOTKEY(vk, mods) make_number (((vk) & 255) | ((mods) << 8)) @@ -2743,6 +3118,8 @@ cancel_all_deferred_msgs (void) PostThreadMessage (dwWindowsThreadId, WM_NULL, 0, 0); } +DWORD WINAPI w32_msg_worker (void *); + DWORD WINAPI w32_msg_worker (void *arg) { @@ -2917,7 +3294,7 @@ get_wm_chars (HWND aWnd, int *buf, int buflen, int ignore_ctrl, int ctrl, /* Non-character payload in a WM_CHAR (Ctrl-something pressed, see above). Ignore, and report. */ if (ctrl_cnt) - *ctrl_cnt++; + (*ctrl_cnt)++; continue; } /* Traditionally, Emacs would ignore the character payload of VK_NUMPAD* @@ -2945,7 +3322,7 @@ get_wm_chars (HWND aWnd, int *buf, int buflen, int ignore_ctrl, int ctrl, #ifdef DBG_WM_CHARS # define FPRINTF_WM_CHARS(ARG) fprintf ARG #else -# define FPRINTF_WM_CHARS(ARG) 0 +# define FPRINTF_WM_CHARS(ARG) (void)0 #endif /* This is a heuristic only. This is supposed to track the state of the @@ -2961,7 +3338,7 @@ get_wm_chars (HWND aWnd, int *buf, int buflen, int ignore_ctrl, int ctrl, Be ready to treat the case when this delivers WM_(SYS)DEADCHAR. */ static int after_deadkey = -1; -int +static int deliver_wm_chars (int do_translate, HWND hwnd, UINT msg, UINT wParam, UINT lParam, int legacy_alt_meta) { @@ -3004,8 +3381,8 @@ deliver_wm_chars (int do_translate, HWND hwnd, UINT msg, UINT wParam, { W32Msg wmsg; DWORD console_modifiers = construct_console_modifiers (); - int *b = buf, strip_Alt = 1, strip_ExtraMods = 1, hairy = 0; - char *type_CtrlAlt = NULL; + int *b = buf, strip_ExtraMods = 1, hairy = 0; + const char *type_CtrlAlt = NULL; /* XXXX In fact, there may be another case when we need to do the same: What happens if the string defined in the LIGATURES has length @@ -3132,25 +3509,25 @@ deliver_wm_chars (int do_translate, HWND hwnd, UINT msg, UINT wParam, && console_modifiers & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) { type_CtrlAlt = "bB"; /* generic bindable Ctrl-Alt- modifiers */ - if (console_modifiers & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) + if ((console_modifiers & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) == (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) /* double-Ctrl: e.g. AltGr-rCtrl on some layouts (in this order!) */ type_CtrlAlt = "dD"; - else if (console_modifiers - & (LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED) + else if ((console_modifiers + & (LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED)) == (LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED)) type_CtrlAlt = "lL"; /* Ctrl-Alt- modifiers on the left */ else if (!NILP (Vw32_recognize_altgr) - && (console_modifiers - & (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)) + && ((console_modifiers + & (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED))) == (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)) type_CtrlAlt = "gG"; /* modifiers as in AltGr */ } else if (wmsg.dwModifiers & (alt_modifier | meta_modifier) - || (console_modifiers - & (LEFT_WIN_PRESSED | RIGHT_WIN_PRESSED - | APPS_PRESSED | SCROLLLOCK_ON))) + || ((console_modifiers + & (LEFT_WIN_PRESSED | RIGHT_WIN_PRESSED + | APPS_PRESSED | SCROLLLOCK_ON)))) { /* Pure Alt (or combination of Alt, Win, APPS, scrolllock. */ type_CtrlAlt = "aA"; @@ -3161,9 +3538,45 @@ deliver_wm_chars (int do_translate, HWND hwnd, UINT msg, UINT wParam, SHORT r = VkKeyScanW (*b), bitmap = 0x1FF; FPRINTF_WM_CHARS((stderr, "VkKeyScanW %#06x %#04x\n", (int)r, - wParam)); + wParam)); if ((r & 0xFF) == wParam) bitmap = r>>8; /* *b is reachable via simple interface */ + else + { + /* VkKeyScanW() (essentially) returns the FIRST key with + the specified character; so here the pressed key is the + SECONDARY key producing the character. + + Essentially, we have no information about the "role" of + modifiers on this key: which contribute into the + produced character (so "are consumed"), and which are + "extra" (must attache to bindable events). + + The default above would consume ALL modifiers, so the + character is reported "as is". However, on many layouts + the ordering of the keys (in the layout table) is not + thought out well, so the "secondary" keys are often those + which the users would prefer to use with Alt-CHAR. + (Moreover - with e.g. Czech-QWERTY - the ASCII + punctuation is accessible from two equally [nu]preferable + AltGr-keys.) + + SO: Heuristic: if the reported char is ASCII, AND Meta + modifier is a candidate, behave as if Meta is present + (fallback to the legacy branch; bug#23251). + + (This would break layouts + - delivering ASCII characters + - on SECONDARY keys + - with not Shift/AltGr-like modifier combinations. + All 3 conditions together must be pretty exotic + cases - and a workaround exists: use "primary" keys!) */ + if (*b < 0x80 + && (wmsg.dwModifiers + & (alt_modifier | meta_modifier + | super_modifier | hyper_modifier))) + return 0; + } if (*type_CtrlAlt == 'a') /* Simple Alt seen */ { if ((bitmap & ~1) == 0) /* 1: KBDSHIFT */ @@ -3447,7 +3860,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) switch (wParam) { case VK_LWIN: - if (NILP (Vw32_pass_lwindow_to_system)) + if (!w32_kbdhook_active && NILP (Vw32_pass_lwindow_to_system)) { /* Prevent system from acting on keyup (which opens the Start menu if no other key was pressed) by simulating a @@ -3466,7 +3879,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return 0; break; case VK_RWIN: - if (NILP (Vw32_pass_rwindow_to_system)) + if (!w32_kbdhook_active && NILP (Vw32_pass_rwindow_to_system)) { if (GetAsyncKeyState (wParam) & 1) { @@ -4323,10 +4736,12 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_SETFOCUS: dpyinfo->faked_key = 0; reset_modifiers (); - register_hot_keys (hwnd); + if (!w32_kbdhook_active) + register_hot_keys (hwnd); goto command; case WM_KILLFOCUS: - unregister_hot_keys (hwnd); + if (!w32_kbdhook_active) + unregister_hot_keys (hwnd); button_state = 0; ReleaseCapture (); /* Relinquish the system caret. */ @@ -4355,109 +4770,38 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) my_post_msg (&wmsg, hwnd, msg, wParam, lParam); goto dflt; +#ifdef WINDOWSNT + case WM_CREATE: + setup_w32_kbdhook (); + goto dflt; +#endif + case WM_DESTROY: +#ifdef WINDOWSNT + remove_w32_kbdhook (); +#endif CoUninitialize (); return 0; + case WM_WTSSESSION_CHANGE: + if (wParam == WTS_SESSION_LOCK) + reset_w32_kbdhook_state (); + goto dflt; + case WM_CLOSE: wmsg.dwModifiers = w32_get_modifiers (); my_post_msg (&wmsg, hwnd, msg, wParam, lParam); return 0; + case WM_ENDSESSION: + my_post_msg (&wmsg, hwnd, msg, wParam, lParam); + /* If we return, the process will be terminated immediately. */ + sleep (1000); + case WM_WINDOWPOSCHANGING: /* Don't restrict the sizing of any kind of frames. If the window manager doesn't, there's no reason to do it ourselves. */ -#if 0 - if (frame_resize_pixelwise || hwnd == tip_window) -#endif - return 0; - -#if 0 - /* Don't restrict the sizing of fullscreened frames, allowing them to be - flush with the sides of the screen. */ - f = x_window_to_frame (dpyinfo, hwnd); - if (f && FRAME_PREV_FSMODE (f) != FULLSCREEN_NONE) - return 0; - - { - WINDOWPLACEMENT wp; - LPWINDOWPOS lppos = (WINDOWPOS *) lParam; - - wp.length = sizeof (WINDOWPLACEMENT); - GetWindowPlacement (hwnd, &wp); - - if (wp.showCmd != SW_SHOWMAXIMIZED && wp.showCmd != SW_SHOWMINIMIZED - && (lppos->flags & SWP_NOSIZE) == 0) - { - RECT rect; - int wdiff; - int hdiff; - DWORD font_width; - DWORD line_height; - DWORD internal_border; - DWORD vscrollbar_extra; - DWORD hscrollbar_extra; - RECT wr; - - wp.length = sizeof (wp); - GetWindowRect (hwnd, &wr); - - enter_crit (); - - font_width = GetWindowLong (hwnd, WND_FONTWIDTH_INDEX); - line_height = GetWindowLong (hwnd, WND_LINEHEIGHT_INDEX); - internal_border = GetWindowLong (hwnd, WND_BORDER_INDEX); - vscrollbar_extra = GetWindowLong (hwnd, WND_VSCROLLBAR_INDEX); - hscrollbar_extra = GetWindowLong (hwnd, WND_HSCROLLBAR_INDEX); - - leave_crit (); - - memset (&rect, 0, sizeof (rect)); - AdjustWindowRect (&rect, GetWindowLong (hwnd, GWL_STYLE), - GetMenu (hwnd) != NULL); - - /* Force width and height of client area to be exact - multiples of the character cell dimensions. */ - wdiff = (lppos->cx - (rect.right - rect.left) - - 2 * internal_border - vscrollbar_extra) - % font_width; - hdiff = (lppos->cy - (rect.bottom - rect.top) - - 2 * internal_border - hscrollbar_extra) - % line_height; - - if (wdiff || hdiff) - { - /* For right/bottom sizing we can just fix the sizes. - However for top/left sizing we will need to fix the X - and Y positions as well. */ - - int cx_mintrack = GetSystemMetrics (SM_CXMINTRACK); - int cy_mintrack = GetSystemMetrics (SM_CYMINTRACK); - - lppos->cx = max (lppos->cx - wdiff, cx_mintrack); - lppos->cy = max (lppos->cy - hdiff, cy_mintrack); - - if (wp.showCmd != SW_SHOWMAXIMIZED - && (lppos->flags & SWP_NOMOVE) == 0) - { - if (lppos->x != wr.left || lppos->y != wr.top) - { - lppos->x += wdiff; - lppos->y += hdiff; - } - else - { - lppos->flags |= SWP_NOMOVE; - } - } - - return 0; - } - } - } - - goto dflt; -#endif + return 0; case WM_GETMINMAXINFO: /* Hack to allow resizing the Emacs frame above the screen size. @@ -4714,8 +5058,7 @@ my_create_tip_window (struct frame *f) rect.right = FRAME_PIXEL_WIDTH (f); rect.bottom = FRAME_PIXEL_HEIGHT (f); - AdjustWindowRect (&rect, f->output_data.w32->dwStyle, - FRAME_EXTERNAL_MENU_BAR (f)); + AdjustWindowRect (&rect, f->output_data.w32->dwStyle, false); tip_window = FRAME_W32_WINDOW (f) = CreateWindow (EMACS_CLASS, @@ -4907,12 +5250,6 @@ do_unwind_create_frame (Lisp_Object frame) unwind_create_frame (frame); } -static void -unwind_create_frame_1 (Lisp_Object val) -{ - inhibit_lisp_code = val; -} - static void x_default_font_parameter (struct frame *f, Lisp_Object parms) { @@ -4928,7 +5265,7 @@ x_default_font_parameter (struct frame *f, Lisp_Object parms) if (!STRINGP (font)) { int i; - static char *names[] + static const char *names[] = { "Courier New-10", "-*-Courier-normal-r-*-*-13-*-*-*-c-*-iso8859-1", "-*-Fixedsys-normal-r-*-*-12-*-*-*-c-*-iso8859-1", @@ -4948,7 +5285,8 @@ x_default_font_parameter (struct frame *f, Lisp_Object parms) { /* Remember the explicit font parameter, so we can re-apply it after we've applied the `default' face settings. */ - x_set_frame_parameters (f, Fcons (Fcons (Qfont_param, font_param), Qnil)); + x_set_frame_parameters (f, Fcons (Fcons (Qfont_parameter, font_param), + Qnil)); } x_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING); } @@ -4976,6 +5314,7 @@ This function is an internal primitive--use `make-frame' instead. */) struct w32_display_info *dpyinfo = NULL; Lisp_Object parent; struct kboard *kb; + int x_width = 0, x_height = 0; if (!FRAME_W32_P (SELECTED_FRAME ()) && !FRAME_INITIAL_P (SELECTED_FRAME ())) @@ -5198,7 +5537,7 @@ This function is an internal primitive--use `make-frame' instead. */) f->output_data.w32->current_cursor = f->output_data.w32->nontext_cursor; - window_prompting = x_figure_window_size (f, parameters, true); + window_prompting = x_figure_window_size (f, parameters, true, &x_width, &x_height); tem = x_get_arg (dpyinfo, parameters, Qunsplittable, 0, 0, RES_TYPE_BOOLEAN); f->no_split = minibuffer_only || EQ (tem, Qt); @@ -5232,8 +5571,10 @@ This function is an internal primitive--use `make-frame' instead. */) /* Allow x_set_window_size, now. */ f->can_x_set_window_size = true; - adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), 0, true, - Qx_create_frame_2); + if (x_width > 0) + SET_FRAME_WIDTH (f, x_width); + if (x_height > 0) + SET_FRAME_HEIGHT (f, x_height); /* Tell the server what size and position, etc, we want, and how badly we want them. This should be done after we have the menu @@ -5242,6 +5583,9 @@ This function is an internal primitive--use `make-frame' instead. */) x_wm_set_size_hint (f, window_prompting, false); unblock_input (); + adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), 0, true, + Qx_create_frame_2); + /* Process fullscreen parameter here in the hope that normalizing a fullheight/fullwidth frame will produce the size set by the last adjust_frame_size call. */ @@ -5264,8 +5608,10 @@ This function is an internal primitive--use `make-frame' instead. */) else if (! NILP (visibility)) x_make_frame_visible (f); else - /* Must have been Qnil. */ - ; + { + /* Must have been Qnil. */ + ; + } } /* Initialize `default-minibuffer-frame' in case this is the first @@ -5305,7 +5651,7 @@ x_get_focus_frame (struct frame *frame) DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0, doc: /* Internal function called by `color-defined-p', which see. -(Note that the Nextstep version of this function ignores FRAME.) */) +\(Note that the Nextstep version of this function ignores FRAME.) */) (Lisp_Object color, Lisp_Object frame) { XColor foo; @@ -5313,7 +5659,7 @@ DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0, CHECK_STRING (color); - if (w32_defined_color (f, SDATA (color), &foo, false)) + if (w32_defined_color (f, SSDATA (color), &foo, false)) return Qt; else return Qnil; @@ -5328,7 +5674,7 @@ DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0, CHECK_STRING (color); - if (w32_defined_color (f, SDATA (color), &foo, false)) + if (w32_defined_color (f, SSDATA (color), &foo, false)) return list3i ((GetRValue (foo.pixel) << 8) | GetRValue (foo.pixel), (GetGValue (foo.pixel) << 8) | GetGValue (foo.pixel), (GetBValue (foo.pixel) << 8) | GetBValue (foo.pixel)); @@ -5447,7 +5793,7 @@ If omitted or nil, that stands for the selected frame's display. */) DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0, doc: /* Return the "vendor ID" string of the GUI software on TERMINAL. -(Labeling every distributor as a "vendor" embodies the false assumption +\(Labeling every distributor as a "vendor" embodies the false assumption that operating systems cannot be developed and distributed noncommercially.) For GNU and Unix systems, this queries the X server software; for @@ -5808,11 +6154,13 @@ SOUND is nil to use the normal beep. */) return sound; } +#if 0 /* unused */ int x_screen_planes (register struct frame *f) { return FRAME_DISPLAY_INFO (f)->n_planes; } +#endif /* Return the display structure for the display named NAME. Open a new connection if necessary. */ @@ -5833,8 +6181,7 @@ x_display_info_for_name (Lisp_Object name) validate_x_resource_name (); - dpyinfo = w32_term_init (name, (unsigned char *)0, - SSDATA (Vx_resource_name)); + dpyinfo = w32_term_init (name, NULL, SSDATA (Vx_resource_name)); if (dpyinfo == 0) error ("Cannot connect to server %s", SDATA (name)); @@ -5850,10 +6197,10 @@ DISPLAY is the name of the display to connect to. Optional second arg XRM-STRING is a string of resources in xrdb format. If the optional third arg MUST-SUCCEED is non-nil, terminate Emacs if we can't open the connection. -(In the Nextstep version, the last two arguments are currently ignored.) */) +\(In the Nextstep version, the last two arguments are currently ignored.) */) (Lisp_Object display, Lisp_Object xrm_string, Lisp_Object must_succeed) { - unsigned char *xrm_option; + char *xrm_option; struct w32_display_info *dpyinfo; CHECK_STRING (display); @@ -5895,9 +6242,9 @@ terminate Emacs if we can't open the connection. add_system_logical_colors_to_map (&Vw32_color_map); if (! NILP (xrm_string)) - xrm_option = SDATA (xrm_string); + xrm_option = SSDATA (xrm_string); else - xrm_option = (unsigned char *) 0; + xrm_option = NULL; /* Use this general default value to start with. */ /* First remove .exe suffix from invocation-name - it looks ugly. */ @@ -5915,8 +6262,7 @@ terminate Emacs if we can't open the connection. /* This is what opens the connection and sets x_current_display. This also initializes many symbols, such as those used for input. */ - dpyinfo = w32_term_init (display, xrm_option, - SSDATA (Vx_resource_name)); + dpyinfo = w32_term_init (display, xrm_option, SSDATA (Vx_resource_name)); if (dpyinfo == 0) { @@ -6120,8 +6466,6 @@ no value of TYPE (always string in the MS Windows case). */) Tool tips ***********************************************************************/ -static Lisp_Object x_create_tip_frame (struct w32_display_info *, - Lisp_Object, Lisp_Object); static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object, Lisp_Object, int, int, int *, int *); @@ -6156,8 +6500,7 @@ unwind_create_tip_frame (Lisp_Object frame) /* Create a frame for a tooltip on the display described by DPYINFO. - PARMS is a list of frame parameters. TEXT is the string to - display in the tip frame. Value is the frame. + PARMS is a list of frame parameters. Value is the frame. Note that functions called here, esp. x_default_parameter can signal errors, for instance when a specified color name is @@ -6165,19 +6508,16 @@ unwind_create_tip_frame (Lisp_Object frame) when this happens. */ static Lisp_Object -x_create_tip_frame (struct w32_display_info *dpyinfo, - Lisp_Object parms, Lisp_Object text) +x_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms) { struct frame *f; Lisp_Object frame; Lisp_Object name; - long window_prompting = 0; int width, height; ptrdiff_t count = SPECPDL_INDEX (); struct kboard *kb; bool face_change_before = face_change; - Lisp_Object buffer; - struct buffer *old_buffer; + int x_width = 0, x_height = 0; /* Use this general default value to start with until we know if this frame has a specified name. */ @@ -6200,23 +6540,9 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, frame = Qnil; /* Make a frame without minibuffer nor mode-line. */ f = make_frame (false); - f->wants_modeline = 0; + f->wants_modeline = false; XSETFRAME (frame, f); - AUTO_STRING (tip, " *tip*"); - buffer = Fget_buffer_create (tip); - /* Use set_window_buffer instead of Fset_window_buffer (see - discussion of bug#11984, bug#12025, bug#12026). */ - set_window_buffer (FRAME_ROOT_WINDOW (f), buffer, false, false); - old_buffer = current_buffer; - set_buffer_internal_1 (XBUFFER (buffer)); - bset_truncate_lines (current_buffer, Qnil); - specbind (Qinhibit_read_only, Qt); - specbind (Qinhibit_modification_hooks, Qt); - Ferase_buffer (); - Finsert (1, &text); - set_buffer_internal_1 (old_buffer); - record_unwind_protect (unwind_create_tip_frame, frame); /* By setting the output method, we're essentially saying that @@ -6250,7 +6576,7 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, { fset_name (f, name); f->explicit_name = true; - /* use the frame's title when getting resources for this frame. */ + /* Use the frame's title when getting resources for this frame. */ specbind (Qx_resource_name, name); } @@ -6280,14 +6606,10 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, parms = Fcons (Fcons (Qinternal_border_width, value), parms); } + x_default_parameter (f, parms, Qinternal_border_width, make_number (1), "internalBorderWidth", "internalBorderWidth", RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qright_divider_width, make_number (0), - NULL, NULL, RES_TYPE_NUMBER); - x_default_parameter (f, parms, Qbottom_divider_width, make_number (0), - NULL, NULL, RES_TYPE_NUMBER); - /* Also do the stuff which must be set before the window exists. */ x_default_parameter (f, parms, Qforeground_color, build_string ("black"), "foreground", "Foreground", RES_TYPE_STRING); @@ -6308,12 +6630,15 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, f->output_data.w32->dwStyle = WS_BORDER | WS_POPUP | WS_DISABLED; f->output_data.w32->parent_desc = FRAME_DISPLAY_INFO (f)->root_window; - window_prompting = x_figure_window_size (f, parms, false); + x_figure_window_size (f, parms, true, &x_width, &x_height); /* No fringes on tip frame. */ f->fringe_cols = 0; f->left_fringe_width = 0; f->right_fringe_width = 0; + /* No dividers on tip frame. */ + f->right_divider_width = 0; + f->bottom_divider_width = 0; block_input (); my_create_tip_window (f); @@ -6340,7 +6665,6 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, SET_FRAME_LINES (f, 0); adjust_frame_size (f, width * FRAME_COLUMN_WIDTH (f), height * FRAME_LINE_HEIGHT (f), 0, true, Qtip_frame); - /* Add `tooltip' frame parameter's default value. */ if (NILP (Fframe_parameter (frame, Qtooltip))) Fmodify_frame_parameters (frame, Fcons (Fcons (Qtooltip, Qt), Qnil)); @@ -6358,8 +6682,6 @@ x_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object fg = Fframe_parameter (frame, Qforeground_color); Lisp_Object colors = Qnil; - /* Set tip_frame here, so that */ - tip_frame = frame; call2 (Qface_set_after_frame_default, frame, Qnil); if (!EQ (bg, Fframe_parameter (frame, Qbackground_color))) @@ -6477,7 +6799,7 @@ compute_tip_xy (struct frame *f, if (INTEGERP (left)) *root_x = XINT (left); else if (INTEGERP (right)) - *root_y = XINT (right) - width; + *root_x = XINT (right) - width; else if (*root_x + XINT (dx) <= min_x) *root_x = 0; /* Can happen for negative dx */ else if (*root_x + XINT (dx) + width <= max_x) @@ -6491,6 +6813,48 @@ compute_tip_xy (struct frame *f, *root_x = min_x; } +/* Hide tooltip. Delete its frame if DELETE is true. */ +static Lisp_Object +x_hide_tip (bool delete) +{ + if (!NILP (tip_timer)) + { + call1 (Qcancel_timer, tip_timer); + tip_timer = Qnil; + } + + if (NILP (tip_frame) + || (!delete && FRAMEP (tip_frame) + && !FRAME_VISIBLE_P (XFRAME (tip_frame)))) + return Qnil; + else + { + ptrdiff_t count; + Lisp_Object was_open = Qnil; + + count = SPECPDL_INDEX (); + specbind (Qinhibit_redisplay, Qt); + specbind (Qinhibit_quit, Qt); + + if (FRAMEP (tip_frame)) + { + if (delete) + { + delete_frame (tip_frame, Qnil); + tip_frame = Qnil; + } + else + x_make_frame_invisible (XFRAME (tip_frame)); + + was_open = Qt; + } + else + tip_frame = Qnil; + + return unbind_to (count, was_open); + } +} + DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, doc: /* Show STRING in a \"tooltip\" window on frame FRAME. @@ -6524,20 +6888,22 @@ A tooltip's maximum size is specified by `x-max-tooltip-size'. Text larger than the specified size is clipped. */) (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy) { - struct frame *f; + struct frame *tip_f; struct window *w; int root_x, root_y; struct buffer *old_buffer; struct text_pos pos; - int i, width, height; - bool seen_reversed_p; + int width, height; int old_windows_or_buffers_changed = windows_or_buffers_changed; ptrdiff_t count = SPECPDL_INDEX (); + ptrdiff_t count_1; + Lisp_Object window, size; + AUTO_STRING (tip, " *tip*"); specbind (Qinhibit_redisplay, Qt); CHECK_STRING (string); - f = decode_window_system_frame (frame); + decode_window_system_frame (frame); if (NILP (timeout)) timeout = make_number (5); else @@ -6556,91 +6922,155 @@ Text larger than the specified size is clipped. */) if (NILP (last_show_tip_args)) last_show_tip_args = Fmake_vector (make_number (3), Qnil); - if (!NILP (tip_frame)) + if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame))) { Lisp_Object last_string = AREF (last_show_tip_args, 0); Lisp_Object last_frame = AREF (last_show_tip_args, 1); Lisp_Object last_parms = AREF (last_show_tip_args, 2); - if (EQ (frame, last_frame) - && !NILP (Fequal (last_string, string)) + if (FRAME_VISIBLE_P (XFRAME (tip_frame)) + && EQ (frame, last_frame) + && !NILP (Fequal_including_properties (last_string, string)) && !NILP (Fequal (last_parms, parms))) { - struct frame *f = XFRAME (tip_frame); - /* Only DX and DY have changed. */ + tip_f = XFRAME (tip_frame); if (!NILP (tip_timer)) { Lisp_Object timer = tip_timer; + tip_timer = Qnil; call1 (Qcancel_timer, timer); } block_input (); - compute_tip_xy (f, parms, dx, dy, FRAME_PIXEL_WIDTH (f), - FRAME_PIXEL_HEIGHT (f), &root_x, &root_y); + compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f), + FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y); /* Put tooltip in topmost group and in position. */ - SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOPMOST, + SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOPMOST, root_x, root_y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); /* Ensure tooltip is on top of other topmost windows (eg menus). */ - SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOP, + SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); + /* Let redisplay know that we have made the frame visible already. */ + SET_FRAME_VISIBLE (tip_f, 1); + ShowWindow (FRAME_W32_WINDOW (tip_f), SW_SHOWNOACTIVATE); unblock_input (); + goto start_timer; } - } + else if (tooltip_reuse_hidden_frame && EQ (frame, last_frame)) + { + bool delete = false; + Lisp_Object tail, elt, parm, last; - /* Hide a previous tip, if any. */ - Fx_hide_tip (); + /* Check if every parameter in PARMS has the same value in + last_parms. This may destruct last_parms which, however, + will be recreated below. */ + for (tail = parms; CONSP (tail); tail = XCDR (tail)) + { + elt = XCAR (tail); + parm = Fcar (elt); + /* The left, top, right and bottom parameters are handled + by compute_tip_xy so they can be ignored here. */ + if (!EQ (parm, Qleft) && !EQ (parm, Qtop) + && !EQ (parm, Qright) && !EQ (parm, Qbottom)) + { + last = Fassq (parm, last_parms); + if (NILP (Fequal (Fcdr (elt), Fcdr (last)))) + { + /* We lost, delete the old tooltip. */ + delete = true; + break; + } + else + last_parms = call2 (Qassq_delete_all, parm, last_parms); + } + else + last_parms = call2 (Qassq_delete_all, parm, last_parms); + } + + /* Now check if there's a parameter left in last_parms with a + non-nil value. */ + for (tail = last_parms; CONSP (tail); tail = XCDR (tail)) + { + elt = XCAR (tail); + parm = Fcar (elt); + if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright) + && !EQ (parm, Qbottom) && !NILP (Fcdr (elt))) + { + /* We lost, delete the old tooltip. */ + delete = true; + break; + } + } + + x_hide_tip (delete); + } + else + x_hide_tip (true); + } + else + x_hide_tip (true); ASET (last_show_tip_args, 0, string); ASET (last_show_tip_args, 1, frame); ASET (last_show_tip_args, 2, parms); - /* Add default values to frame parameters. */ - if (NILP (Fassq (Qname, parms))) - parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms); - if (NILP (Fassq (Qinternal_border_width, parms))) - parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms); - if (NILP (Fassq (Qright_divider_width, parms))) - parms = Fcons (Fcons (Qright_divider_width, make_number (0)), parms); - if (NILP (Fassq (Qbottom_divider_width, parms))) - parms = Fcons (Fcons (Qbottom_divider_width, make_number (0)), parms); - if (NILP (Fassq (Qborder_width, parms))) - parms = Fcons (Fcons (Qborder_width, make_number (1)), parms); - if (NILP (Fassq (Qborder_color, parms))) - parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms); - if (NILP (Fassq (Qbackground_color, parms))) - parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")), - parms); - /* Block input until the tip has been fully drawn, to avoid crashes when drawing tips in menus. */ block_input (); - /* Create a frame for the tooltip, and record it in the global - variable tip_frame. */ - frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms, string); - f = XFRAME (frame); + if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame))) + { + /* Add default values to frame parameters. */ + if (NILP (Fassq (Qname, parms))) + parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms); + if (NILP (Fassq (Qinternal_border_width, parms))) + parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms); + if (NILP (Fassq (Qborder_width, parms))) + parms = Fcons (Fcons (Qborder_width, make_number (1)), parms); + if (NILP (Fassq (Qborder_color, parms))) + parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms); + if (NILP (Fassq (Qbackground_color, parms))) + parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")), + parms); - /* Set up the frame's root window. */ - w = XWINDOW (FRAME_ROOT_WINDOW (f)); + /* Create a frame for the tooltip, and record it in the global + variable tip_frame. */ + struct frame *f; /* The value is unused. */ + if (NILP (tip_frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms))) + { + /* Creating the tip frame failed. */ + unblock_input (); + return unbind_to (count, Qnil); + } + } + + tip_f = XFRAME (tip_frame); + window = FRAME_ROOT_WINDOW (tip_f); + set_window_buffer (window, Fget_buffer_create (tip), false, false); + w = XWINDOW (window); + w->pseudo_window_p = true; + + /* Set up the frame's root window. Note: The following code does not + try to size the window or its frame correctly. Its only purpose is + to make the subsequent text size calculations work. The right + sizes should get installed when the toolkit gets back to us. */ w->left_col = 0; w->top_line = 0; w->pixel_left = 0; w->pixel_top = 0; if (CONSP (Vx_max_tooltip_size) - && INTEGERP (XCAR (Vx_max_tooltip_size)) - && XINT (XCAR (Vx_max_tooltip_size)) > 0 - && INTEGERP (XCDR (Vx_max_tooltip_size)) - && XINT (XCDR (Vx_max_tooltip_size)) > 0) + && RANGED_INTEGERP (1, XCAR (Vx_max_tooltip_size), INT_MAX) + && RANGED_INTEGERP (1, XCDR (Vx_max_tooltip_size), INT_MAX)) { w->total_cols = XFASTINT (XCAR (Vx_max_tooltip_size)); w->total_lines = XFASTINT (XCDR (Vx_max_tooltip_size)); @@ -6651,165 +7081,71 @@ Text larger than the specified size is clipped. */) w->total_lines = 40; } - w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (f); - w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (f); - - FRAME_TOTAL_COLS (f) = WINDOW_TOTAL_COLS (w); - adjust_frame_glyphs (f); - w->pseudo_window_p = true; + w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f); + w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f); + FRAME_TOTAL_COLS (tip_f) = WINDOW_TOTAL_COLS (w); + adjust_frame_glyphs (tip_f); - /* Display the tooltip text in a temporary buffer. */ + /* Insert STRING into the root window's buffer and fit the frame to + the buffer. */ + count_1 = SPECPDL_INDEX (); old_buffer = current_buffer; - set_buffer_internal_1 (XBUFFER (XWINDOW (FRAME_ROOT_WINDOW (f))->contents)); + set_buffer_internal_1 (XBUFFER (w->contents)); bset_truncate_lines (current_buffer, Qnil); + specbind (Qinhibit_read_only, Qt); + specbind (Qinhibit_modification_hooks, Qt); + specbind (Qinhibit_point_motion_hooks, Qt); + Ferase_buffer (); + Finsert (1, &string); clear_glyph_matrix (w->desired_matrix); clear_glyph_matrix (w->current_matrix); SET_TEXT_POS (pos, BEGV, BEGV_BYTE); - try_window (FRAME_ROOT_WINDOW (f), pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); - - /* Compute width and height of the tooltip. */ - width = height = 0; - seen_reversed_p = false; - for (i = 0; i < w->desired_matrix->nrows; ++i) - { - struct glyph_row *row = &w->desired_matrix->rows[i]; - struct glyph *last; - int row_width; - - /* Stop at the first empty row at the end. */ - if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row)) - break; - - /* Let the row go over the full width of the frame. */ - row->full_width_p = true; - - row_width = row->pixel_width; - if (row->used[TEXT_AREA]) - { - if (!row->reversed_p) - { - /* There's a glyph at the end of rows that is used to - place the cursor there. Don't include the width of - this glyph. */ - last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1]; - if (NILP (last->object)) - row_width -= last->pixel_width; - } - else - { - /* There could be a stretch glyph at the beginning of R2L - rows that is produced by extend_face_to_end_of_line. - Don't count that glyph. */ - struct glyph *g = row->glyphs[TEXT_AREA]; - - if (g->type == STRETCH_GLYPH && NILP (g->object)) - { - row_width -= g->pixel_width; - seen_reversed_p = true; - } - } - } - - height += row->height; - width = max (width, row_width); - } - - /* If we've seen partial-length R2L rows, we need to re-adjust the - tool-tip frame width and redisplay it again, to avoid over-wide - tips due to the stretch glyph that extends R2L lines to full - width of the frame. */ - if (seen_reversed_p) - { - /* PXW: Why do we do the pixel-to-cols conversion only if - seen_reversed_p holds? Don't we have to set other fields of - the window/frame structure? - - w->total_cols and FRAME_TOTAL_COLS want the width in columns, - not in pixels. */ - w->pixel_width = width; - width /= WINDOW_FRAME_COLUMN_WIDTH (w); - w->total_cols = width; - FRAME_TOTAL_COLS (f) = width; - SET_FRAME_WIDTH (f, width); - adjust_frame_glyphs (f); - w->pseudo_window_p = 1; - clear_glyph_matrix (w->desired_matrix); - clear_glyph_matrix (w->current_matrix); - try_window (FRAME_ROOT_WINDOW (f), pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); - width = height = 0; - /* Recompute width and height of the tooltip. */ - for (i = 0; i < w->desired_matrix->nrows; ++i) - { - struct glyph_row *row = &w->desired_matrix->rows[i]; - struct glyph *last; - int row_width; - - if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row)) - break; - row->full_width_p = true; - row_width = row->pixel_width; - if (row->used[TEXT_AREA] && !row->reversed_p) - { - last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1]; - if (NILP (last->object)) - row_width -= last->pixel_width; - } - - height += row->height; - width = max (width, row_width); - } - } - - /* Add the frame's internal border to the width and height the w32 - window should have. */ - height += 2 * FRAME_INTERNAL_BORDER_WIDTH (f); - width += 2 * FRAME_INTERNAL_BORDER_WIDTH (f); - - /* Move the tooltip window where the mouse pointer is. Resize and - show it. - - PXW: This should use the frame's pixel coordinates. */ - compute_tip_xy (f, parms, dx, dy, width, height, &root_x, &root_y); - + try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE); + /* Calculate size of tooltip window. */ + size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil, + make_number (w->pixel_height), Qnil); + /* Add the frame's internal border to calculated size. */ + width = XINT (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + height = XINT (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f); + /* Calculate position of tooltip frame. */ + compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y); + + /* Show tooltip frame. */ { - /* Adjust Window size to take border into account. */ RECT rect; + int pad = (NUMBERP (Vw32_tooltip_extra_pixels) + ? max (0, XINT (Vw32_tooltip_extra_pixels)) + : FRAME_COLUMN_WIDTH (tip_f)); + rect.left = rect.top = 0; rect.right = width; rect.bottom = height; - AdjustWindowRect (&rect, f->output_data.w32->dwStyle, - FRAME_EXTERNAL_MENU_BAR (f)); - - /* Position and size tooltip, and put it in the topmost group. - The add-on of FRAME_COLUMN_WIDTH to the 5th argument is a - peculiarity of w32 display: without it, some fonts cause the - last character of the tip to be truncated or wrapped around to - the next line. */ - SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOPMOST, + AdjustWindowRect (&rect, tip_f->output_data.w32->dwStyle, + FRAME_EXTERNAL_MENU_BAR (tip_f)); + + /* Position and size tooltip and put it in the topmost group. */ + SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOPMOST, root_x, root_y, - rect.right - rect.left + FRAME_COLUMN_WIDTH (f), + rect.right - rect.left + pad, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER); /* Ensure tooltip is on top of other topmost windows (eg menus). */ - SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOP, + SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); /* Let redisplay know that we have made the frame visible already. */ - SET_FRAME_VISIBLE (f, 1); + SET_FRAME_VISIBLE (tip_f, 1); - ShowWindow (FRAME_W32_WINDOW (f), SW_SHOWNOACTIVATE); + ShowWindow (FRAME_W32_WINDOW (tip_f), SW_SHOWNOACTIVATE); } - /* Draw into the window. */ w->must_be_updated_p = true; update_single_window (w); - - unblock_input (); - - /* Restore original current buffer. */ set_buffer_internal_1 (old_buffer); + unbind_to (count_1, Qnil); + unblock_input (); windows_or_buffers_changed = old_windows_or_buffers_changed; start_timer: @@ -6826,31 +7162,7 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0, Value is t if tooltip was open, nil otherwise. */) (void) { - ptrdiff_t count; - Lisp_Object deleted, frame, timer; - - /* Return quickly if nothing to do. */ - if (NILP (tip_timer) && NILP (tip_frame)) - return Qnil; - - frame = tip_frame; - timer = tip_timer; - tip_frame = tip_timer = deleted = Qnil; - - count = SPECPDL_INDEX (); - specbind (Qinhibit_redisplay, Qt); - specbind (Qinhibit_quit, Qt); - - if (!NILP (timer)) - call1 (Qcancel_timer, timer); - - if (FRAMEP (frame)) - { - delete_frame (frame, Qnil); - deleted = Qt; - } - - return unbind_to (count, deleted); + return x_hide_tip (!tooltip_reuse_hidden_frame); } /*********************************************************************** @@ -6977,7 +7289,9 @@ value of DIR as in previous invocations; this is standard Windows behavior. */) { /* Filter index: 1: All Files, 2: Directories only */ static const wchar_t filter_w[] = L"All Files (*.*)\0*.*\0Directories\0*|*\0"; +#ifndef NTGUI_UNICODE static const char filter_a[] = "All Files (*.*)\0*.*\0Directories\0*|*\0"; +#endif Lisp_Object filename = default_filename; struct frame *f = SELECTED_FRAME (); @@ -7070,9 +7384,9 @@ value of DIR as in previous invocations; this is standard Windows behavior. */) /* We modify these in-place, so make copies for safety. */ dir = Fcopy_sequence (dir); - unixtodos_filename (SDATA (dir)); + unixtodos_filename (SSDATA (dir)); filename = Fcopy_sequence (filename); - unixtodos_filename (SDATA (filename)); + unixtodos_filename (SSDATA (filename)); if (SBYTES (filename) >= MAX_UTF8_PATH) report_file_error ("filename too long", default_filename); if (w32_unicode_filenames) @@ -7085,12 +7399,12 @@ value of DIR as in previous invocations; this is standard Windows behavior. */) if (errno == ENOENT && filename_buf_w[MAX_PATH - 1] != 0) report_file_error ("filename too long", default_filename); } - len = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + len = pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, SSDATA (prompt), -1, NULL, 0); if (len > 32768) len = 32768; prompt_w = alloca (len * sizeof (wchar_t)); - pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, SSDATA (prompt), -1, prompt_w, len); } else @@ -7103,12 +7417,12 @@ value of DIR as in previous invocations; this is standard Windows behavior. */) if (errno == ENOENT && filename_buf_a[MAX_PATH - 1] != 0) report_file_error ("filename too long", default_filename); } - len = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + len = pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, SSDATA (prompt), -1, NULL, 0); if (len > 32768) len = 32768; prompt_w = alloca (len * sizeof (wchar_t)); - pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, SSDATA (prompt), -1, prompt_w, len); len = pWideCharToMultiByte (CP_ACP, 0, prompt_w, -1, NULL, 0, NULL, NULL); if (len > 32768) @@ -7295,7 +7609,7 @@ DEFUN ("system-move-file-to-trash", Fsystem_move_file_to_trash, encoded_file = ENCODE_FILE (filename); - path = map_w32_filename (SDATA (encoded_file), NULL); + path = map_w32_filename (SSDATA (encoded_file), NULL); /* The Unicode version of SHFileOperation is not supported on Windows 9X. */ @@ -7334,7 +7648,8 @@ DEFUN ("system-move-file-to-trash", Fsystem_move_file_to_trash, /* If a file cannot be represented in ANSI codepage, don't let them inadvertently delete other files because some characters are interpreted as a wildcards. */ - if (_mbspbrk (tmp_path_a, "?*")) + if (_mbspbrk ((unsigned char *)tmp_path_a, + (const unsigned char *)"?*")) result = ERROR_FILE_NOT_FOUND; else { @@ -7589,10 +7904,10 @@ a ShowWindow flag: current_dir = ENCODE_FILE (current_dir); /* Cannot use filename_to_utf16/ansi with DOCUMENT, since it could be a URL that is not limited to MAX_PATH chararcters. */ - doclen = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + doclen = pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, SSDATA (document), -1, NULL, 0); doc_w = xmalloc (doclen * sizeof (wchar_t)); - pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, SSDATA (document), -1, doc_w, doclen); if (use_unicode) { @@ -7607,12 +7922,12 @@ a ShowWindow flag: int len; parameters = ENCODE_SYSTEM (parameters); - len = pMultiByteToWideChar (CP_ACP, MB_ERR_INVALID_CHARS, + len = pMultiByteToWideChar (CP_ACP, multiByteToWideCharFlags, SSDATA (parameters), -1, NULL, 0); if (len > 32768) len = 32768; params_w = alloca (len * sizeof (wchar_t)); - pMultiByteToWideChar (CP_ACP, MB_ERR_INVALID_CHARS, + pMultiByteToWideChar (CP_ACP, multiByteToWideCharFlags, SSDATA (parameters), -1, params_w, len); params_w[len - 1] = 0; } @@ -7651,7 +7966,7 @@ a ShowWindow flag: } else { - char document_a[MAX_PATH], current_dir_a[MAX_PATH]; + char current_dir_a[MAX_PATH]; SHELLEXECUTEINFOA shexinfo_a; int codepage = codepage_for_filenames (NULL); int ldoc_a = pWideCharToMultiByte (codepage, 0, doc_w, -1, NULL, 0, @@ -7717,19 +8032,34 @@ lookup_vk_code (char *key) && strcmp (lispy_function_keys[i], key) == 0) return i; + if (w32_kbdhook_active) + { + /* Alphanumerics map to themselves. */ + if (key[1] == 0) + { + if ((key[0] >= 'A' && key[0] <= 'Z') + || (key[0] >= '0' && key[0] <= '9')) + return key[0]; + if (key[0] >= 'a' && key[0] <= 'z') + return toupper(key[0]); + } + } + return -1; } /* Convert a one-element vector style key sequence to a hot key definition. */ static Lisp_Object -w32_parse_hot_key (Lisp_Object key) +w32_parse_and_hook_hot_key (Lisp_Object key, int hook) { /* Copied from Fdefine_key and store_in_keymap. */ register Lisp_Object c; int vk_code; int lisp_modifiers; int w32_modifiers; + Lisp_Object res = Qnil; + char* vkname; CHECK_VECTOR (key); @@ -7752,7 +8082,12 @@ w32_parse_hot_key (Lisp_Object key) c = Fcar (c); if (!SYMBOLP (c)) emacs_abort (); - vk_code = lookup_vk_code (SDATA (SYMBOL_NAME (c))); + vkname = SSDATA (SYMBOL_NAME (c)); + /* [s-], [M-], [h-]: Register all keys for this modifier */ + if (w32_kbdhook_active && vkname[0] == 0) + vk_code = VK_ANY; + else + vk_code = lookup_vk_code (vkname); } else if (INTEGERP (c)) { @@ -7776,34 +8111,75 @@ w32_parse_hot_key (Lisp_Object key) #define MOD_WIN 0x0008 #endif - /* Convert lisp modifiers to Windows hot-key form. */ - w32_modifiers = (lisp_modifiers & hyper_modifier) ? MOD_WIN : 0; - w32_modifiers |= (lisp_modifiers & alt_modifier) ? MOD_ALT : 0; - w32_modifiers |= (lisp_modifiers & ctrl_modifier) ? MOD_CONTROL : 0; - w32_modifiers |= (lisp_modifiers & shift_modifier) ? MOD_SHIFT : 0; + if (w32_kbdhook_active) + { + /* Register Alt-x combinations. */ + if (lisp_modifiers & alt_modifier) + { + hook_w32_key (hook, VK_MENU, vk_code); + res = Qt; + } + /* Register Win-x combinations based on modifier mappings. */ + if (((lisp_modifiers & hyper_modifier) + && EQ (Vw32_lwindow_modifier, Qhyper)) + || ((lisp_modifiers & super_modifier) + && EQ (Vw32_lwindow_modifier, Qsuper))) + { + hook_w32_key (hook, VK_LWIN, vk_code); + res = Qt; + } + if (((lisp_modifiers & hyper_modifier) + && EQ (Vw32_rwindow_modifier, Qhyper)) + || ((lisp_modifiers & super_modifier) + && EQ (Vw32_rwindow_modifier, Qsuper))) + { + hook_w32_key (hook, VK_RWIN, vk_code); + res = Qt; + } + return res; + } + else + { + /* Convert lisp modifiers to Windows hot-key form. */ + w32_modifiers = (lisp_modifiers & hyper_modifier) ? MOD_WIN : 0; + w32_modifiers |= (lisp_modifiers & alt_modifier) ? MOD_ALT : 0; + w32_modifiers |= (lisp_modifiers & ctrl_modifier) ? MOD_CONTROL : 0; + w32_modifiers |= (lisp_modifiers & shift_modifier) ? MOD_SHIFT : 0; - return HOTKEY (vk_code, w32_modifiers); + return HOTKEY (vk_code, w32_modifiers); + } } DEFUN ("w32-register-hot-key", Fw32_register_hot_key, Sw32_register_hot_key, 1, 1, 0, doc: /* Register KEY as a hot-key combination. -Certain key combinations like Alt-Tab are reserved for system use on -Windows, and therefore are normally intercepted by the system. However, -most of these key combinations can be received by registering them as -hot-keys, overriding their special meaning. - -KEY must be a one element key definition in vector form that would be -acceptable to `define-key' (e.g. [A-tab] for Alt-Tab). The meta -modifier is interpreted as Alt if `w32-alt-is-meta' is t, and hyper -is always interpreted as the Windows modifier keys. - -The return value is the hotkey-id if registered, otherwise nil. */) +Certain key combinations like Alt-Tab and Win-R are reserved for +system use on Windows, and therefore are normally intercepted by the +system. These key combinations can be received by registering them +as hot-keys, except for Win-L which always locks the computer. + +On Windows 98 and ME, KEY must be a one element key definition in +vector form that would be acceptable to `define-key' (e.g. [A-tab] for +Alt-Tab). The meta modifier is interpreted as Alt if +`w32-alt-is-meta' is t, and hyper is always interpreted as the Windows +modifier keys. The return value is the hotkey-id if registered, otherwise nil. + +On Windows versions since NT, KEY can also be specified as [M-], [s-] or +[h-] to indicate that all combinations of that key should be processed +by Emacs instead of the operating system. The super and hyper +modifiers are interpreted according to the current values of +`w32-lwindow-modifier' and `w32-rwindow-modifier'. For instance, +setting `w32-lwindow-modifier' to `super' and then calling +`(register-hot-key [s-])' grabs all combinations of the left Windows +key to Emacs, but leaves the right Windows key free for the operating +system keyboard shortcuts. The return value is t if the call affected +any key combinations, otherwise nil. */) (Lisp_Object key) { - key = w32_parse_hot_key (key); + key = w32_parse_and_hook_hot_key (key, 1); - if (!NILP (key) && NILP (Fmemq (key, w32_grabbed_keys))) + if (!w32_kbdhook_active + && !NILP (key) && NILP (Fmemq (key, w32_grabbed_keys))) { /* Reuse an empty slot if possible. */ Lisp_Object item = Fmemq (Qnil, w32_grabbed_keys); @@ -7831,7 +8207,10 @@ DEFUN ("w32-unregister-hot-key", Fw32_unregister_hot_key, Lisp_Object item; if (!INTEGERP (key)) - key = w32_parse_hot_key (key); + key = w32_parse_and_hook_hot_key (key, 0); + + if (w32_kbdhook_active) + return key; item = Fmemq (key, w32_grabbed_keys); @@ -8010,7 +8389,6 @@ and width values are in pixels. int single_menu_bar_height, wrapped_menu_bar_height, menu_bar_height; int tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f); int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); - bool fullboth = EQ (get_frame_param (f, Qfullscreen), Qfullboth); if (FRAME_INITIAL_P (f) || !FRAME_W32_P (f)) return Qnil; @@ -8191,14 +8569,25 @@ DEFUN ("w32-set-mouse-absolute-pixel-position", Fw32_set_mouse_absolute_pixel_po Sw32_set_mouse_absolute_pixel_position, 2, 2, 0, doc: /* Move mouse pointer to absolute pixel position (X, Y). The coordinates X and Y are interpreted in pixels relative to a position -(0, 0) of the selected frame's display. */) +\(0, 0) of the selected frame's display. */) (Lisp_Object x, Lisp_Object y) { + UINT trail_num = 0; + BOOL ret = false; + CHECK_TYPE_RANGED_INTEGER (int, x); CHECK_TYPE_RANGED_INTEGER (int, y); block_input (); + /* When "mouse trails" are in effect, moving the mouse cursor + sometimes leaves behind an annoying "ghost" of the pointer. + Avoid that by momentarily switching off mouse trails. */ + if (os_subtype == OS_NT + && w32_major_version + w32_minor_version >= 6) + ret = SystemParametersInfo (SPI_GETMOUSETRAILS, 0, &trail_num, 0); SetCursorPos (XINT (x), XINT (y)); + if (ret) + SystemParametersInfo (SPI_SETMOUSETRAILS, trail_num, NULL, 0); unblock_input (); return Qnil; @@ -8283,7 +8672,7 @@ The following %-sequences are provided: else { long m; - float h; + double h; char buffer[16]; snprintf (buffer, 16, "%ld", seconds_left); seconds = build_string (buffer); @@ -8356,7 +8745,7 @@ If the underlying system call fails, value is nil. */) char rootname[MAX_UTF8_PATH]; wchar_t rootname_w[MAX_PATH]; char rootname_a[MAX_PATH]; - char *name = SDATA (encoded); + char *name = SSDATA (encoded); BOOL result; /* find the root name of the volume if given */ @@ -8576,7 +8965,7 @@ w32_strerror (int error_no) --ret; buf[ret] = '\0'; if (!ret) - sprintf (buf, "w32 error %u", error_no); + sprintf (buf, "w32 error %d", error_no); return buf; } @@ -8584,6 +8973,8 @@ w32_strerror (int error_no) /* For convenience when debugging. (You cannot call GetLastError directly from GDB: it will crash, because it uses the __stdcall calling convention, not the _cdecl convention assumed by GDB.) */ +DWORD w32_last_error (void); + DWORD w32_last_error (void) { @@ -8810,7 +9201,7 @@ w32_kbd_patch_key (KEY_EVENT_RECORD *event, int cpId) event->uChar.UnicodeChar = buf[isdead - 1]; isdead = WideCharToMultiByte (cpId, 0, buf, isdead, - ansi_code, 4, NULL, NULL); + (LPSTR)ansi_code, 4, NULL, NULL); } else isdead = 0; @@ -8852,6 +9243,457 @@ Internal use only. */) return menubar_in_use ? Qt : Qnil; } +#if defined WINDOWSNT && !defined HAVE_DBUS + +/*********************************************************************** + Tray notifications + ***********************************************************************/ +/* A private struct declaration to avoid compile-time limits. */ +typedef struct MY_NOTIFYICONDATAW { + DWORD cbSize; + HWND hWnd; + UINT uID; + UINT uFlags; + UINT uCallbackMessage; + HICON hIcon; + WCHAR szTip[128]; + DWORD dwState; + DWORD dwStateMask; + WCHAR szInfo[256]; + _ANONYMOUS_UNION union { + UINT uTimeout; + UINT uVersion; + } DUMMYUNIONNAME; + WCHAR szInfoTitle[64]; + DWORD dwInfoFlags; + GUID guidItem; + HICON hBalloonIcon; +} MY_NOTIFYICONDATAW; + +#define MYNOTIFYICONDATAW_V1_SIZE offsetof (MY_NOTIFYICONDATAW, szTip[64]) +#define MYNOTIFYICONDATAW_V2_SIZE offsetof (MY_NOTIFYICONDATAW, guidItem) +#define MYNOTIFYICONDATAW_V3_SIZE offsetof (MY_NOTIFYICONDATAW, hBalloonIcon) +#ifndef NIF_INFO +# define NIF_INFO 0x00000010 +#endif +#ifndef NIIF_NONE +# define NIIF_NONE 0x00000000 +#endif +#ifndef NIIF_INFO +# define NIIF_INFO 0x00000001 +#endif +#ifndef NIIF_WARNING +# define NIIF_WARNING 0x00000002 +#endif +#ifndef NIIF_ERROR +# define NIIF_ERROR 0x00000003 +#endif + + +#define EMACS_TRAY_NOTIFICATION_ID 42 /* arbitrary */ +#define EMACS_NOTIFICATION_MSG (WM_APP + 1) + +enum NI_Severity { + Ni_None, + Ni_Info, + Ni_Warn, + Ni_Err +}; + +/* Report the version of a DLL given by its name. The return value is + constructed using MAKEDLLVERULL. */ +static ULONGLONG +get_dll_version (const char *dll_name) +{ + ULONGLONG version = 0; + HINSTANCE hdll = LoadLibrary (dll_name); + + if (hdll) + { + DLLGETVERSIONPROC pDllGetVersion + = (DLLGETVERSIONPROC) GetProcAddress (hdll, "DllGetVersion"); + + if (pDllGetVersion) + { + DLLVERSIONINFO dvi; + HRESULT result; + + memset (&dvi, 0, sizeof(dvi)); + dvi.cbSize = sizeof(dvi); + result = pDllGetVersion (&dvi); + if (SUCCEEDED (result)) + version = MAKEDLLVERULL (dvi.dwMajorVersion, dvi.dwMinorVersion, + 0, 0); + } + FreeLibrary (hdll); + } + + return version; +} + +/* Return the number of bytes in UTF-8 encoded string STR that + corresponds to at most LIM characters. If STR ends before LIM + characters, return the number of bytes in STR including the + terminating null byte. */ +static int +utf8_mbslen_lim (const char *str, int lim) +{ + const char *p = str; + int mblen = 0, nchars = 0; + + while (*p && nchars < lim) + { + int nbytes = CHAR_BYTES (*p); + + mblen += nbytes; + nchars++; + p += nbytes; + } + + if (!*p && nchars < lim) + mblen++; + + return mblen; +} + +/* Low-level subroutine to show tray notifications. All strings are + supposed to be unibyte UTF-8 encoded by the caller. */ +static EMACS_INT +add_tray_notification (struct frame *f, const char *icon, const char *tip, + enum NI_Severity severity, unsigned timeout, + const char *title, const char *msg) +{ + EMACS_INT retval = EMACS_TRAY_NOTIFICATION_ID; + + if (FRAME_W32_P (f)) + { + MY_NOTIFYICONDATAW nidw; + ULONGLONG shell_dll_version = get_dll_version ("Shell32.dll"); + wchar_t tipw[128], msgw[256], titlew[64]; + int tiplen; + + memset (&nidw, 0, sizeof(nidw)); + + /* MSDN says the full struct is supported since Vista, whose + Shell32.dll version is said to be 6.0.6. But DllGetVersion + cannot report the 3rd field value, it reports "build number" + instead, which is something else. So we use the Windows 7's + version 6.1 as cutoff, and Vista loses. (Actually, the loss + is not a real one, since we don't expose the hBalloonIcon + member of the struct to Lisp.) */ + if (shell_dll_version >= MAKEDLLVERULL (6, 1, 0, 0)) /* >= Windows 7 */ + nidw.cbSize = sizeof (nidw); + else if (shell_dll_version >= MAKEDLLVERULL (6, 0, 0, 0)) /* XP */ + nidw.cbSize = MYNOTIFYICONDATAW_V3_SIZE; + else if (shell_dll_version >= MAKEDLLVERULL (5, 0, 0, 0)) /* W2K */ + nidw.cbSize = MYNOTIFYICONDATAW_V2_SIZE; + else + nidw.cbSize = MYNOTIFYICONDATAW_V1_SIZE; /* < W2K */ + nidw.hWnd = FRAME_W32_WINDOW (f); + nidw.uID = EMACS_TRAY_NOTIFICATION_ID; + nidw.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_INFO; + nidw.uCallbackMessage = EMACS_NOTIFICATION_MSG; + if (!*icon) + nidw.hIcon = LoadIcon (hinst, EMACS_CLASS); + else + { + if (w32_unicode_filenames) + { + wchar_t icon_w[MAX_PATH]; + + if (filename_to_utf16 (icon, icon_w) != 0) + { + errno = ENOENT; + return -1; + } + nidw.hIcon = LoadImageW (NULL, icon_w, IMAGE_ICON, 0, 0, + LR_DEFAULTSIZE | LR_LOADFROMFILE); + } + else + { + char icon_a[MAX_PATH]; + + if (filename_to_ansi (icon, icon_a) != 0) + { + errno = ENOENT; + return -1; + } + nidw.hIcon = LoadImageA (NULL, icon_a, IMAGE_ICON, 0, 0, + LR_DEFAULTSIZE | LR_LOADFROMFILE); + } + } + if (!nidw.hIcon) + { + switch (GetLastError ()) + { + case ERROR_FILE_NOT_FOUND: + errno = ENOENT; + break; + default: + errno = ENOMEM; + break; + } + return -1; + } + + /* Windows 9X and NT4 support only 64 characters in the Tip, + later versions support up to 128. */ + if (nidw.cbSize == MYNOTIFYICONDATAW_V1_SIZE) + { + tiplen = pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, + tip, utf8_mbslen_lim (tip, 63), + tipw, 64); + if (tiplen >= 63) + tipw[63] = 0; + } + else + { + tiplen = pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, + tip, utf8_mbslen_lim (tip, 127), + tipw, 128); + if (tiplen >= 127) + tipw[127] = 0; + } + if (tiplen == 0) + { + errno = EINVAL; + retval = -1; + goto done; + } + wcscpy (nidw.szTip, tipw); + + /* The rest of the structure is only supported since Windows 2000. */ + if (nidw.cbSize > MYNOTIFYICONDATAW_V1_SIZE) + { + int slen; + + slen = pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, + msg, utf8_mbslen_lim (msg, 255), + msgw, 256); + if (slen >= 255) + msgw[255] = 0; + else if (slen == 0) + { + errno = EINVAL; + retval = -1; + goto done; + } + wcscpy (nidw.szInfo, msgw); + nidw.uTimeout = timeout; + slen = pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, + title, utf8_mbslen_lim (title, 63), + titlew, 64); + if (slen >= 63) + titlew[63] = 0; + else if (slen == 0) + { + errno = EINVAL; + retval = -1; + goto done; + } + wcscpy (nidw.szInfoTitle, titlew); + + switch (severity) + { + case Ni_None: + nidw.dwInfoFlags = NIIF_NONE; + break; + case Ni_Info: + default: + nidw.dwInfoFlags = NIIF_INFO; + break; + case Ni_Warn: + nidw.dwInfoFlags = NIIF_WARNING; + break; + case Ni_Err: + nidw.dwInfoFlags = NIIF_ERROR; + break; + } + } + + if (!Shell_NotifyIconW (NIM_ADD, (PNOTIFYICONDATAW)&nidw)) + { + /* GetLastError returns meaningless results when + Shell_NotifyIcon fails. */ + DebPrint (("Shell_NotifyIcon ADD failed (err=%d)\n", + GetLastError ())); + errno = EINVAL; + retval = -1; + } + done: + if (*icon && !DestroyIcon (nidw.hIcon)) + DebPrint (("DestroyIcon failed (err=%d)\n", GetLastError ())); + } + return retval; +} + +/* Low-level subroutine to remove a tray notification. Note: we only + pass the minimum data about the notification: its ID and the handle + of the window to which it sends messages. MSDN doesn't say this is + enough, but it works in practice. This allows us to avoid keeping + the notification data around after we show the notification. */ +static void +delete_tray_notification (struct frame *f, int id) +{ + if (FRAME_W32_P (f)) + { + MY_NOTIFYICONDATAW nidw; + + memset (&nidw, 0, sizeof(nidw)); + nidw.hWnd = FRAME_W32_WINDOW (f); + nidw.uID = id; + + if (!Shell_NotifyIconW (NIM_DELETE, (PNOTIFYICONDATAW)&nidw)) + { + /* GetLastError returns meaningless results when + Shell_NotifyIcon fails. */ + DebPrint (("Shell_NotifyIcon DELETE failed\n")); + errno = EINVAL; + return; + } + } + return; +} + +DEFUN ("w32-notification-notify", + Fw32_notification_notify, Sw32_notification_notify, + 0, MANY, 0, + doc: /* Display an MS-Windows tray notification as specified by PARAMS. + +Value is the integer unique ID of the notification that can be used +to remove the notification using `w32-notification-close', which see. +If the function fails, the return value is nil. + +Tray notifications, a.k.a. \"taskbar messages\", are messages that +inform the user about events unrelated to the current user activity, +such as a significant system event, by briefly displaying informative +text in a balloon from an icon in the notification area of the taskbar. + +Parameters in PARAMS are specified as keyword/value pairs. All the +parameters are optional, but if no parameters are specified, the +function will do nothing and return nil. + +The following parameters are supported: + +:icon ICON -- Display ICON in the system tray. If ICON is a string, + it should specify a file name from which to load the + icon; the specified file should be a .ico Windows icon + file. If ICON is not a string, or if this parameter + is not specified, the standard Emacs icon will be used. + +:tip TIP -- Use TIP as the tooltip for the notification. If TIP + is a string, this is the text of a tooltip that will + be shown when the mouse pointer hovers over the tray + icon added by the notification. If TIP is not a + string, or if this parameter is not specified, the + default tooltip text is \"Emacs notification\". The + tooltip text can be up to 127 characters long (63 + on Windows versions before W2K). Longer strings + will be truncated. + +:level LEVEL -- Notification severity level, one of `info', + `warning', or `error'. If given, the value + determines the icon displayed to the left of the + notification title, but only if the `:title' + parameter (see below) is also specified and is a + string. + +:title TITLE -- The title of the notification. If TITLE is a string, + it is displayed in a larger font immediately above + the body text. The title text can be up to 63 + characters long; longer text will be truncated. + +:body BODY -- The body of the notification. If BODY is a string, + it specifies the text of the notification message. + Use embedded newlines to control how the text is + broken into lines. The body text can be up to 255 + characters long, and will be truncated if it's longer. + +Note that versions of Windows before W2K support only `:icon' and `:tip'. +You can pass the other parameters, but they will be ignored on those +old systems. + +There can be at most one active notification at any given time. An +active notification must be removed by calling `w32-notification-close' +before a new one can be shown. + +usage: (w32-notification-notify &rest PARAMS) */) + (ptrdiff_t nargs, Lisp_Object *args) +{ + struct frame *f = SELECTED_FRAME (); + Lisp_Object arg_plist, lres; + EMACS_INT retval; + char *icon, *tip, *title, *msg; + enum NI_Severity severity; + unsigned timeout = 0; + + if (nargs == 0) + return Qnil; + + arg_plist = Flist (nargs, args); + + /* Icon. */ + lres = Fplist_get (arg_plist, QCicon); + if (STRINGP (lres)) + icon = SSDATA (ENCODE_FILE (Fexpand_file_name (lres, Qnil))); + else + icon = (char *)""; + + /* Tip. */ + lres = Fplist_get (arg_plist, QCtip); + if (STRINGP (lres)) + tip = SSDATA (code_convert_string_norecord (lres, Qutf_8, 1)); + else + tip = (char *)"Emacs notification"; + + /* Severity. */ + lres = Fplist_get (arg_plist, QClevel); + if (NILP (lres)) + severity = Ni_None; + else if (EQ (lres, Qinfo)) + severity = Ni_Info; + else if (EQ (lres, Qwarning)) + severity = Ni_Warn; + else if (EQ (lres, Qerror)) + severity = Ni_Err; + else + severity = Ni_Info; + + /* Title. */ + lres = Fplist_get (arg_plist, QCtitle); + if (STRINGP (lres)) + title = SSDATA (code_convert_string_norecord (lres, Qutf_8, 1)); + else + title = (char *)""; + + /* Notification body text. */ + lres = Fplist_get (arg_plist, QCbody); + if (STRINGP (lres)) + msg = SSDATA (code_convert_string_norecord (lres, Qutf_8, 1)); + else + msg = (char *)""; + + /* Do it! */ + retval = add_tray_notification (f, icon, tip, severity, timeout, title, msg); + return (retval < 0 ? Qnil : make_number (retval)); +} + +DEFUN ("w32-notification-close", + Fw32_notification_close, Sw32_notification_close, + 1, 1, 0, + doc: /* Remove the MS-Windows tray notification specified by its ID. */) + (Lisp_Object id) +{ + struct frame *f = SELECTED_FRAME (); + + if (INTEGERP (id)) + delete_tray_notification (f, XINT (id)); + + return Qnil; +} + +#endif /* WINDOWSNT && !HAVE_DBUS */ + /*********************************************************************** Initialization @@ -8918,19 +9760,29 @@ syms_of_w32fns (void) DEFSYM (Qctrl, "ctrl"); DEFSYM (Qcontrol, "control"); DEFSYM (Qshift, "shift"); - DEFSYM (Qfont_param, "font-parameter"); + DEFSYM (Qfont_parameter, "font-parameter"); DEFSYM (Qgeometry, "geometry"); DEFSYM (Qworkarea, "workarea"); DEFSYM (Qmm_size, "mm-size"); DEFSYM (Qframes, "frames"); DEFSYM (Qtip_frame, "tip-frame"); + DEFSYM (Qassq_delete_all, "assq-delete-all"); DEFSYM (Qunicode_sip, "unicode-sip"); +#if defined WINDOWSNT && !defined HAVE_DBUS + DEFSYM (QCicon, ":icon"); + DEFSYM (QCtip, ":tip"); + DEFSYM (QClevel, ":level"); + DEFSYM (Qinfo, "info"); + DEFSYM (Qwarning, "warning"); + DEFSYM (QCtitle, ":title"); + DEFSYM (QCbody, ":body"); +#endif /* Symbols used elsewhere, but only in MS-Windows-specific code. */ - DEFSYM (Qgnutls_dll, "gnutls"); - DEFSYM (Qlibxml2_dll, "libxml2"); + DEFSYM (Qgnutls, "gnutls"); + DEFSYM (Qlibxml2, "libxml2"); DEFSYM (Qserif, "serif"); - DEFSYM (Qzlib_dll, "zlib"); + DEFSYM (Qzlib, "zlib"); Fput (Qundefined_color, Qerror_conditions, listn (CONSTYPE_PURE, 2, Qundefined_color, Qerror)); @@ -8968,11 +9820,15 @@ When non-nil, the Start menu is opened by tapping the key. If you set this to nil, the left \"Windows\" key is processed by Emacs according to the value of `w32-lwindow-modifier', which see. -Note that some combinations of the left \"Windows\" key with other keys are -caught by Windows at low level, and so binding them in Emacs will have no -effect. For example, -r always pops up the Windows Run dialog, -- pops up the "System Properties" dialog, etc. However, see -the doc string of `w32-phantom-key-code'. */); +Note that some combinations of the left \"Windows\" key with other +keys are caught by Windows at low level. For example, -r +pops up the Windows Run dialog, - pops up the "System +Properties" dialog, etc. On Windows 10, no \"Windows\" key +combinations are normally handed to applications. To enable Emacs to +process \"Windows\" key combinations, use the function +`w32-register-hot-key`. + +For Windows 98/ME, see the doc string of `w32-phantom-key-code'. */); Vw32_pass_lwindow_to_system = Qt; DEFVAR_LISP ("w32-pass-rwindow-to-system", @@ -8983,11 +9839,15 @@ When non-nil, the Start menu is opened by tapping the key. If you set this to nil, the right \"Windows\" key is processed by Emacs according to the value of `w32-rwindow-modifier', which see. -Note that some combinations of the right \"Windows\" key with other keys are -caught by Windows at low level, and so binding them in Emacs will have no -effect. For example, -r always pops up the Windows Run dialog, -- pops up the "System Properties" dialog, etc. However, see -the doc string of `w32-phantom-key-code'. */); +Note that some combinations of the right \"Windows\" key with other +keys are caught by Windows at low level. For example, -r +pops up the Windows Run dialog, - pops up the "System +Properties" dialog, etc. On Windows 10, no \"Windows\" key +combinations are normally handed to applications. To enable Emacs to +process \"Windows\" key combinations, use the function +`w32-register-hot-key`. + +For Windows 98/ME, see the doc string of `w32-phantom-key-code'. */); Vw32_pass_rwindow_to_system = Qt; DEFVAR_LISP ("w32-phantom-key-code", @@ -8997,7 +9857,11 @@ Value is a number between 0 and 255. Phantom key presses are generated in order to stop the system from acting on \"Windows\" key events when `w32-pass-lwindow-to-system' or -`w32-pass-rwindow-to-system' is nil. */); +`w32-pass-rwindow-to-system' is nil. + +This variable is only used on Windows 98 and ME. For other Windows +versions, see the documentation of the `w32-register-hot-key` +function. */); /* Although 255 is technically not a valid key code, it works and means that this hack won't interfere with any real key code. */ XSETINT (Vw32_phantom_key_code, 255); @@ -9027,7 +9891,9 @@ Any other value will cause the Scroll Lock key to be ignored. */); doc: /* Modifier to use for the left \"Windows\" key. The value can be hyper, super, meta, alt, control or shift for the respective modifier, or nil to appear as the `lwindow' key. -Any other value will cause the key to be ignored. */); +Any other value will cause the key to be ignored. + +Also see the documentation of the `w32-register-hot-key` function. */); Vw32_lwindow_modifier = Qnil; DEFVAR_LISP ("w32-rwindow-modifier", @@ -9035,7 +9901,9 @@ Any other value will cause the key to be ignored. */); doc: /* Modifier to use for the right \"Windows\" key. The value can be hyper, super, meta, alt, control or shift for the respective modifier, or nil to appear as the `rwindow' key. -Any other value will cause the key to be ignored. */); +Any other value will cause the key to be ignored. + +Also see the documentation of the `w32-register-hot-key` function. */); Vw32_rwindow_modifier = Qnil; DEFVAR_LISP ("w32-apps-modifier", @@ -9211,6 +10079,18 @@ Default is nil. This variable has effect only on Windows Vista and later. */); w32_disable_new_uniscribe_apis = 0; + DEFVAR_LISP ("w32-tooltip-extra-pixels", + Vw32_tooltip_extra_pixels, + doc: /* Number of pixels added after tooltip text. +On Windows some fonts may cause the last character of a tooltip be +truncated or wrapped around to the next line. Adding some extra space +at the end of the toooltip works around this problem. + +This variable specifies the number of pixels that shall be added. The +default value t means to add the width of one canonical character of the +tip frame. */); + Vw32_tooltip_extra_pixels = Qt; + #if 0 /* TODO: Port to W32 */ defsubr (&Sx_change_window_property); defsubr (&Sx_delete_window_property); @@ -9258,6 +10138,10 @@ This variable has effect only on Windows Vista and later. */); defsubr (&Sw32_window_exists_p); defsubr (&Sw32_battery_status); defsubr (&Sw32__menu_bar_in_use); +#if defined WINDOWSNT && !defined HAVE_DBUS + defsubr (&Sw32_notification_notify); + defsubr (&Sw32_notification_close); +#endif #ifdef WINDOWSNT defsubr (&Sfile_system_info); @@ -9296,6 +10180,12 @@ static PVOID except_addr; /* Stack overflow recovery. */ +/* MinGW headers don't declare this (should be in malloc.h). Also, + the function is not present pre-W2K, so make the call through + a function pointer. */ +typedef int (__cdecl *_resetstkoflw_proc) (void); +static _resetstkoflw_proc resetstkoflw; + /* Re-establish the guard page at stack limit. This is needed because when a stack overflow is detected, Windows removes the guard bit from the guard page, so if we don't re-establish that protection, @@ -9303,12 +10193,14 @@ static PVOID except_addr; void w32_reset_stack_overflow_guard (void) { - /* MinGW headers don't declare this (should be in malloc.h). */ - _CRTIMP int __cdecl _resetstkoflw (void); - + if (resetstkoflw == NULL) + resetstkoflw = + (_resetstkoflw_proc)GetProcAddress (GetModuleHandle ("msvcrt.dll"), + "_resetstkoflw"); /* We ignore the return value. If _resetstkoflw fails, the next stack overflow will crash the program. */ - (void)_resetstkoflw (); + if (resetstkoflw != NULL) + (void)resetstkoflw (); } static void @@ -9369,7 +10261,7 @@ typedef USHORT (WINAPI * CaptureStackBackTrace_proc) (ULONG, ULONG, PVOID *, #define BACKTRACE_LIMIT_MAX 62 -int +static int w32_backtrace (void **buffer, int limit) { static CaptureStackBackTrace_proc s_pfn_CaptureStackBackTrace = NULL; @@ -9442,8 +10334,8 @@ emacs_abort (void) but not on Windows 7. addr2line doesn't mind a missing "0x", but will be confused by an extra one. */ if (except_addr) - sprintf (buf, "\r\nException 0x%lx at this address:\r\n%p\r\n", - except_code, except_addr); + sprintf (buf, "\r\nException 0x%x at this address:\r\n%p\r\n", + (unsigned int) except_code, except_addr); if (stderr_fd >= 0) { if (except_addr) @@ -9539,6 +10431,7 @@ globals_of_w32fns (void) except_addr = 0; #ifndef CYGWIN prev_exception_handler = SetUnhandledExceptionFilter (my_exception_handler); + resetstkoflw = NULL; #endif DEFVAR_INT ("w32-ansi-code-page", @@ -9557,10 +10450,6 @@ globals_of_w32fns (void) InitCommonControls (); syms_of_w32uniscribe (); - - /* Needed for recovery from C stack overflows in batch mode. */ - if (noninteractive) - dwMainThreadId = GetCurrentThreadId (); } #ifdef NTGUI_UNICODE