XEmacs 21.4.6 "Common Lisp".
[chise/xemacs-chise.git.1] / src / win32.c
1 /* Utility routines for XEmacs on Windows 9x, NT and Cygwin.
2    Copyright (C) 2000 Ben Wing.
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"
25
26 #include "syssignal.h"
27 #include "systime.h"
28 #include "syswindows.h"
29
30 typedef BOOL (WINAPI *pfSwitchToThread_t) (VOID);
31 pfSwitchToThread_t xSwitchToThread;
32
33 typedef HKL (WINAPI *pfGetKeyboardLayout_t) (DWORD);
34 pfGetKeyboardLayout_t xGetKeyboardLayout;
35 typedef BOOL (WINAPI *pfSetMenuDefaultItem_t) (HMENU, UINT, UINT);
36 pfSetMenuDefaultItem_t xSetMenuDefaultItem;
37 typedef BOOL (WINAPI *pfInsertMenuItemA_t) 
38      (HMENU, UINT, BOOL, LPCMENUITEMINFOA);
39 pfInsertMenuItemA_t xInsertMenuItemA;
40 typedef BOOL (WINAPI *pfInsertMenuItemW_t) 
41      (HMENU, UINT, BOOL, LPCMENUITEMINFOW);
42 pfInsertMenuItemW_t xInsertMenuItemW;
43 typedef HANDLE (WINAPI *pfLoadImageA_t) 
44      (HINSTANCE, LPCSTR, UINT, int, int, UINT);
45 pfLoadImageA_t xLoadImageA;
46 typedef HANDLE (WINAPI *pfLoadImageW_t)
47      (HINSTANCE, LPCWSTR, UINT, int, int, UINT);
48 pfLoadImageW_t xLoadImageW;
49 typedef ATOM (WINAPI *pfRegisterClassExA_t) (CONST WNDCLASSEXA *);
50 pfRegisterClassExA_t xRegisterClassExA;
51 typedef ATOM (WINAPI *pfRegisterClassExW_t) (CONST WNDCLASSEXW *);
52 pfRegisterClassExW_t xRegisterClassExW;
53
54 typedef int (WINAPI *pfEnumFontFamiliesExA_t) 
55      (HDC, LPLOGFONTA, FONTENUMPROCA, LPARAM, DWORD);
56 pfEnumFontFamiliesExA_t xEnumFontFamiliesExA;
57 typedef int (WINAPI *pfEnumFontFamiliesExW_t) 
58      (HDC, LPLOGFONTW, FONTENUMPROCW, LPARAM, DWORD);
59 pfEnumFontFamiliesExW_t xEnumFontFamiliesExW;
60
61 typedef DWORD (WINAPI *pfSHGetFileInfoA_t) 
62      (LPCSTR, DWORD, SHFILEINFOA FAR *, UINT, UINT);
63 pfSHGetFileInfoA_t xSHGetFileInfoA;
64 typedef DWORD (WINAPI *pfSHGetFileInfoW_t) 
65      (LPCWSTR, DWORD, SHFILEINFOW FAR *, UINT, UINT);
66 pfSHGetFileInfoW_t xSHGetFileInfoW;
67
68 Lisp_Object
69 tstr_to_local_file_format (Extbyte *pathout)
70 {
71   Bufbyte *ttlff;
72   Lisp_Object in;
73
74   EXTERNAL_TO_C_STRING (pathout, ttlff, Qmswindows_tstr);
75   WIN32_TO_LOCAL_FILE_FORMAT (ttlff, in);
76
77   return in;
78 }
79
80 static void
81 init_potentially_nonexistent_functions (void)
82 {
83   HMODULE h_kernel = GetModuleHandle ("kernel32");
84   HMODULE h_user = GetModuleHandle ("user32");
85   HMODULE h_gdi = GetModuleHandle ("gdi32");
86   HMODULE h_shell = GetModuleHandle ("shell32");
87
88   if (h_kernel)
89     {
90       xSwitchToThread =
91         (pfSwitchToThread_t) GetProcAddress (h_kernel, "SwitchToThread");
92     }
93
94   if (h_user)
95     {
96       xGetKeyboardLayout =
97         (pfGetKeyboardLayout_t) GetProcAddress (h_user, "GetKeyboardLayout");
98       xSetMenuDefaultItem =
99         (pfSetMenuDefaultItem_t) GetProcAddress (h_user, "SetMenuDefaultItem");
100       xInsertMenuItemA =
101         (pfInsertMenuItemA_t) GetProcAddress (h_user, "InsertMenuItemA");
102       xInsertMenuItemW =
103         (pfInsertMenuItemW_t) GetProcAddress (h_user, "InsertMenuItemW");
104       xLoadImageA =
105         (pfLoadImageA_t) GetProcAddress (h_user, "LoadImageA");
106       xLoadImageW =
107         (pfLoadImageW_t) GetProcAddress (h_user, "LoadImageW");
108       xRegisterClassExA =
109         (pfRegisterClassExA_t) GetProcAddress (h_user, "RegisterClassExA");
110       xRegisterClassExW =
111         (pfRegisterClassExW_t) GetProcAddress (h_user, "RegisterClassExW");
112     }
113
114   if (h_gdi)
115     {
116       xEnumFontFamiliesExA =
117         (pfEnumFontFamiliesExA_t) GetProcAddress (h_gdi, "EnumFontFamiliesExA");
118       xEnumFontFamiliesExW =
119         (pfEnumFontFamiliesExW_t) GetProcAddress (h_gdi, "EnumFontFamiliesExW");
120     }
121
122   if (h_shell)
123     {
124       xSHGetFileInfoA =
125         (pfSHGetFileInfoA_t) GetProcAddress (h_shell, "SHGetFileInfoA");
126       xSHGetFileInfoW =
127         (pfSHGetFileInfoW_t) GetProcAddress (h_shell, "SHGetFileInfoW");
128     }
129 }
130
131 DEFUN ("mswindows-shell-execute", Fmswindows_shell_execute, 2, 4, 0, /*
132 Get Windows to perform OPERATION on DOCUMENT.
133 This is a wrapper around the ShellExecute system function, which
134 invokes the application registered to handle OPERATION for DOCUMENT.
135 OPERATION is typically \"open\", \"print\" or \"explore\" (but can be
136 nil for the default action), and DOCUMENT is typically the name of a
137 document file or URL, but can also be a program executable to run or
138 a directory to open in the Windows Explorer.
139
140 If DOCUMENT is a program executable, PARAMETERS can be a string
141 containing command line parameters, but otherwise should be nil.
142
143 SHOW-FLAG can be used to control whether the invoked application is hidden
144 or minimized.  If SHOW-FLAG is nil, the application is displayed normally,
145 otherwise it is an integer representing a ShowWindow flag:
146
147   0 - start hidden
148   1 - start normally
149   3 - start maximized
150   6 - start minimized
151 */
152        (operation, document, parameters, show_flag))
153 {
154   /* Encode filename and current directory.  */
155   Lisp_Object current_dir = Ffile_name_directory (document);
156   char* path = NULL;
157 #ifdef CYGWIN
158   char* fname1, *fname2;
159   int pos, sz;
160 #endif
161   char* doc = NULL;
162   int ret;
163   struct gcpro gcpro1, gcpro2;
164
165   CHECK_STRING (document);
166
167   if (NILP (current_dir))
168     current_dir = current_buffer->directory;
169
170   GCPRO2 (current_dir, document);
171
172   /* Use mule and cygwin-safe APIs top get at file data. */
173   if (STRINGP (current_dir))
174     {
175       LOCAL_TO_WIN32_FILE_FORMAT (current_dir, path);
176     }
177
178   if (STRINGP (document))
179     {
180       doc = XSTRING_DATA (document);
181 #ifdef CYGWIN
182       if ((fname1 = strchr (doc, ':')) != NULL 
183           && *++fname1 == '/' && *++fname1 == '/')
184         {
185           fname1++;
186           pos = fname1 - doc;
187           if (!(isalpha (fname1[0]) && (IS_DEVICE_SEP (fname1[1]))))
188             {
189               sz = cygwin_posix_to_win32_path_list_buf_size (fname1);
190               fname2 = alloca (sz + pos);
191               strncpy (fname2, doc, pos);
192               doc = fname2;
193               fname2 += pos;
194               cygwin_posix_to_win32_path_list (fname1, fname2);
195             }
196         }
197       else {
198         LOCAL_TO_WIN32_FILE_FORMAT (document, doc);
199       }
200 #endif
201     }
202
203   UNGCPRO;
204
205   ret = (int) ShellExecute (NULL,
206                             (STRINGP (operation) ?
207                              XSTRING_DATA (operation) : NULL),
208                             doc, 
209                             (STRINGP (parameters) ?
210                              XSTRING_DATA (parameters) : NULL),
211                             path,
212                             (INTP (show_flag) ?
213                              XINT (show_flag) : SW_SHOWDEFAULT));
214
215   if (ret > 32)
216     return Qt;
217   
218   if (ret == ERROR_FILE_NOT_FOUND)
219     signal_simple_error ("file not found", document);
220   else if (ret == ERROR_PATH_NOT_FOUND)
221     signal_simple_error ("path not found", current_dir);
222   else if (ret == ERROR_BAD_FORMAT)
223     signal_simple_error ("bad executable format", document);
224   else
225     error ("internal error");
226
227   return Qnil;
228 }
229
230 #ifdef CYGWIN
231 DEFUN ("mswindows-cygwin-to-win32-path", Fmswindows_cygwin_to_win32_path, 1, 1, 0, /*
232 Get the cygwin environment to convert the Unix PATH to win32 format.
233 No expansion is performed, all conversion is done by the cygwin runtime.
234 */
235        (path))
236 {
237   Extbyte* f;
238   Bufbyte* p;
239   CHECK_STRING (path);
240
241   /* There appears to be a bug in the cygwin conversion routines in
242      that they are not idempotent. */
243   p = XSTRING_DATA (path);
244   if (isalpha (p[0]) && (IS_DEVICE_SEP (p[1])))
245     return path;
246
247   /* Use mule and cygwin-safe APIs top get at file data. */
248   LOCAL_TO_WIN32_FILE_FORMAT (path, f);
249   return build_ext_string (f, Qnative);
250 }
251 #endif
252
253 \f
254 /*--------------------------------------------------------------------*/
255 /*                               Async timers                         */
256 /*--------------------------------------------------------------------*/
257
258 /* setitimer() does not exist on native MS Windows, and appears broken
259    on Cygwin (random lockups when BROKEN_SIGIO is defined), so we
260    emulate in both cases by using multimedia timers. */
261
262 /* We emulate two timers, one for SIGALRM, another for SIGPROF.
263
264    itimerproc() function has an implementation limitation: it does
265    not allow to set *both* interval and period. If an attempt is
266    made to set both, and then they are unequal, the function
267    asserts.
268
269    Minimum timer resolution on Win32 systems varies, and is greater
270    than or equal than 1 ms. The resolution is always wrapped not to
271    attempt to get below the system defined limit.
272    */
273
274 /* Timer precision, denominator of one fraction: for 100 ms
275    interval, request 10 ms precision
276    */
277 const int setitimer_helper_timer_prec = 10;
278
279 /* Last itimervals, as set by calls to setitimer */
280 static struct itimerval it_alarm;
281 static struct itimerval it_prof;
282
283 /* Timer IDs as returned by MM */
284 MMRESULT tid_alarm = 0;
285 MMRESULT tid_prof = 0;
286
287 static void CALLBACK
288 setitimer_helper_proc (UINT uID, UINT uMsg, DWORD dwUser,
289                        DWORD dw1, DWORD dw2)
290 {
291   /* Just raise the signal indicated by the dwUser parameter */
292 #ifdef CYGWIN
293   kill (getpid (), dwUser);
294 #else
295   mswindows_raise (dwUser);
296 #endif
297 }
298
299 /* Divide time in ms specified by IT by DENOM. Return 1 ms
300    if division results in zero */
301 static UINT
302 setitimer_helper_period (const struct itimerval* it, UINT denom)
303 {
304   static TIMECAPS time_caps;
305
306   UINT res;
307   const struct timeval* tv = 
308     (it->it_value.tv_sec == 0 && it->it_value.tv_usec == 0)
309     ? &it->it_interval : &it->it_value;
310   
311   /* Zero means stop timer */
312   if (tv->tv_sec == 0 && tv->tv_usec == 0)
313     return 0;
314   
315   /* Convert to ms and divide by denom */
316   res = (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) / denom;
317   
318   /* Converge to minimum timer resolution */
319   if (time_caps.wPeriodMin == 0)
320       timeGetDevCaps (&time_caps, sizeof(time_caps));
321
322   if (res < time_caps.wPeriodMin)
323     res = time_caps.wPeriodMin;
324
325   return res;
326 }
327
328 static int
329 setitimer_helper (const struct itimerval* itnew,
330                   struct itimerval* itold, struct itimerval* itcurrent,
331                   MMRESULT* tid, DWORD sigkind)
332 {
333   UINT delay, resolution, event_type;
334
335   /* First stop the old timer */
336   if (*tid)
337     {
338       timeKillEvent (*tid);
339       timeEndPeriod (setitimer_helper_period (itcurrent,
340                                               setitimer_helper_timer_prec));
341       *tid = 0;
342     }
343
344   /* Return old itimerval if requested */
345   if (itold)
346     *itold = *itcurrent;
347
348   *itcurrent = *itnew;
349
350   /* Determine if to start new timer */
351   delay = setitimer_helper_period (itnew, 1);
352   if (delay)
353     {
354       resolution = setitimer_helper_period (itnew,
355                                             setitimer_helper_timer_prec);
356       event_type = (itnew->it_value.tv_sec == 0 &&
357                     itnew->it_value.tv_usec == 0)
358         ? TIME_ONESHOT : TIME_PERIODIC;
359       timeBeginPeriod (resolution);
360       *tid = timeSetEvent (delay, resolution, setitimer_helper_proc, sigkind,
361                            event_type);
362     }
363
364   return !delay || *tid;
365 }
366  
367 int
368 mswindows_setitimer (int kind, const struct itimerval *itnew,
369                      struct itimerval *itold)
370 {
371   /* In this version, both interval and value are allowed
372      only if they are equal. */
373   assert ((itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
374           || (itnew->it_interval.tv_sec == 0 &&
375               itnew->it_interval.tv_usec == 0)
376           || (itnew->it_value.tv_sec == itnew->it_interval.tv_sec &&
377               itnew->it_value.tv_usec == itnew->it_interval.tv_usec));
378
379   if (kind == ITIMER_REAL)
380     return setitimer_helper (itnew, itold, &it_alarm, &tid_alarm, SIGALRM);
381   else if (kind == ITIMER_PROF)
382     return setitimer_helper (itnew, itold, &it_prof, &tid_prof, SIGPROF);
383   else
384     return errno = EINVAL;
385 }
386
387 \f
388 void
389 syms_of_win32 (void)
390 {
391   DEFSUBR (Fmswindows_shell_execute);
392 #ifdef CYGWIN
393   DEFSUBR (Fmswindows_cygwin_to_win32_path);
394 #endif
395 }
396
397 void
398 init_win32 (void)
399 {
400   init_potentially_nonexistent_functions ();
401 }