442
|
1 /* Utility routines for XEmacs on Windows 9x, NT and Cygwin.
|
771
|
2 Copyright (C) 2000, 2001, 2002 Ben Wing.
|
442
|
3
|
|
4 This file is part of XEmacs.
|
|
5
|
|
6 XEmacs is free software; you can redistribute it and/or modify it
|
|
7 under the terms of the GNU General Public License as published by the
|
|
8 Free Software Foundation; either version 2, or (at your option) any
|
|
9 later version.
|
|
10
|
|
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
|
|
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
14 for more details.
|
|
15
|
|
16 You should have received a copy of the GNU General Public License
|
|
17 along with XEmacs; see the file COPYING. If not, write to the Free
|
|
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
19 02111-1307, USA. */
|
|
20
|
|
21 #include <config.h>
|
|
22 #include "lisp.h"
|
|
23
|
|
24 #include "buffer.h"
|
771
|
25 #include "console-msw.h"
|
611
|
26
|
771
|
27 #include "sysfile.h"
|
|
28 #include "sysproc.h"
|
611
|
29 #include "syssignal.h"
|
|
30 #include "systime.h"
|
442
|
31
|
771
|
32 /* Control conversion of upper case file names to lower case.
|
|
33 nil means no, t means yes. */
|
|
34 Lisp_Object Vmswindows_downcase_file_names;
|
|
35
|
|
36 int mswindows_windows9x_p;
|
|
37
|
442
|
38 pfSwitchToThread_t xSwitchToThread;
|
|
39
|
771
|
40 pfNetUserEnum_t xNetUserEnum;
|
|
41 pfNetApiBufferFree_t xNetApiBufferFree;
|
|
42
|
|
43 /* Convert a filename in standard Win32 format into our internal format
|
|
44 (which may be significantly different if we're running on Cygwin), and
|
|
45 turn it into a file: URL. Return a newly malloc()ed string.
|
442
|
46
|
771
|
47 #### This comes from code that just prepended `file:', which is not
|
|
48 good. See comment in mswindows_dde_callback(), case XTYP_EXECUTE.
|
|
49 */
|
|
50 Intbyte *
|
|
51 urlify_filename (Intbyte *filename)
|
|
52 {
|
|
53 Intbyte *pseudo_url;
|
|
54
|
|
55 WIN32_TO_LOCAL_FILE_FORMAT (filename, filename);
|
|
56 pseudo_url = xnew_array (Intbyte, 5 + qxestrlen (filename) + 1);
|
|
57 qxestrcpy_c (pseudo_url, "file:");
|
|
58 qxestrcat (pseudo_url, filename);
|
|
59 /* URL's only have /, no backslash */
|
|
60 for (filename = pseudo_url; *filename; filename++)
|
|
61 {
|
|
62 if (*filename == '\\')
|
|
63 *filename = '/';
|
|
64 }
|
442
|
65
|
771
|
66 return pseudo_url;
|
|
67 }
|
531
|
68
|
442
|
69 Lisp_Object
|
|
70 tstr_to_local_file_format (Extbyte *pathout)
|
|
71 {
|
665
|
72 Intbyte *ttlff;
|
771
|
73
|
|
74 TSTR_TO_C_STRING (pathout, ttlff);
|
|
75 WIN32_TO_LOCAL_FILE_FORMAT (ttlff, ttlff);
|
|
76
|
|
77 return build_intstring (ttlff);
|
|
78 }
|
|
79
|
|
80 /* Normalize filename by converting all path separators to the specified
|
|
81 separator. Also conditionally convert all-upper-case path name
|
|
82 components to lower case. Return a newly malloc()ed string.
|
|
83 */
|
|
84
|
|
85 Intbyte *
|
|
86 mswindows_canonicalize_filename (Intbyte *name)
|
|
87 {
|
|
88 Intbyte *fp = name;
|
|
89 DECLARE_EISTRING (newname);
|
|
90 DECLARE_EISTRING (component);
|
|
91 int do_casefrob = 1;
|
442
|
92
|
771
|
93 /* Always lower-case drive letters a-z, even if the filesystem
|
|
94 preserves case in filenames.
|
|
95 This is so filenames can be compared by string comparison
|
|
96 functions that are case-sensitive. Even case-preserving filesystems
|
|
97 do not distinguish case in drive letters. */
|
|
98 if (name[0] >= 'A' && name[0] <= 'Z' && name[1] == ':')
|
|
99 {
|
|
100 eicat_ch (newname, name[0] + 'a' - 'A');
|
|
101 eicat_ch (newname, ':');
|
|
102 fp += 2;
|
|
103 }
|
|
104
|
|
105 while (1)
|
|
106 {
|
|
107 Emchar ch = charptr_emchar (fp);
|
|
108 if (LOWERCASEP (0, ch))
|
|
109 do_casefrob = 0; /* don't convert this element */
|
442
|
110
|
771
|
111 if (ch == 0 || IS_ANY_SEP (ch))
|
|
112 {
|
|
113 if (do_casefrob && !NILP (Vmswindows_downcase_file_names))
|
|
114 eilwr (component);
|
|
115 do_casefrob = 1;
|
|
116 eicat_ei (newname, component);
|
|
117 eireset (component);
|
|
118 if (IS_DIRECTORY_SEP (ch))
|
|
119 eicat_ch (newname, DIRECTORY_SEP);
|
|
120 else if (ch)
|
|
121 eicat_ch (newname, ch);
|
|
122 else
|
|
123 break;
|
|
124 }
|
|
125 else
|
|
126 eicat_ch (component, ch);
|
|
127
|
|
128 INC_CHARPTR (fp);
|
|
129 }
|
|
130
|
|
131 return eicpyout_malloc (newname, 0);
|
442
|
132 }
|
|
133
|
814
|
134 Extbyte *
|
|
135 mswindows_get_module_file_name (void)
|
|
136 {
|
|
137 Extbyte *path = NULL;
|
|
138 int bufsize = 4096;
|
|
139 int cchpathsize;
|
|
140
|
|
141 while (1)
|
|
142 {
|
|
143 path = (Extbyte *) xrealloc (path, bufsize * XETCHAR_SIZE);
|
|
144 cchpathsize = qxeGetModuleFileName (NULL, path, bufsize);
|
|
145 if (!cchpathsize)
|
|
146 return 0;
|
|
147 if (cchpathsize + 1 <= bufsize)
|
|
148 break;
|
|
149 bufsize *= 2;
|
|
150 }
|
|
151
|
|
152 return path;
|
|
153 }
|
|
154
|
442
|
155 static void
|
|
156 init_potentially_nonexistent_functions (void)
|
|
157 {
|
771
|
158 HMODULE h_kernel = qxeGetModuleHandle (XETEXT ("kernel32"));
|
531
|
159 /* the following does not seem to get mapped in automatically */
|
771
|
160 HMODULE h_netapi = qxeLoadLibrary (XETEXT ("netapi32.dll"));
|
442
|
161
|
|
162 if (h_kernel)
|
|
163 {
|
|
164 xSwitchToThread =
|
|
165 (pfSwitchToThread_t) GetProcAddress (h_kernel, "SwitchToThread");
|
|
166 }
|
|
167
|
531
|
168 if (h_netapi)
|
|
169 {
|
|
170 xNetUserEnum =
|
|
171 (pfNetUserEnum_t) GetProcAddress (h_netapi, "NetUserEnum");
|
|
172 xNetApiBufferFree =
|
|
173 (pfNetApiBufferFree_t) GetProcAddress (h_netapi, "NetApiBufferFree");
|
|
174 }
|
442
|
175 }
|
|
176
|
771
|
177 static Lisp_Object
|
|
178 mswindows_lisp_error_1 (int errnum, int no_recurse)
|
|
179 {
|
|
180 LPTSTR lpMsgBuf;
|
|
181 Lisp_Object result;
|
|
182 Intbyte *inres;
|
|
183 Bytecount len;
|
|
184 int i;
|
|
185
|
|
186 /* The docs for FormatMessage say:
|
|
187
|
|
188 If you pass a specific LANGID in this parameter, FormatMessage
|
|
189 will return a message for that LANGID only. If the function
|
|
190 cannot find a message for that LANGID, it returns
|
|
191 ERROR_RESOURCE_LANG_NOT_FOUND. If you pass in zero, FormatMessage
|
|
192 looks for a message for LANGIDs in the following order:
|
|
193
|
|
194 Language neutral
|
|
195 Thread LANGID, based on the thread's locale value
|
|
196 User default LANGID, based on the user's default locale value
|
|
197 System default LANGID, based on the system default locale value
|
|
198 US English
|
|
199
|
|
200 If FormatMessage doesn't find a message for any of the preceding
|
|
201 LANGIDs, it returns any language message string that is present. If
|
|
202 that fails, it returns ERROR_RESOURCE_LANG_NOT_FOUND. (Note, this is
|
|
203 returned through GetLastError(), not the return value.)
|
|
204
|
|
205 #### what the hell is "language neutral"? i can find no info on this.
|
|
206 so let's do our own language first.
|
|
207 */
|
|
208
|
|
209 for (i = 0; ; i++)
|
|
210 {
|
|
211 int lang = 0;
|
|
212 int retval;
|
|
213
|
|
214 switch (i)
|
|
215 {
|
|
216 #ifdef MULE
|
|
217 /* Urk! Windows 95 doesn't let you set the thread locale!
|
|
218 so we have to maintain our own. */
|
|
219 case 0: lang = LANGIDFROMLCID (mswindows_current_locale ()); break;
|
|
220 case 1: lang = 0; break;
|
|
221 #else
|
|
222 case 0: lang = 0; break;
|
|
223 #endif
|
|
224 default: abort ();
|
|
225 }
|
|
226
|
|
227 retval = qxeFormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
228 | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
229 NULL, errnum, lang,
|
|
230 /* yeah, i'm casting a char ** to a char *.
|
|
231 ya gotta problem widdat? */
|
|
232 (Extbyte *) &lpMsgBuf, 0, NULL);
|
|
233
|
|
234 if (!retval)
|
|
235 {
|
|
236 if (lang != 0)
|
|
237 continue;
|
|
238
|
|
239 if (no_recurse)
|
|
240 return emacs_sprintf_string
|
|
241 ("Unknown error code %d (error return %ld from FormatMessage())",
|
|
242 errnum, GetLastError ());
|
|
243 else
|
|
244 return emacs_sprintf_string
|
|
245 ("Unknown error code %d (error return %s from FormatMessage())",
|
|
246 /* It's OK, emacs_sprintf_string disables GC explicitly */
|
|
247 errnum, XSTRING_DATA (mswindows_lisp_error_1 (errnum, 1)));
|
|
248 }
|
|
249 else
|
|
250 break;
|
|
251 }
|
|
252
|
|
253 TSTR_TO_C_STRING (lpMsgBuf, inres);
|
|
254 len = qxestrlen (inres);
|
|
255 /* Messages tend to end with a period and newline */
|
|
256 if (len >= 3 && !intbyte_strcmp (inres + len - 3, ".\r\n"))
|
|
257 len -= 3;
|
|
258 result = make_string (inres, len);
|
|
259
|
|
260 LocalFree (lpMsgBuf);
|
|
261 return result;
|
|
262 }
|
|
263
|
|
264 Lisp_Object
|
|
265 mswindows_lisp_error (int errnum)
|
|
266 {
|
|
267 return mswindows_lisp_error_1 (errnum, 0);
|
|
268 }
|
|
269
|
|
270 void
|
|
271 mswindows_output_last_error (char *frob)
|
|
272 {
|
|
273 int errval = GetLastError ();
|
|
274 Lisp_Object errmess = mswindows_lisp_error (errval);
|
|
275
|
|
276 stderr_out ("last error during %s is %d: %s\n",
|
|
277 frob, errval, XSTRING_DATA (errmess));
|
|
278 }
|
|
279
|
|
280 DOESNT_RETURN
|
|
281 mswindows_report_process_error (const char *string, Lisp_Object data,
|
|
282 int errnum)
|
|
283 {
|
|
284 report_file_type_error (Qprocess_error, mswindows_lisp_error (errnum),
|
|
285 string, data);
|
|
286 }
|
|
287
|
442
|
288 DEFUN ("mswindows-shell-execute", Fmswindows_shell_execute, 2, 4, 0, /*
|
|
289 Get Windows to perform OPERATION on DOCUMENT.
|
|
290 This is a wrapper around the ShellExecute system function, which
|
|
291 invokes the application registered to handle OPERATION for DOCUMENT.
|
|
292 OPERATION is typically \"open\", \"print\" or \"explore\" (but can be
|
|
293 nil for the default action), and DOCUMENT is typically the name of a
|
|
294 document file or URL, but can also be a program executable to run or
|
|
295 a directory to open in the Windows Explorer.
|
|
296
|
|
297 If DOCUMENT is a program executable, PARAMETERS can be a string
|
|
298 containing command line parameters, but otherwise should be nil.
|
|
299
|
|
300 SHOW-FLAG can be used to control whether the invoked application is hidden
|
|
301 or minimized. If SHOW-FLAG is nil, the application is displayed normally,
|
|
302 otherwise it is an integer representing a ShowWindow flag:
|
|
303
|
|
304 0 - start hidden
|
|
305 1 - start normally
|
|
306 3 - start maximized
|
|
307 6 - start minimized
|
|
308 */
|
|
309 (operation, document, parameters, show_flag))
|
|
310 {
|
|
311 /* Encode filename and current directory. */
|
|
312 Lisp_Object current_dir = Ffile_name_directory (document);
|
|
313 int ret;
|
|
314
|
|
315 CHECK_STRING (document);
|
|
316
|
|
317 if (NILP (current_dir))
|
|
318 current_dir = current_buffer->directory;
|
|
319
|
771
|
320 {
|
|
321 Extbyte *opext = NULL;
|
|
322 Extbyte *parmext = NULL;
|
|
323 Extbyte *path = NULL;
|
|
324 Extbyte *doc = NULL;
|
442
|
325
|
771
|
326 if (STRINGP (operation))
|
|
327 LISP_STRING_TO_TSTR (operation, opext);
|
|
328 if (STRINGP (parameters))
|
|
329 LISP_STRING_TO_TSTR (parameters, parmext);
|
|
330 if (STRINGP (current_dir))
|
|
331 LOCAL_FILE_FORMAT_TO_TSTR (current_dir, path);
|
442
|
332 if (STRINGP (document))
|
|
333 {
|
|
334 #ifdef CYGWIN
|
819
|
335 Extbyte *fname1;
|
|
336 Extbyte *fname2;
|
|
337 int pos, sz;
|
|
338 LISP_STRING_TO_TSTR (document, doc);
|
771
|
339
|
819
|
340 if ((fname1 = strchr (doc, ':')) != NULL
|
|
341 && *++fname1 == '/' && *++fname1 == '/')
|
|
342 {
|
|
343 /* If URL style file, the innards may have Cygwin mount points and
|
|
344 the like. so separate out the innards, process them, and put back
|
|
345 together. */
|
|
346 if (qxestrncasecmp (doc, "file://", 7) == 0)
|
|
347 {
|
|
348 fname1++;
|
|
349 pos = fname1 - doc;
|
|
350 if (!(isalpha (fname1[0]) && (IS_DEVICE_SEP (fname1[1]))))
|
|
351 {
|
|
352 sz = cygwin_posix_to_win32_path_list_buf_size (fname1);
|
|
353 fname2 = alloca (sz + pos);
|
|
354 qxestrncpy (fname2, doc, pos);
|
|
355 doc = fname2;
|
|
356 fname2 += pos;
|
|
357 cygwin_posix_to_win32_path_list (fname1, fname2);
|
|
358 }
|
|
359 }
|
673
|
360 }
|
819
|
361 else {
|
|
362 /* Not URL-style, must be a straight filename. */
|
|
363 LOCAL_FILE_FORMAT_TO_TSTR (document, doc);
|
|
364 }
|
442
|
365 #endif
|
819
|
366
|
442
|
367 }
|
|
368
|
771
|
369 ret = (int) qxeShellExecute (NULL, opext, doc, parmext, path,
|
|
370 (INTP (show_flag) ?
|
|
371 XINT (show_flag) : SW_SHOWDEFAULT));
|
|
372 }
|
442
|
373
|
771
|
374 if (ret <= 32)
|
|
375 {
|
|
376 /* Convert to more standard errors */
|
|
377 #define FROB(a, b) if (ret == a) ret = b
|
|
378 FROB (SE_ERR_ACCESSDENIED, ERROR_ACCESS_DENIED);
|
|
379 FROB (SE_ERR_ASSOCINCOMPLETE, ERROR_NO_ASSOCIATION);
|
|
380 FROB (SE_ERR_DDEBUSY, ERROR_DDE_FAIL);
|
|
381 FROB (SE_ERR_DDEFAIL, ERROR_DDE_FAIL);
|
|
382 FROB (SE_ERR_DDETIMEOUT, ERROR_DDE_FAIL);
|
|
383 FROB (SE_ERR_DLLNOTFOUND, ERROR_DLL_NOT_FOUND);
|
|
384 FROB (SE_ERR_FNF, ERROR_FILE_NOT_FOUND);
|
|
385 FROB (SE_ERR_NOASSOC, ERROR_NO_ASSOCIATION);
|
|
386 FROB (SE_ERR_OOM, ERROR_NOT_ENOUGH_MEMORY);
|
|
387 FROB (SE_ERR_PNF, ERROR_PATH_NOT_FOUND);
|
|
388 FROB (SE_ERR_SHARE, ERROR_SHARING_VIOLATION);
|
|
389 #undef FROB
|
|
390
|
|
391 mswindows_report_process_error ("Running ShellExecute",
|
|
392 ret == ERROR_PATH_NOT_FOUND ?
|
|
393 list4 (Qunbound, operation, document,
|
|
394 current_dir) :
|
|
395 list3 (Qunbound, operation, document),
|
|
396 ret);
|
|
397 }
|
442
|
398
|
771
|
399 return Qt;
|
442
|
400 }
|
|
401
|
673
|
402 #ifdef CYGWIN
|
|
403 DEFUN ("mswindows-cygwin-to-win32-path", Fmswindows_cygwin_to_win32_path, 1, 1, 0, /*
|
|
404 Get the cygwin environment to convert the Unix PATH to win32 format.
|
|
405 No expansion is performed, all conversion is done by the cygwin runtime.
|
|
406 */
|
|
407 (path))
|
|
408 {
|
771
|
409 Intbyte *p;
|
673
|
410 CHECK_STRING (path);
|
|
411
|
|
412 /* There appears to be a bug in the cygwin conversion routines in
|
|
413 that they are not idempotent. */
|
|
414 p = XSTRING_DATA (path);
|
|
415 if (isalpha (p[0]) && (IS_DEVICE_SEP (p[1])))
|
|
416 return path;
|
|
417
|
|
418 /* Use mule and cygwin-safe APIs top get at file data. */
|
771
|
419 LOCAL_TO_WIN32_FILE_FORMAT (p, p);
|
|
420 return build_intstring (p);
|
673
|
421 }
|
|
422 #endif
|
|
423
|
613
|
424 #if defined (WIN32_NATIVE) || defined (CYGWIN_BROKEN_SIGNALS)
|
|
425
|
|
426 /* setitimer() does not exist on native MS Windows, and appears broken
|
|
427 on Cygwin (random lockups when BROKEN_SIGIO is defined), so we
|
|
428 emulate in both cases by using multimedia timers. Furthermore,
|
|
429 the lockups still occur on Cygwin even when we do nothing but
|
|
430 use the standard signalling mechanism -- so we have to emulate
|
|
431 that, too. (But only for timeouts -- we have to use the standard
|
|
432 mechanism for SIGCHLD. Yuck.)
|
|
433 */
|
|
434
|
|
435
|
|
436 /*--------------------------------------------------------------------*/
|
|
437 /* Signal support */
|
|
438 /*--------------------------------------------------------------------*/
|
|
439
|
|
440 #define sigmask(nsig) (1U << nsig)
|
|
441
|
|
442 /* We can support as many signals as fit into word */
|
|
443 #define SIG_MAX 32
|
|
444
|
|
445 /* Signal handlers. Initial value = 0 = SIG_DFL */
|
|
446 static mswindows_sighandler signal_handlers[SIG_MAX] = {0};
|
|
447
|
|
448 /* Signal block mask: bit set to 1 means blocked */
|
|
449 unsigned signal_block_mask = 0;
|
|
450
|
|
451 /* Signal pending mask: bit set to 1 means sig is pending */
|
|
452 unsigned signal_pending_mask = 0;
|
|
453
|
|
454 mswindows_sighandler
|
|
455 mswindows_sigset (int nsig, mswindows_sighandler handler)
|
|
456 {
|
|
457 /* We delegate some signals to the system function */
|
|
458 if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
|
|
459 return signal (nsig, handler);
|
|
460
|
|
461 if (nsig < 0 || nsig > SIG_MAX)
|
|
462 {
|
|
463 errno = EINVAL;
|
|
464 return NULL;
|
|
465 }
|
|
466
|
|
467 /* Store handler ptr */
|
|
468 {
|
|
469 mswindows_sighandler old_handler = signal_handlers[nsig];
|
|
470 signal_handlers[nsig] = handler;
|
|
471 return old_handler;
|
|
472 }
|
|
473 }
|
|
474
|
|
475 int
|
|
476 mswindows_sighold (int nsig)
|
|
477 {
|
|
478 if (nsig < 0 || nsig > SIG_MAX)
|
|
479 return errno = EINVAL;
|
|
480
|
|
481 signal_block_mask |= sigmask (nsig);
|
|
482 return 0;
|
|
483 }
|
|
484
|
|
485 int
|
|
486 mswindows_sigrelse (int nsig)
|
|
487 {
|
|
488 if (nsig < 0 || nsig > SIG_MAX)
|
|
489 return errno = EINVAL;
|
|
490
|
|
491 signal_block_mask &= ~sigmask (nsig);
|
|
492
|
|
493 if (signal_pending_mask & sigmask (nsig))
|
|
494 mswindows_raise (nsig);
|
|
495
|
|
496 return 0;
|
|
497 }
|
|
498
|
|
499 int
|
|
500 mswindows_sigpause (int nsig)
|
|
501 {
|
|
502 /* This is currently not called, because the only call to sigpause
|
|
503 inside XEmacs is with SIGCHLD parameter. Just in case, we put an
|
|
504 assert here, so anyone adds a call to sigpause will be surprised
|
|
505 (or surprise someone else...) */
|
|
506 assert (0);
|
|
507 return 0;
|
|
508 }
|
|
509
|
|
510 int
|
|
511 mswindows_raise (int nsig)
|
|
512 {
|
|
513 /* We delegate some raises to the system routine */
|
|
514 if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
|
|
515 return raise (nsig);
|
|
516
|
|
517 if (nsig < 0 || nsig > SIG_MAX)
|
|
518 return errno = EINVAL;
|
|
519
|
|
520 /* If the signal is blocked, remember to issue later */
|
|
521 if (signal_block_mask & sigmask (nsig))
|
|
522 {
|
|
523 signal_pending_mask |= sigmask (nsig);
|
|
524 return 0;
|
|
525 }
|
|
526
|
|
527 if (signal_handlers[nsig] == SIG_IGN)
|
|
528 return 0;
|
|
529
|
|
530 if (signal_handlers[nsig] != SIG_DFL)
|
|
531 {
|
|
532 (*signal_handlers[nsig]) (nsig);
|
|
533 return 0;
|
|
534 }
|
|
535
|
|
536 /* Default signal actions */
|
|
537 if (nsig == SIGALRM || nsig == SIGPROF)
|
|
538 exit (3);
|
|
539
|
|
540 /* Other signals are ignored by default */
|
|
541 return 0;
|
|
542 }
|
|
543
|
611
|
544
|
|
545 /*--------------------------------------------------------------------*/
|
|
546 /* Async timers */
|
|
547 /*--------------------------------------------------------------------*/
|
|
548
|
|
549 /* We emulate two timers, one for SIGALRM, another for SIGPROF.
|
|
550
|
|
551 itimerproc() function has an implementation limitation: it does
|
|
552 not allow to set *both* interval and period. If an attempt is
|
|
553 made to set both, and then they are unequal, the function
|
|
554 asserts.
|
|
555
|
|
556 Minimum timer resolution on Win32 systems varies, and is greater
|
|
557 than or equal than 1 ms. The resolution is always wrapped not to
|
|
558 attempt to get below the system defined limit.
|
|
559 */
|
|
560
|
|
561 /* Timer precision, denominator of one fraction: for 100 ms
|
|
562 interval, request 10 ms precision
|
|
563 */
|
|
564 const int setitimer_helper_timer_prec = 10;
|
|
565
|
|
566 /* Last itimervals, as set by calls to setitimer */
|
|
567 static struct itimerval it_alarm;
|
|
568 static struct itimerval it_prof;
|
|
569
|
|
570 /* Timer IDs as returned by MM */
|
|
571 MMRESULT tid_alarm = 0;
|
|
572 MMRESULT tid_prof = 0;
|
|
573
|
|
574 static void CALLBACK
|
|
575 setitimer_helper_proc (UINT uID, UINT uMsg, DWORD dwUser,
|
|
576 DWORD dw1, DWORD dw2)
|
|
577 {
|
|
578 /* Just raise the signal indicated by the dwUser parameter */
|
|
579 mswindows_raise (dwUser);
|
|
580 }
|
|
581
|
|
582 /* Divide time in ms specified by IT by DENOM. Return 1 ms
|
|
583 if division results in zero */
|
|
584 static UINT
|
|
585 setitimer_helper_period (const struct itimerval* it, UINT denom)
|
|
586 {
|
|
587 static TIMECAPS time_caps;
|
|
588
|
|
589 UINT res;
|
|
590 const struct timeval* tv =
|
|
591 (it->it_value.tv_sec == 0 && it->it_value.tv_usec == 0)
|
|
592 ? &it->it_interval : &it->it_value;
|
|
593
|
|
594 /* Zero means stop timer */
|
|
595 if (tv->tv_sec == 0 && tv->tv_usec == 0)
|
|
596 return 0;
|
|
597
|
|
598 /* Convert to ms and divide by denom */
|
|
599 res = (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) / denom;
|
|
600
|
|
601 /* Converge to minimum timer resolution */
|
|
602 if (time_caps.wPeriodMin == 0)
|
|
603 timeGetDevCaps (&time_caps, sizeof(time_caps));
|
|
604
|
|
605 if (res < time_caps.wPeriodMin)
|
|
606 res = time_caps.wPeriodMin;
|
|
607
|
|
608 return res;
|
|
609 }
|
|
610
|
|
611 static int
|
|
612 setitimer_helper (const struct itimerval* itnew,
|
|
613 struct itimerval* itold, struct itimerval* itcurrent,
|
|
614 MMRESULT* tid, DWORD sigkind)
|
|
615 {
|
|
616 UINT delay, resolution, event_type;
|
|
617
|
|
618 /* First stop the old timer */
|
|
619 if (*tid)
|
|
620 {
|
|
621 timeKillEvent (*tid);
|
|
622 timeEndPeriod (setitimer_helper_period (itcurrent,
|
|
623 setitimer_helper_timer_prec));
|
|
624 *tid = 0;
|
|
625 }
|
|
626
|
|
627 /* Return old itimerval if requested */
|
|
628 if (itold)
|
|
629 *itold = *itcurrent;
|
|
630
|
|
631 *itcurrent = *itnew;
|
|
632
|
|
633 /* Determine if to start new timer */
|
|
634 delay = setitimer_helper_period (itnew, 1);
|
|
635 if (delay)
|
|
636 {
|
|
637 resolution = setitimer_helper_period (itnew,
|
|
638 setitimer_helper_timer_prec);
|
|
639 event_type = (itnew->it_value.tv_sec == 0 &&
|
|
640 itnew->it_value.tv_usec == 0)
|
|
641 ? TIME_ONESHOT : TIME_PERIODIC;
|
|
642 timeBeginPeriod (resolution);
|
|
643 *tid = timeSetEvent (delay, resolution, setitimer_helper_proc, sigkind,
|
|
644 event_type);
|
|
645 }
|
|
646
|
|
647 return !delay || *tid;
|
|
648 }
|
|
649
|
|
650 int
|
|
651 mswindows_setitimer (int kind, const struct itimerval *itnew,
|
|
652 struct itimerval *itold)
|
|
653 {
|
|
654 /* In this version, both interval and value are allowed
|
|
655 only if they are equal. */
|
|
656 assert ((itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
|
|
657 || (itnew->it_interval.tv_sec == 0 &&
|
|
658 itnew->it_interval.tv_usec == 0)
|
|
659 || (itnew->it_value.tv_sec == itnew->it_interval.tv_sec &&
|
|
660 itnew->it_value.tv_usec == itnew->it_interval.tv_usec));
|
|
661
|
|
662 if (kind == ITIMER_REAL)
|
|
663 return setitimer_helper (itnew, itold, &it_alarm, &tid_alarm, SIGALRM);
|
|
664 else if (kind == ITIMER_PROF)
|
|
665 return setitimer_helper (itnew, itold, &it_prof, &tid_prof, SIGPROF);
|
|
666 else
|
|
667 return errno = EINVAL;
|
|
668 }
|
|
669
|
613
|
670 #endif /* defined (WIN32_NATIVE) || defined (CYGWIN_BROKEN_SIGNALS) */
|
|
671
|
611
|
672
|
442
|
673 void
|
|
674 syms_of_win32 (void)
|
|
675 {
|
|
676 DEFSUBR (Fmswindows_shell_execute);
|
673
|
677 #ifdef CYGWIN
|
|
678 DEFSUBR (Fmswindows_cygwin_to_win32_path);
|
|
679 #endif
|
442
|
680 }
|
|
681
|
|
682 void
|
771
|
683 vars_of_win32 (void)
|
|
684 {
|
|
685 DEFVAR_LISP ("mswindows-downcase-file-names", &Vmswindows_downcase_file_names /*
|
|
686 Non-nil means convert all-upper case file names to lower case.
|
|
687 This applies when performing completions and file name expansion.
|
|
688 */ );
|
|
689 Vmswindows_downcase_file_names = Qnil;
|
|
690 }
|
|
691
|
|
692 void
|
442
|
693 init_win32 (void)
|
|
694 {
|
|
695 init_potentially_nonexistent_functions ();
|
|
696 }
|
771
|
697
|
|
698 void
|
|
699 init_win32_very_early (void)
|
|
700 {
|
|
701 mswindows_windows9x_p = GetVersion () & 0x80000000;
|
|
702 }
|