(<DENTISTRY SYMBOL *>): Add missing `general-category'.
[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           // URL-style if we get here, but we must only convert file
186           // arguments, since win32 paths are illegal in http etc.
187           if (strncmp (doc, "file://", 7) == 0)
188             {
189               fname1++;
190               pos = fname1 - doc;
191               if (!(isalpha (fname1[0]) && (IS_DEVICE_SEP (fname1[1]))))
192                 {
193                   sz = cygwin_posix_to_win32_path_list_buf_size (fname1);
194                   fname2 = alloca (sz + pos);
195                   strncpy (fname2, doc, pos);
196                   doc = fname2;
197                   fname2 += pos;
198                   cygwin_posix_to_win32_path_list (fname1, fname2);
199                 }
200             }
201         }
202       else {
203         // Not URL-style, must be a straight filename.
204         LOCAL_TO_WIN32_FILE_FORMAT (document, doc);
205       }
206 #endif
207     }
208
209   UNGCPRO;
210
211   ret = (int) ShellExecute (NULL,
212                             (STRINGP (operation) ?
213                              XSTRING_DATA (operation) : NULL),
214                             doc, 
215                             (STRINGP (parameters) ?
216                              XSTRING_DATA (parameters) : NULL),
217                             path,
218                             (INTP (show_flag) ?
219                              XINT (show_flag) : SW_SHOWDEFAULT));
220
221   if (ret > 32)
222     return Qt;
223   
224   if (ret == ERROR_FILE_NOT_FOUND)
225     signal_simple_error ("file not found", document);
226   else if (ret == ERROR_PATH_NOT_FOUND)
227     signal_simple_error ("path not found", current_dir);
228   else if (ret == ERROR_BAD_FORMAT)
229     signal_simple_error ("bad executable format", document);
230   else
231     error ("internal error");
232
233   return Qnil;
234 }
235
236 #ifdef CYGWIN
237 DEFUN ("mswindows-cygwin-to-win32-path", Fmswindows_cygwin_to_win32_path, 1, 1, 0, /*
238 Get the cygwin environment to convert the Unix PATH to win32 format.
239 No expansion is performed, all conversion is done by the cygwin runtime.
240 */
241        (path))
242 {
243   Extbyte* f;
244   Bufbyte* p;
245   CHECK_STRING (path);
246
247   /* There appears to be a bug in the cygwin conversion routines in
248      that they are not idempotent. */
249   p = XSTRING_DATA (path);
250   if (isalpha (p[0]) && (IS_DEVICE_SEP (p[1])))
251     return path;
252
253   /* Use mule and cygwin-safe APIs top get at file data. */
254   LOCAL_TO_WIN32_FILE_FORMAT (path, f);
255   return build_ext_string (f, Qnative);
256 }
257 #endif
258
259 \f
260 /*--------------------------------------------------------------------*/
261 /*                               Async timers                         */
262 /*--------------------------------------------------------------------*/
263
264 /* setitimer() does not exist on native MS Windows, and appears broken
265    on Cygwin (random lockups when BROKEN_SIGIO is defined), so we
266    emulate in both cases by using multimedia timers. */
267
268 /* We emulate two timers, one for SIGALRM, another for SIGPROF.
269
270    itimerproc() function has an implementation limitation: it does
271    not allow to set *both* interval and period. If an attempt is
272    made to set both, and then they are unequal, the function
273    asserts.
274
275    Minimum timer resolution on Win32 systems varies, and is greater
276    than or equal than 1 ms. The resolution is always wrapped not to
277    attempt to get below the system defined limit.
278    */
279
280 /* Timer precision, denominator of one fraction: for 100 ms
281    interval, request 10 ms precision
282    */
283 const int setitimer_helper_timer_prec = 10;
284
285 /* Last itimervals, as set by calls to setitimer */
286 static struct itimerval it_alarm;
287 static struct itimerval it_prof;
288
289 /* Timer IDs as returned by MM */
290 MMRESULT tid_alarm = 0;
291 MMRESULT tid_prof = 0;
292
293 static void CALLBACK
294 setitimer_helper_proc (UINT uID, UINT uMsg, DWORD dwUser,
295                        DWORD dw1, DWORD dw2)
296 {
297   /* Just raise the signal indicated by the dwUser parameter */
298 #ifdef CYGWIN
299   kill (getpid (), dwUser);
300 #else
301   mswindows_raise (dwUser);
302 #endif
303 }
304
305 /* Divide time in ms specified by IT by DENOM. Return 1 ms
306    if division results in zero */
307 static UINT
308 setitimer_helper_period (const struct itimerval* it, UINT denom)
309 {
310   static TIMECAPS time_caps;
311
312   UINT res;
313   const struct timeval* tv = 
314     (it->it_value.tv_sec == 0 && it->it_value.tv_usec == 0)
315     ? &it->it_interval : &it->it_value;
316   
317   /* Zero means stop timer */
318   if (tv->tv_sec == 0 && tv->tv_usec == 0)
319     return 0;
320   
321   /* Convert to ms and divide by denom */
322   res = (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) / denom;
323   
324   /* Converge to minimum timer resolution */
325   if (time_caps.wPeriodMin == 0)
326       timeGetDevCaps (&time_caps, sizeof(time_caps));
327
328   if (res < time_caps.wPeriodMin)
329     res = time_caps.wPeriodMin;
330
331   return res;
332 }
333
334 static int
335 setitimer_helper (const struct itimerval* itnew,
336                   struct itimerval* itold, struct itimerval* itcurrent,
337                   MMRESULT* tid, DWORD sigkind)
338 {
339   UINT delay, resolution, event_type;
340
341   /* First stop the old timer */
342   if (*tid)
343     {
344       timeKillEvent (*tid);
345       timeEndPeriod (setitimer_helper_period (itcurrent,
346                                               setitimer_helper_timer_prec));
347       *tid = 0;
348     }
349
350   /* Return old itimerval if requested */
351   if (itold)
352     *itold = *itcurrent;
353
354   *itcurrent = *itnew;
355
356   /* Determine if to start new timer */
357   delay = setitimer_helper_period (itnew, 1);
358   if (delay)
359     {
360       resolution = setitimer_helper_period (itnew,
361                                             setitimer_helper_timer_prec);
362       event_type = (itnew->it_value.tv_sec == 0 &&
363                     itnew->it_value.tv_usec == 0)
364         ? TIME_ONESHOT : TIME_PERIODIC;
365       timeBeginPeriod (resolution);
366       *tid = timeSetEvent (delay, resolution, setitimer_helper_proc, sigkind,
367                            event_type);
368     }
369
370   return !delay || *tid;
371 }
372  
373 int
374 mswindows_setitimer (int kind, const struct itimerval *itnew,
375                      struct itimerval *itold)
376 {
377   /* In this version, both interval and value are allowed
378      only if they are equal. */
379   assert ((itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
380           || (itnew->it_interval.tv_sec == 0 &&
381               itnew->it_interval.tv_usec == 0)
382           || (itnew->it_value.tv_sec == itnew->it_interval.tv_sec &&
383               itnew->it_value.tv_usec == itnew->it_interval.tv_usec));
384
385   if (kind == ITIMER_REAL)
386     return setitimer_helper (itnew, itold, &it_alarm, &tid_alarm, SIGALRM);
387   else if (kind == ITIMER_PROF)
388     return setitimer_helper (itnew, itold, &it_prof, &tid_prof, SIGPROF);
389   else
390     return errno = EINVAL;
391 }
392
393 \f
394 void
395 syms_of_win32 (void)
396 {
397   DEFSUBR (Fmswindows_shell_execute);
398 #ifdef CYGWIN
399   DEFSUBR (Fmswindows_cygwin_to_win32_path);
400 #endif
401 }
402
403 void
404 init_win32 (void)
405 {
406   init_potentially_nonexistent_functions ();
407 }