XEmacs 21.4.5 "Civil Service".
[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   char* doc = NULL;
158   Extbyte* f=0;
159   int ret;
160   struct gcpro gcpro1, gcpro2;
161
162   CHECK_STRING (document);
163
164   if (NILP (current_dir))
165     current_dir = current_buffer->directory;
166
167   GCPRO2 (current_dir, document);
168
169   /* Use mule and cygwin-safe APIs top get at file data. */
170   if (STRINGP (current_dir))
171     {
172       TO_EXTERNAL_FORMAT (LISP_STRING, current_dir,
173                           C_STRING_ALLOCA, f,
174                           Qfile_name);
175 #ifdef CYGWIN
176       CYGWIN_WIN32_PATH (f, path);
177 #else
178       path = f;
179 #endif
180     }
181
182   if (STRINGP (document))
183     {
184       TO_EXTERNAL_FORMAT (LISP_STRING, document,
185                           C_STRING_ALLOCA, f,
186                           Qfile_name);
187 #ifdef CYGWIN
188       CYGWIN_WIN32_PATH (f, doc);
189 #else
190       doc = f;
191 #endif
192     }
193
194   UNGCPRO;
195
196   ret = (int) ShellExecute (NULL,
197                             (STRINGP (operation) ?
198                              XSTRING_DATA (operation) : NULL),
199                             doc, 
200                             (STRINGP (parameters) ?
201                              XSTRING_DATA (parameters) : NULL),
202                             path,
203                             (INTP (show_flag) ?
204                              XINT (show_flag) : SW_SHOWDEFAULT));
205
206   if (ret > 32)
207     return Qt;
208   
209   if (ret == ERROR_FILE_NOT_FOUND)
210     signal_simple_error ("file not found", document);
211   else if (ret == ERROR_PATH_NOT_FOUND)
212     signal_simple_error ("path not found", current_dir);
213   else if (ret == ERROR_BAD_FORMAT)
214     signal_simple_error ("bad executable format", document);
215   else
216     error ("internal error");
217
218   return Qnil;
219 }
220
221 \f
222 /*--------------------------------------------------------------------*/
223 /*                               Async timers                         */
224 /*--------------------------------------------------------------------*/
225
226 /* setitimer() does not exist on native MS Windows, and appears broken
227    on Cygwin (random lockups when BROKEN_SIGIO is defined), so we
228    emulate in both cases by using multimedia timers. */
229
230 /* We emulate two timers, one for SIGALRM, another for SIGPROF.
231
232    itimerproc() function has an implementation limitation: it does
233    not allow to set *both* interval and period. If an attempt is
234    made to set both, and then they are unequal, the function
235    asserts.
236
237    Minimum timer resolution on Win32 systems varies, and is greater
238    than or equal than 1 ms. The resolution is always wrapped not to
239    attempt to get below the system defined limit.
240    */
241
242 /* Timer precision, denominator of one fraction: for 100 ms
243    interval, request 10 ms precision
244    */
245 const int setitimer_helper_timer_prec = 10;
246
247 /* Last itimervals, as set by calls to setitimer */
248 static struct itimerval it_alarm;
249 static struct itimerval it_prof;
250
251 /* Timer IDs as returned by MM */
252 MMRESULT tid_alarm = 0;
253 MMRESULT tid_prof = 0;
254
255 static void CALLBACK
256 setitimer_helper_proc (UINT uID, UINT uMsg, DWORD dwUser,
257                        DWORD dw1, DWORD dw2)
258 {
259   /* Just raise the signal indicated by the dwUser parameter */
260 #ifdef CYGWIN
261   kill (getpid (), dwUser);
262 #else
263   mswindows_raise (dwUser);
264 #endif
265 }
266
267 /* Divide time in ms specified by IT by DENOM. Return 1 ms
268    if division results in zero */
269 static UINT
270 setitimer_helper_period (const struct itimerval* it, UINT denom)
271 {
272   static TIMECAPS time_caps;
273
274   UINT res;
275   const struct timeval* tv = 
276     (it->it_value.tv_sec == 0 && it->it_value.tv_usec == 0)
277     ? &it->it_interval : &it->it_value;
278   
279   /* Zero means stop timer */
280   if (tv->tv_sec == 0 && tv->tv_usec == 0)
281     return 0;
282   
283   /* Convert to ms and divide by denom */
284   res = (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) / denom;
285   
286   /* Converge to minimum timer resolution */
287   if (time_caps.wPeriodMin == 0)
288       timeGetDevCaps (&time_caps, sizeof(time_caps));
289
290   if (res < time_caps.wPeriodMin)
291     res = time_caps.wPeriodMin;
292
293   return res;
294 }
295
296 static int
297 setitimer_helper (const struct itimerval* itnew,
298                   struct itimerval* itold, struct itimerval* itcurrent,
299                   MMRESULT* tid, DWORD sigkind)
300 {
301   UINT delay, resolution, event_type;
302
303   /* First stop the old timer */
304   if (*tid)
305     {
306       timeKillEvent (*tid);
307       timeEndPeriod (setitimer_helper_period (itcurrent,
308                                               setitimer_helper_timer_prec));
309       *tid = 0;
310     }
311
312   /* Return old itimerval if requested */
313   if (itold)
314     *itold = *itcurrent;
315
316   *itcurrent = *itnew;
317
318   /* Determine if to start new timer */
319   delay = setitimer_helper_period (itnew, 1);
320   if (delay)
321     {
322       resolution = setitimer_helper_period (itnew,
323                                             setitimer_helper_timer_prec);
324       event_type = (itnew->it_value.tv_sec == 0 &&
325                     itnew->it_value.tv_usec == 0)
326         ? TIME_ONESHOT : TIME_PERIODIC;
327       timeBeginPeriod (resolution);
328       *tid = timeSetEvent (delay, resolution, setitimer_helper_proc, sigkind,
329                            event_type);
330     }
331
332   return !delay || *tid;
333 }
334  
335 int
336 mswindows_setitimer (int kind, const struct itimerval *itnew,
337                      struct itimerval *itold)
338 {
339   /* In this version, both interval and value are allowed
340      only if they are equal. */
341   assert ((itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
342           || (itnew->it_interval.tv_sec == 0 &&
343               itnew->it_interval.tv_usec == 0)
344           || (itnew->it_value.tv_sec == itnew->it_interval.tv_sec &&
345               itnew->it_value.tv_usec == itnew->it_interval.tv_usec));
346
347   if (kind == ITIMER_REAL)
348     return setitimer_helper (itnew, itold, &it_alarm, &tid_alarm, SIGALRM);
349   else if (kind == ITIMER_PROF)
350     return setitimer_helper (itnew, itold, &it_prof, &tid_prof, SIGPROF);
351   else
352     return errno = EINVAL;
353 }
354
355 \f
356 void
357 syms_of_win32 (void)
358 {
359   DEFSUBR (Fmswindows_shell_execute);
360 }
361
362 void
363 init_win32 (void)
364 {
365   init_potentially_nonexistent_functions ();
366 }