From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Po Lu via "Emacs development discussions." Newsgroups: gmane.emacs.devel Subject: Windows 9X without KernelEx Date: Fri, 14 Jun 2024 23:12:59 +0800 Message-ID: <875xub8sn8.fsf@yahoo.com> References: <875xub8sn8.fsf.ref@yahoo.com> Reply-To: Po Lu Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="24508"; mail-complaints-to="usenet@ciao.gmane.io" To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Fri Jun 14 17:14:08 2024 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1sI8cy-00065m-CE for ged-emacs-devel@m.gmane-mx.org; Fri, 14 Jun 2024 17:14:08 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sI8cI-0001d3-Kl; Fri, 14 Jun 2024 11:13:26 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sI8cD-0001b2-7b for emacs-devel@gnu.org; Fri, 14 Jun 2024 11:13:22 -0400 Original-Received: from sonic311-24.consmr.mail.ne1.yahoo.com ([66.163.188.205]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sI8c8-0005xt-JS for emacs-devel@gnu.org; Fri, 14 Jun 2024 11:13:20 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1718377987; bh=k6c6RvdHZjZ4mfdZSs9TizA6IffPrqTbHg6rR7Ia/Go=; h=From:To:Subject:Date:References:From:Subject:Reply-To; b=M8cC40W6eMwMpdOGecDZ6RctVFlC0R4Ft5ZaIPT70sePK8TbaGplRUDIK86LmhXiz8P6so/Pv+5n77tLUlfEebWmlhZ4y/vRHY2pbeYqGO1bEzgd/nARHtSdmQu+Mhh67AV95J3mT1uHzKJd15KVA2kXjTsRucieviSUr6IfkdNwSDwD5nX7cmQT97BaURvxNyrnrM6vRKcJ2k7YJZ2ObNgjRPo2UsSa63FlHARxhDD28wyrS5mz3O5OmoCVeWEBfAOBwsyU/tsVSktFvyIJwPFTxiWa/I9sLhRpUBbYIPH1XtkfiblsM6bWKOg/wQVxzCEW4d2/veevyYhaHBt9Ag== X-SONIC-DKIM-SIGN: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1718377987; bh=UP1dpJNdBw2cXVn/ayIGHtUHb23W4w+b5pVh6X+2Y9H=; h=X-Sonic-MF:From:To:Subject:Date:From:Subject; b=eSS/zroqRt+r5WZtZTBUOv7e6ejlR9l+ajcuukGre3WIfjOOtLEIRrhrWohrPjxDo3k5N2hHrRm2DBQTLNTTuXaNqWarQFRmSGpJOqmcstKRJ1IVGcvPpi+UhszsbvwY5gkHKzbf/3JJsLlZy8BkWK14tLRzKfLR9YmAshbJdhjXMI6N+UtVSaFRpTphPSnyDbp8seTlChBrTq0Vs8tq9zPyecBEmnOww4F8kYtfAcYiEUfiXcfTvXCV7By68mec0c7SMQ4UcjnDgcZM5ClfjqHOAhCsdKDObPD+2O46bTz7ZarTnRVk3eUfjk62k4UJ/6p1eJs2daKD8cSoNJ7LEQ== X-YMail-OSG: b7zhvjoVM1mU69BB_RafTEpjqtlEJ0eTt.rAciYhkoiciNOnwgkdHnP7cJlxz6L iqXvJEQw85kCqNo68dymOOtlnG7yAeQtMDTOqKmOTNeQVgPAA1iDJpJiCYVVVycyh9wLEbJHj_Ho IoD.IbSK6jI4cuEhyFl5ttkJ7IUDLbxuLxohnm.7D3svzD4JG.hQuZATx6dsql7jq1f4vzlUGAZs RdLwOXgt__AazxJbxAtgMt9VnvzHENbCi6LICOVX.I3ghG.imKrBX3VTf.gxjEdGKwG4yROnTjC0 __QHWMXhhyk2cS8yRNmjLI7KK4t1mgAFT_UiRnbswi3FE4VDecElAfKAZ0f80GDoVyCB46CBFTVF g3YPZ0mP.HnUngDW5In_A2bVp3zLElwNUtN0DSsqBBxkNpIciIF1PD_7yCZA4Xh_dpkmqw.EPFSM iDe46bWdqdRuC8d8njE0_jAsEPQ12sNKTWqd8ymII_uuePA3Vbt4uLkc09aPlMa7xPll_SzcJEtw 9C1zMvjj90CLAFOKqw7lgbI1E_N10jFqqAKsT6B8cAoCDi6PrUewAvhMWBqWwmV2Kp2J2aotyLAv Sdf0TA0obkYp_X5I9vSUUP4.oFq0rJ3Zs.R5udk1oFDvhAcOlfU0cDg4p3EK.tA3rVU_4aIQGIfh elbU403r4vaNCxHyYkl_LpMpAx8WvzFKQXoNpaftXc2k9AmMRnr5QWa3qeCnrVt8iVBj74DJferl WcQRO4WeKioPhLKC6wyDKynwimweUZP9SNA9QkPn7Cl_y4cwfekIDSrY4z8tlNQtx8uwb1fQ7_ld iJqT6h2zrMLnFXDWrvsWCyvBEyaiff7feqByxY7Skw X-Sonic-MF: X-Sonic-ID: f0824a1c-9208-40d2-a385-3c7084a54e55 Original-Received: from sonic.gate.mail.ne1.yahoo.com by sonic311.consmr.mail.ne1.yahoo.com with HTTP; Fri, 14 Jun 2024 15:13:07 +0000 Original-Received: by hermes--production-sg3-7b469d9f6-jl6j9 (Yahoo Inc. Hermes SMTP Server) with ESMTPA ID b6c5067d22befd38fc449e9603cb0346; Fri, 14 Jun 2024 15:13:04 +0000 (UTC) X-Mailer: WebService/1.1.22407 mail.backend.jedi.jws.acl:role.jedi.acl.token.atz.jws.hermes.yahoo Received-SPF: pass client-ip=66.163.188.205; envelope-from=luangruo@yahoo.com; helo=sonic311-24.consmr.mail.ne1.yahoo.com X-Spam_score_int: 12 X-Spam_score: 1.2 X-Spam_bar: + X-Spam_report: (1.2 / 5.0 requ) BAYES_00=-1.9, DKIM_ADSP_CUSTOM_MED=0.001, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, FREEMAIL_FROM=0.001, NML_ADSP_CUSTOM_MED=0.9, PDS_OTHER_BAD_TLD=1.999, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:320077 Archived-At: Having become concerned that Emacs doesn't function on the complete set of systems advertised in nt/README*, and after a comparison of the symbol imports of emacs.exe with those exported by system libraries on MS-Windows 95 confirmed my fears, I've produced a patch that should enable Emacs to function on all Windows 9X systems with UNICOWS.DLL, whether or not KernelEx is installed. It further converts the linker dependency on USP10.DLL to an optional dependency, because information on the internet as to the availability of USP10.DLL on Windows 95 is too ambiguous and contradictory to be trusted. It has not been put to the test, but barring glaring oversights or prejudice to modern Windows users, might this be installed now in its untested condition? Otherwise we are liable to proceed to more important business till the release, by which time it will be far too late. 2024-06-14 Po Lu * configure.ac: Do not link with -lusp10 on non-Cygwin W32 systems. * src/emacs.c (main): Call init_w32notify. * src/w32.c (w32_init_file_name_codepage): Clear g_b_init_is_windows9x, so that is_windows_9x may return values independent of the dump system before globals_of_w32 is called. Set w32_unicode_filenames to 0, again that it may be independent of its value at the time of dumping. (pfnCancelIo): New function pointer. (sys_write): Do not call CancelIo when absent. (init_ntproc): Initialize pfnCancelIo. * src/w32.h (init_w32notify): New function declaration. * src/w32fns.c (pfnSendInput): New function. (funhook): Use new function pointer. (setup_w32_kbdhook): Return if !pfnSendInput. (Fx_create_frame): Do not register Uniscribe font drivers if Uniscribe is unavailable. (pfnSHFileOperationW): New variable. (Fsystem_move_file_to_trash): Call SHFileOperationW through such variable. (pfnShellExecuteExW): New variable. (Fw32_shell_execute): Call ShellExecuteExW through such variable. (pfnGetMenuBarInfo, pfnGetWindowInfo): New variables. (Fw32_frame_geometry): Call GetWindowInfo and GetMenuBarInfo through those variables. When the former is absent, substitute GetWindowLongA and GetWindowRect for the same. (pfnShell_NotifyIconW): New function pointer. (add_tray_notification, delete_tray_notification) (Fw32_notification_notify): Call Shell_NotifyIconW through such function pointer, punting when it is unavailable. (Fw32_notification_close): Likewise. (globals_of_w32fns): Initialize new function pointers. * src/w32notify.c (pfnReadDirectoryChangesW, pfnCancelIo): New variables. (watch_end, watch_completion, WINAPI, remove_watch) (Fw32notify_rm_watch, Fw32notify_valid_p): Call ReadDirectoryChangesW and CancelIo through those variables. (init_w32notify): Attempt to load those functions from kernel32.dll. * src/w32term.c (w32_set_window_size): Do not call GetMenuBarInfo when absent. (syms_of_w32term): Document status of feature consequently unavailable on Windows 95. * src/w32uniscribe.c (pfnScriptItemize, pfnScriptShape) (pfnScriptPlace, pfnScriptGetGlyphABCWidth, pfnScriptFreeCache) (pfnScriptGetCMap): New function pointers. (uniscribe_close, uniscribe_shape, uniscribe_encode_char) (uniscribe_check_otf_1): Call Uniscribe functions through the same. (syms_of_w32uniscribe_for_pdumper): Load Uniscribe library and required functions from the same, and if unavailable, return while leaving uniscribe_available intact. diff --git a/configure.ac b/configure.ac index f75c0a98820..e4ce59b8a05 100644 --- a/configure.ac +++ b/configure.ac @@ -3133,7 +3133,7 @@ AC_DEFUN NATIVE_IMAGE_API="yes (w32)" W32_OBJ="$W32_OBJ w32image.o" fi - W32_LIBS="$W32_LIBS -lwinmm -lusp10 -lgdi32 -lcomdlg32" + W32_LIBS="$W32_LIBS -lwinmm -lgdi32 -lcomdlg32" W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32" W32_RES_LINK="\$(EMACSRES)" CLIENTRES="emacsclient.res" diff --git a/src/emacs.c b/src/emacs.c index d786bc65141..bb3070ed2fa 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -2187,6 +2187,7 @@ main (int argc, char **argv) Emacs. */ init_environment (argv); init_ntproc (will_dump_p ()); /* must precede init_editfns. */ + init_w32notify (); #endif /* AIX crashes are reported in system versions 3.2.3 and 3.2.4 diff --git a/src/w32.c b/src/w32.c index 1c6a56bcbd9..67aec88cb65 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1683,6 +1683,15 @@ w32_valid_pointer_p (void *p, int size) void w32_init_file_name_codepage (void) { + /* This must be initialized to a suitable value lest it be in a dumped + Emacs erroneously set to that of the build system during + startup. */ + g_b_init_is_windows_9x = false; + if (is_windows_9x ()) + w32_unicode_filenames = 0; + else + w32_unicode_filenames = 1; + file_name_codepage = CP_ACP; w32_ansi_code_page = CP_ACP; } @@ -9261,6 +9270,9 @@ sys_read (int fd, char * buffer, unsigned int count) /* From w32xfns.c */ extern HANDLE interrupt_handle; +/* Functions required for serial port communications. */ +static BOOL (WINAPI *pfnCancelIo) (HANDLE); + int sys_write (int fd, const void * buffer, unsigned int count) { @@ -9350,7 +9362,8 @@ sys_write (int fd, const void * buffer, unsigned int count) Don't bother cleaning up as we may only get stuck in buggy drivers. */ PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR); - CancelIo (hnd); + if (pfnCancelIo) + (*pfnCancelIo) (hnd); errno = EIO; /* Why not EINTR? */ nchars = -1; break; @@ -10555,6 +10568,13 @@ init_ntproc (int dumping) /* Reset the volume info cache. */ volume_cache = NULL; } + + { + HANDLE kernel32 = GetModuleHandle ("kernel32"); + + if (kernel32) + pfnCancelIo = (void *) get_proc_addr (kernel32, "CancelIo"); + } } /* shutdown_handler ensures that buffers' autosave files are up to diff --git a/src/w32.h b/src/w32.h index cf470ae9901..8a732de7e79 100644 --- a/src/w32.h +++ b/src/w32.h @@ -249,6 +249,9 @@ #define FILE_DONT_CLOSE 0x1000 /* Used instead of execvp to restart Emacs. */ extern int w32_reexec_emacs (char *, const char *); +/* Initialize w32notify.c. */ +extern void init_w32notify (void); + #ifdef HAVE_GNUTLS #include diff --git a/src/w32fns.c b/src/w32fns.c index 5b0e4a895d0..81261281ae9 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -2613,6 +2613,9 @@ my_post_msg (W32Msg * wmsg, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } #ifdef WINDOWSNT + +static UINT (WINAPI *pfnSendInput) (UINT, LPINPUT, int); + /* The Windows keyboard hook callback. */ static LRESULT CALLBACK funhook (int code, WPARAM w, LPARAM l) @@ -2689,8 +2692,8 @@ funhook (int code, WPARAM w, LPARAM l) 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))) + 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)); @@ -2705,7 +2708,7 @@ funhook (int code, WPARAM w, LPARAM l) inputs[1].ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; inputs[1].ki.time = 0; - SendInput (2, inputs, sizeof (INPUT)); + (*pfnSendInput) (2, inputs, sizeof (INPUT)); } else if (focus != NULL) { @@ -2758,7 +2761,7 @@ funhook (int code, WPARAM w, LPARAM l) inputs[1].ki.dwFlags = (hs->flags & LLKHF_EXTENDED) ? KEYEVENTF_EXTENDEDKEY : 0; inputs[1].ki.time = 0; - SendInput (2, inputs, sizeof (INPUT)); + (*pfnSendInput) (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. */ @@ -2815,6 +2818,9 @@ setup_w32_kbdhook (HWND hwnd) { kbdhook.hook_count++; + if (!pfnSendInput) + return; + /* This hook gets in the way of debugging, since when Emacs stops, its input thread stops, and there's nothing to process keyboard events, whereas this hook is global, and is invoked in the @@ -6168,7 +6174,8 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, if (harfbuzz_available) register_font_driver (&harfbuzz_font_driver, f); #endif - register_font_driver (&uniscribe_font_driver, f); + if (uniscribe_available) + register_font_driver (&uniscribe_font_driver, f); register_font_driver (&w32font_driver, f); gui_default_parameter (f, parameters, Qfont_backend, Qnil, @@ -8285,6 +8292,8 @@ DEFUN ("x-file-dialog", Fx_file_dialog, Sx_file_dialog, 2, 5, 0, #ifdef WINDOWSNT +static int (WINAPI *pfnSHFileOperationW) (LPSHFILEOPSTRUCTW); + /* Moving files to the system recycle bin. Used by `move-file-to-trash' instead of the default moving to ~/.Trash */ DEFUN ("system-move-file-to-trash", Fsystem_move_file_to_trash, @@ -8344,7 +8353,10 @@ DEFUN ("system-move-file-to-trash", Fsystem_move_file_to_trash, | FOF_NOERRORUI | FOF_NO_CONNECTED_ELEMENTS; file_op_w.fAnyOperationsAborted = FALSE; - result = SHFileOperationW (&file_op_w); + /* This is stated to exist on all versions of Windows NT Emacs + supports. */ + eassert (pfnSHFileOperationW); + result = (*pfnSHFileOperationW) (&file_op_w); } else { @@ -8409,6 +8421,10 @@ DEFUN ("w32-send-sys-command", Fw32_send_sys_command, return Qnil; } +#ifndef CYGWIN +static BOOL (WINAPI *pfnShellExecuteExW) (LPSHELLEXECUTEINFOW); +#endif /* !CYGWIN */ + DEFUN ("w32-shell-execute", Fw32_shell_execute, Sw32_shell_execute, 2, 4, 0, doc: /* Get Windows to perform OPERATION on DOCUMENT. This is a wrapper around the ShellExecute system function, which @@ -8618,7 +8634,7 @@ parameters (e.g., \"printto\" requires the printer address). Otherwise, doc_w = xmalloc (doclen * sizeof (wchar_t)); pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags, SSDATA (document), -1, doc_w, doclen); - if (use_unicode) + if (use_unicode && pfnShellExecuteExW) { wchar_t current_dir_w[MAX_PATH]; SHELLEXECUTEINFOW shexinfo_w; @@ -8670,7 +8686,7 @@ parameters (e.g., \"printto\" requires the printer address). Otherwise, shexinfo_w.lpDirectory = current_dir_w; shexinfo_w.nShow = (FIXNUMP (show_flag) ? XFIXNUM (show_flag) : SW_SHOWDEFAULT); - success = ShellExecuteExW (&shexinfo_w); + success = (*pfnShellExecuteExW) (&shexinfo_w); xfree (doc_w); } else @@ -9051,6 +9067,9 @@ DEFUN ("w32-window-exists-p", Fw32_window_exists_p, Sw32_window_exists_p, return Qt; } +BOOL (WINAPI *pfnGetMenuBarInfo) (HWND, LONG, LONG, PMENUBARINFO); +static BOOL (WINAPI *pfnGetWindowInfo) (HWND, PWINDOWINFO); + DEFUN ("w32-frame-geometry", Fw32_frame_geometry, Sw32_frame_geometry, 0, 1, 0, doc: /* Return geometric attributes of FRAME. FRAME must be a live frame and defaults to the selected one. The return @@ -9113,7 +9132,16 @@ DEFUN ("w32-frame-geometry", Fw32_frame_geometry, Sw32_frame_geometry, 0, 1, 0, block_input (); /* Outer rectangle and borders. */ window.cbSize = sizeof (window); - GetWindowInfo (FRAME_W32_WINDOW (f), &window); + if (pfnGetWindowInfo) + (*pfnGetWindowInfo) (FRAME_W32_WINDOW (f), &window); + else + { + /* Windows 95. */ + memset (&window, 0, sizeof window); + window.dwStyle = GetWindowLongA (FRAME_W32_WINDOW (f), + GWL_STYLE); + GetWindowRect (FRAME_W32_WINDOW (f), &window.rcWindow); + } external_border_width = window.cxWindowBorders; external_border_height = window.cyWindowBorders; /* Title bar. */ @@ -9141,7 +9169,10 @@ DEFUN ("w32-frame-geometry", Fw32_frame_geometry, Sw32_frame_geometry, 0, 1, 0, menu_bar.cbSize = sizeof (menu_bar); menu_bar.rcBar.right = menu_bar.rcBar.left = 0; menu_bar.rcBar.top = menu_bar.rcBar.bottom = 0; - GetMenuBarInfo (FRAME_W32_WINDOW (f), 0xFFFFFFFD, 0, &menu_bar); + + /* XXX: How to implement this on Windows 95? */ + if (pfnGetMenuBarInfo) + (*pfnGetMenuBarInfo) (FRAME_W32_WINDOW (f), 0xFFFFFFFD, 0, &menu_bar); single_menu_bar_height = GetSystemMetrics (SM_CYMENU); wrapped_menu_bar_height = GetSystemMetrics (SM_CYMENUSIZE); unblock_input (); @@ -10027,6 +10058,8 @@ DEFUN ("w32--menu-bar-in-use", Fw32__menu_bar_in_use, Sw32__menu_bar_in_use, #if defined WINDOWSNT && !defined HAVE_DBUS +static BOOL (WINAPI *pfnShell_NotifyIconW) (DWORD, PNOTIFYICONDATAW); + /*********************************************************************** Tray notifications ***********************************************************************/ @@ -10293,7 +10326,7 @@ add_tray_notification (struct frame *f, const char *icon, const char *tip, } } - if (!Shell_NotifyIconW (NIM_ADD, (PNOTIFYICONDATAW)&nidw)) + if (!(*pfnShell_NotifyIconW) (NIM_ADD, (PNOTIFYICONDATAW)&nidw)) { /* GetLastError returns meaningless results when Shell_NotifyIcon fails. */ @@ -10325,7 +10358,7 @@ delete_tray_notification (struct frame *f, int id) nidw.hWnd = FRAME_W32_WINDOW (f); nidw.uID = id; - if (!Shell_NotifyIconW (NIM_DELETE, (PNOTIFYICONDATAW)&nidw)) + if (!(*pfnShell_NotifyIconW) (NIM_DELETE, (PNOTIFYICONDATAW)&nidw)) { /* GetLastError returns meaningless results when Shell_NotifyIcon fails. */ @@ -10391,9 +10424,9 @@ DEFUN ("w32-notification-notify", 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. +Note that versions of Windows before W2K support only `:icon' and +`:tip', and that this function does nothing on Windows 95. 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' @@ -10409,7 +10442,7 @@ DEFUN ("w32-notification-notify", enum NI_Severity severity; unsigned timeout = 0; - if (nargs == 0) + if (nargs == 0 || !pfnShell_NotifyIconW) return Qnil; arg_plist = Flist (nargs, args); @@ -10468,7 +10501,7 @@ DEFUN ("w32-notification-close", { struct frame *f = SELECTED_FRAME (); - if (FIXNUMP (id)) + if (FIXNUMP (id) && !pfnShell_NotifyIconW) delete_tray_notification (f, XFIXNUM (id)); return Qnil; @@ -11489,6 +11522,14 @@ globals_of_w32fns (void) SetGestureConfig_fn = (SetGestureConfig_proc) get_proc_addr (user32_lib, "SetGestureConfig"); + pfnGetMenuBarInfo + = (void *) get_proc_addr (user32_lib, "GetMenuBarInfo"); + pfnGetWindowInfo + = (void *) get_proc_addr (user32_lib, "GetWindowInfo"); +#ifdef WINDOWSNT + pfnSendInput + = (void *) get_proc_addr (user32_lib, "SendInput"); +#endif /* WINDOWSNT */ { HMODULE imm32_lib = GetModuleHandle ("imm32.dll"); @@ -11519,7 +11560,17 @@ globals_of_w32fns (void) get_proc_addr (wtsapi32_lib, "WTSRegisterSessionNotification"); WTSUnRegisterSessionNotification_fn = (WTSUnRegisterSessionNotification_Proc) get_proc_addr (wtsapi32_lib, "WTSUnRegisterSessionNotification"); -#endif + + HMODULE shell32_lib = GetModuleHandle ("shell32"); + pfnSHFileOperationW + = (void *) get_proc_addr (shell32_lib, "SHFileOperationW"); + pfnShellExecuteExW + = (void *) get_proc_addr (shell32_lib, "ShellExecuteExW"); +#ifndef HAVE_DBUS + pfnShell_NotifyIconW + = (void *) get_proc_addr (shell32_lib, "Shell_NotifyIconW"); +#endif /* !HAVE_DBUS */ +#endif /* WINDOWSNT */ /* Support OS dark mode on Windows 10 version 1809 and higher. See `w32_applytheme' which uses appropriate APIs per version of Windows. diff --git a/src/w32notify.c b/src/w32notify.c index c93e8796fe2..b3ba1357a05 100644 --- a/src/w32notify.c +++ b/src/w32notify.c @@ -120,6 +120,12 @@ #define DIRWATCH_SIGNATURE 0x01233210 /* Used for communicating notifications to the main thread. */ struct notifications_set *notifications_set_head; +/* Function pointers. */ +static BOOL (WINAPI *pfnReadDirectoryChangesW) (HANDLE, PVOID, DWORD, BOOL, + DWORD, PDWORD, LPOVERLAPPED, + LPOVERLAPPED_COMPLETION_ROUTINE); +static BOOL (WINAPI *pfnCancelIo) (HANDLE); + static Lisp_Object watch_list; /* Signal to the main thread that we have file notifications for it to @@ -175,7 +181,7 @@ watch_end (ULONG_PTR arg) HANDLE hdir = (HANDLE)arg; if (hdir && hdir != INVALID_HANDLE_VALUE) - CancelIo (hdir); + (*pfnCancelIo) (hdir); } /* A completion routine (a.k.a. "APC function") for handling events @@ -252,10 +258,10 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info) /* Calling ReadDirectoryChangesW quickly to watch again for new notifications. */ - if (!ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, - DIRWATCH_BUFFER_SIZE, dirwatch->subtree, - dirwatch->filter, &_bytes, dirwatch->io_info, - watch_completion)) + if (!(*pfnReadDirectoryChangesW) (dirwatch->dir, dirwatch->buf, + DIRWATCH_BUFFER_SIZE, dirwatch->subtree, + dirwatch->filter, &_bytes, + dirwatch->io_info, watch_completion)) { DebPrint (("ReadDirectoryChangesW error: %lu\n", GetLastError ())); /* If this call fails, it means that the directory is not @@ -270,7 +276,7 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info) /* If we were asked to terminate the thread, then fire the event. */ if (terminate) - SetEvent(dirwatch->terminate); + SetEvent (dirwatch->terminate); } /* Worker routine for the watch thread. */ @@ -284,10 +290,10 @@ watch_worker (LPVOID arg) if (dirwatch->dir) { - bErr = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, - DIRWATCH_BUFFER_SIZE, dirwatch->subtree, - dirwatch->filter, &_bytes, - dirwatch->io_info, watch_completion); + bErr = (*pfnReadDirectoryChangesW) (dirwatch->dir, dirwatch->buf, + DIRWATCH_BUFFER_SIZE, dirwatch->subtree, + dirwatch->filter, &_bytes, + dirwatch->io_info, watch_completion); if (!bErr) { DebPrint (("ReadDirectoryChangesW: %lu\n", GetLastError ())); @@ -436,7 +442,7 @@ remove_watch (struct notification *dirwatch) DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ())); /* We also signal the thread that it can terminate. */ - SetEvent(dirwatch->terminate); + SetEvent (dirwatch->terminate); /* Wait for the thread to exit. FIXME: is there a better method that is not overly complex? */ @@ -466,7 +472,7 @@ remove_watch (struct notification *dirwatch) CloseHandle (dirwatch->thr); dirwatch->thr = NULL; } - CloseHandle(dirwatch->terminate); + CloseHandle (dirwatch->terminate); xfree (dirwatch->buf); xfree (dirwatch->io_info); xfree (dirwatch->watchee); @@ -649,7 +655,7 @@ DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch, if (!NILP (watch_object)) { watch_list = Fdelete (watch_object, watch_list); - dirwatch = (struct notification *)xmint_pointer (watch_descriptor); + dirwatch = (struct notification *) xmint_pointer (watch_descriptor); if (w32_valid_pointer_p (dirwatch, sizeof(struct notification))) status = remove_watch (dirwatch); } @@ -687,7 +693,7 @@ DEFUN ("w32notify-valid-p", Fw32notify_valid_p, Sw32notify_valid_p, 1, 1, 0, if (!NILP (watch_object)) { struct notification *dirwatch = - (struct notification *)xmint_pointer (watch_descriptor); + (struct notification *) xmint_pointer (watch_descriptor); if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)) && dirwatch->dir != NULL) return Qt; @@ -722,3 +728,18 @@ syms_of_w32notify (void) Fprovide (intern_c_string ("w32notify"), Qnil); } + +void +init_w32notify (void) +{ + HANDLE kernel32 = GetModuleHandle ("kernel32"); + + /* Initialize pointers to IO functions that provide file + notifications. In the event that these are absent, no harm will be + done, since their absence indicates that Emacs is running on + Windows 9X, where file notifications are unavailable at the + outset. */ + pfnCancelIo = (void *) get_proc_addr (kernel32, "CancelIo"); + pfnReadDirectoryChangesW + = (void *) get_proc_addr (kernel32, "ReadDirectoryChangesW"); +} diff --git a/src/w32term.c b/src/w32term.c index 62037e3b2cd..30f0f9b50f9 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -7143,6 +7143,9 @@ w32fullscreen_hook (struct frame *f) f->want_fullscreen |= FULLSCREEN_WAIT; } +/* Defined in w32fns.c. */ +extern BOOL (WINAPI *pfnGetMenuBarInfo) (HWND, LONG, LONG, PMENUBARINFO); + /* Change the size of frame F's Windows window to WIDTH and HEIGHT pixels. If CHANGE_GRAVITY, change to top-left-corner window gravity for this size change and subsequent size changes. Otherwise leave @@ -7163,10 +7166,11 @@ w32_set_window_size (struct frame *f, bool change_gravity, third argument for AdjustWindowRect. See bug#22105. */ info.cbSize = sizeof (info); info.rcBar.top = info.rcBar.bottom = 0; - GetMenuBarInfo (FRAME_W32_WINDOW (f), 0xFFFFFFFD, 0, &info); + if (pfnGetMenuBarInfo) + (*pfnGetMenuBarInfo) (FRAME_W32_WINDOW (f), 0xFFFFFFFD, 0, &info); menu_bar_height = info.rcBar.bottom - info.rcBar.top; - if (w32_add_wrapped_menu_bar_lines) + if (w32_add_wrapped_menu_bar_lines && pfnGetMenuBarInfo) { /* When the menu bar wraps sending a SetWindowPos shrinks the height of the frame then the wrapped menu bar lines are not @@ -8323,7 +8327,9 @@ syms_of_w32term (void) This usually means that the resulting frame height is off by the number of wrapped menu bar lines. If this is non-nil, Emacs adds the height of wrapped menu bar lines when sending frame resize requests to the Windows -API. */); +API. + +This feature is unavailable on Windows 95. */); w32_add_wrapped_menu_bar_lines = 1; /* Tell Emacs about this window system. */ diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c index b3112912c76..2fe90afd5f6 100644 --- a/src/w32uniscribe.c +++ b/src/w32uniscribe.c @@ -108,6 +108,31 @@ memq_no_quit (Lisp_Object elt, Lisp_Object list) return (CONSP (list)); } + +/* Uniscribe function pointers. */ +static HRESULT (WINAPI * pfnScriptItemize) (const WCHAR *, + int, + int, + const SCRIPT_CONTROL *, + const SCRIPT_STATE *, + SCRIPT_ITEM *, int *); +static HRESULT (WINAPI * pfnScriptShape) (HDC, SCRIPT_CACHE *, + const WCHAR *, + int, int, SCRIPT_ANALYSIS *, + WORD *, WORD *, SCRIPT_VISATTR *, + int *); +static HRESULT (WINAPI * pfnScriptPlace) (HDC, SCRIPT_CACHE *, + const WORD *, int, + const SCRIPT_VISATTR *, + SCRIPT_ANALYSIS *, + int *, GOFFSET *, ABC *); +static HRESULT (WINAPI * pfnScriptGetGlyphABCWidth) (HDC, SCRIPT_CACHE *, + WORD, ABC *); +static HRESULT (WINAPI * pfnScriptFreeCache) (SCRIPT_CACHE *); +static HRESULT (WINAPI * pfnScriptGetCMap) (HDC, SCRIPT_CACHE *, + const WCHAR *, + int, DWORD, WORD *); + /* Font backend interface implementation. */ static Lisp_Object @@ -202,7 +227,7 @@ uniscribe_close (struct font *font) else #endif if (uniscribe_font->cache) - ScriptFreeCache ((SCRIPT_CACHE) &(uniscribe_font->cache)); + (*pfnScriptFreeCache) ((SCRIPT_CACHE) &(uniscribe_font->cache)); uniscribe_font->cache = NULL; @@ -320,8 +345,8 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) max_items = 2; items = xmalloc (sizeof (SCRIPT_ITEM) * max_items + 1); - while ((result = ScriptItemize (chars, nchars, max_items, NULL, NULL, - items, &nitems)) == E_OUTOFMEMORY) + while ((result = (*pfnScriptItemize) (chars, nchars, max_items, NULL, NULL, + items, &nitems)) == E_OUTOFMEMORY) { /* If that wasn't enough, keep trying with one more run. */ max_items++; @@ -344,17 +369,18 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) { int nglyphs, nchars_in_run; nchars_in_run = items[i+1].iCharPos - items[i].iCharPos; - /* Force ScriptShape to generate glyphs in the same order as + /* Force (*pfnScriptShape) to generate glyphs in the same order as they are in the input LGSTRING, which is in the logical order. */ items[i].a.fLogicalOrder = 1; /* Context may be NULL here, in which case the cache should be used without needing to select the font. */ - result = ScriptShape (context, (SCRIPT_CACHE) &(uniscribe_font->cache), - chars + items[i].iCharPos, nchars_in_run, - max_glyphs - done_glyphs, &(items[i].a), - glyphs, clusters, attributes, &nglyphs); + result + = (*pfnScriptShape) (context, (SCRIPT_CACHE) &(uniscribe_font->cache), + chars + items[i].iCharPos, nchars_in_run, + max_glyphs - done_glyphs, &(items[i].a), + glyphs, clusters, attributes, &nglyphs); if (result == E_PENDING && !context) { @@ -365,10 +391,12 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) context = get_frame_dc (f); old_font = SelectObject (context, FONT_HANDLE (font)); - result = ScriptShape (context, (SCRIPT_CACHE) &(uniscribe_font->cache), - chars + items[i].iCharPos, nchars_in_run, - max_glyphs - done_glyphs, &(items[i].a), - glyphs, clusters, attributes, &nglyphs); + result + = (*pfnScriptShape) (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), + chars + items[i].iCharPos, nchars_in_run, + max_glyphs - done_glyphs, &(items[i].a), + glyphs, clusters, attributes, &nglyphs); } if (result == E_OUTOFMEMORY) @@ -390,9 +418,11 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) } else { - result = ScriptPlace (context, (SCRIPT_CACHE) &(uniscribe_font->cache), - glyphs, nglyphs, attributes, &(items[i].a), - advances, offsets, &overall_metrics); + result + = (*pfnScriptPlace) (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), + glyphs, nglyphs, attributes, &(items[i].a), + advances, offsets, &overall_metrics); if (result == E_PENDING && !context) { /* Cache not complete... */ @@ -400,10 +430,11 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) context = get_frame_dc (f); old_font = SelectObject (context, FONT_HANDLE (font)); - result = ScriptPlace (context, - (SCRIPT_CACHE) &(uniscribe_font->cache), - glyphs, nglyphs, attributes, &(items[i].a), - advances, offsets, &overall_metrics); + result + = (*pfnScriptPlace) (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), + glyphs, nglyphs, attributes, &(items[i].a), + advances, offsets, &overall_metrics); } if (SUCCEEDED (result)) { @@ -469,7 +500,7 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) then updated for each successive glyph in the grapheme cluster. */ /* FIXME: Should we use DIRECTION here instead - of what ScriptItemize guessed? */ + of what (*pfnScriptItemize) guessed? */ if (items[i].a.fRTL) { int j1 = j; @@ -496,7 +527,7 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) LGLYPH_SET_ASCENT (lglyph, font->ascent); LGLYPH_SET_DESCENT (lglyph, font->descent); - result = ScriptGetGlyphABCWidth + result = (*pfnScriptGetGlyphABCWidth) (context, (SCRIPT_CACHE) &(uniscribe_font->cache), glyphs[j], &char_metric); if (result == E_PENDING && !context) @@ -505,7 +536,7 @@ uniscribe_shape (Lisp_Object lgstring, Lisp_Object direction) f = XFRAME (selected_frame); context = get_frame_dc (f); old_font = SelectObject (context, FONT_HANDLE (font)); - result = ScriptGetGlyphABCWidth + result = (*pfnScriptGetGlyphABCWidth) (context, (SCRIPT_CACHE) &(uniscribe_font->cache), glyphs[j], &char_metric); } @@ -624,7 +655,8 @@ uniscribe_encode_char (struct font *font, int c) convert surrogate pairs to glyph indexes correctly. */ { items = (SCRIPT_ITEM *) alloca (sizeof (SCRIPT_ITEM) * 2 + 1); - if (SUCCEEDED (ScriptItemize (ch, len, 2, NULL, NULL, items, &nitems))) + if (SUCCEEDED ((*pfnScriptItemize) (ch, len, 2, NULL, NULL, items, + &nitems))) { HRESULT result; /* Surrogates seem to need 2 here, even though only one glyph is @@ -635,14 +667,14 @@ uniscribe_encode_char (struct font *font, int c) SCRIPT_VISATTR attrs[2]; int nglyphs; - /* Force ScriptShape to generate glyphs in the logical + /* Force (*pfnScriptShape) to generate glyphs in the logical order. */ items[0].a.fLogicalOrder = 1; - result = ScriptShape (context, - (SCRIPT_CACHE) &(uniscribe_font->cache), - ch, len, 2, &(items[0].a), - glyphs, clusters, attrs, &nglyphs); + result = (*pfnScriptShape) (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), + ch, len, 2, &(items[0].a), + glyphs, clusters, attrs, &nglyphs); if (result == E_PENDING) { @@ -651,10 +683,11 @@ uniscribe_encode_char (struct font *font, int c) f = XFRAME (selected_frame); context = get_frame_dc (f); old_font = SelectObject (context, FONT_HANDLE (font)); - result = ScriptShape (context, - (SCRIPT_CACHE) &(uniscribe_font->cache), - ch, len, 2, &(items[0].a), - glyphs, clusters, attrs, &nglyphs); + result + = (*pfnScriptShape) (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), + ch, len, 2, &(items[0].a), + glyphs, clusters, attrs, &nglyphs); } if (SUCCEEDED (result) && nglyphs == 1) @@ -670,9 +703,10 @@ uniscribe_encode_char (struct font *font, int c) when shaped. But we still need the return from here to be valid for the shaping engine to be invoked later. */ - result = ScriptGetCMap (context, - (SCRIPT_CACHE) &(uniscribe_font->cache), - ch, len, 0, glyphs); + result + = (*pfnScriptGetCMap) (context, + (SCRIPT_CACHE) &(uniscribe_font->cache), + ch, len, 0, glyphs); if (SUCCEEDED (result) && glyphs[0]) code = glyphs[0]; } @@ -942,7 +976,7 @@ uniscribe_check_otf_1 (HDC context, Lisp_Object script, Lisp_Object lang, no_support: if (cache) - ScriptFreeCache (&cache); + (*pfnScriptFreeCache) (&cache); return ret; } @@ -1505,10 +1539,27 @@ syms_of_w32uniscribe_for_pdumper (void) return; /* Don't register if Uniscribe is not available. */ - HMODULE uniscribe = GetModuleHandle ("usp10"); + HMODULE uniscribe = LoadLibrary ("usp10.dll"); if (!uniscribe) return; + pfnScriptItemize = (void *) get_proc_addr (uniscribe, "ScriptItemize"); + pfnScriptShape = (void *) get_proc_addr (uniscribe, "ScriptShape"); + pfnScriptPlace = (void *) get_proc_addr (uniscribe, "ScriptPlace"); + pfnScriptGetGlyphABCWidth + = (void *) get_proc_addr (uniscribe, "GetGlyphABCWidth"); + pfnScriptFreeCache + = (void *) get_proc_addr (uniscribe, "ScriptFreeCache"); + pfnScriptGetCMap + = (void *) get_proc_addr (uniscribe, "ScriptGetCMap"); + if (!pfnScriptItemize || !pfnScriptShape || !pfnScriptPlace + || !pfnScriptGetGlyphABCWidth || !pfnScriptFreeCache + || !pfnScriptGetCMap) + { + FreeLibrary (uniscribe); + return; + } + uniscribe_available = 1; register_font_driver (&uniscribe_font_driver, NULL);