XEmacs 21.2.34 "Molpe".
[chise/xemacs-chise.git.1] / src / console-msw.c
1 /* Console functions for mswindows.
2    Copyright (C) 1996, 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
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.  */
20
21 /* Synched up with: Not in FSF. */
22
23 /* Authorship:
24
25    Ben Wing: January 1996, for 19.14.
26    Rewritten for mswindows by Jonathan Harris, November 1997 for 21.0
27  */
28
29 #include <config.h>
30 #include "lisp.h"
31
32 #include "buffer.h"
33 #include "console-msw.h"
34 #include "events.h"
35 #include "opaque.h"
36
37 DEFINE_CONSOLE_TYPE (mswindows);
38 DEFINE_CONSOLE_TYPE (msprinter);
39
40 Lisp_Object Qabortretryignore;
41 Lisp_Object Qapplmodal;
42 Lisp_Object Qdefault_desktop_only;
43 Lisp_Object Qdefbutton1;
44 Lisp_Object Qdefbutton2;
45 Lisp_Object Qdefbutton3;
46 Lisp_Object Qdefbutton4;
47 /* Lisp_Object Qhelp; */
48 Lisp_Object Qiconasterisk;
49 Lisp_Object Qiconexclamation;
50 Lisp_Object Qiconhand;
51 Lisp_Object Qiconinformation;
52 Lisp_Object Qiconquestion;
53 Lisp_Object Qiconstop;
54 /* Lisp_Object Qok; */
55 Lisp_Object Qokcancel;
56 Lisp_Object Qretrycancel;
57 /* Lisp_Object Qright; */
58 Lisp_Object Qrtlreading;
59 Lisp_Object Qservice_notification;
60 Lisp_Object Qsetforeground;
61 Lisp_Object Qsystemmodal;
62 Lisp_Object Qtaskmodal;
63 Lisp_Object Qtopmost;
64 Lisp_Object Qyesno;
65 Lisp_Object Qyesnocancel;
66
67 /* Lisp_Object Qabort; */
68 /* Lisp_Object Qcancel; */
69 /* Lisp_Object Qignore; */
70 /* Lisp_Object Qno; */
71 /* Lisp_Object Qok; */
72 /* Lisp_Object Qretry; */
73 /* Lisp_Object Qyes; */
74
75
76 /************************************************************************/
77 /*                       mswindows console methods                      */
78 /************************************************************************/
79
80 static int
81 mswindows_initially_selected_for_input (struct console *con)
82 {
83   return 1;
84 }
85
86 static HWND msw_console_hwnd = 0;
87
88 #define KLUDGE_BUFSIZE 1024 /* buffer size for console window titles */
89
90 /* Direct from the horse's mouth: Microsoft KB article Q124103 */
91 static HWND
92 GetConsoleHwnd (void)
93
94   HWND hwndFound;         /* this is what is returned to the caller */
95   char pszNewWindowTitle[KLUDGE_BUFSIZE]; /* contains fabricated WindowTitle */
96   char pszOldWindowTitle[KLUDGE_BUFSIZE]; /* contains original WindowTitle */
97
98   /* fetch current window title */
99
100   GetConsoleTitle(pszOldWindowTitle, KLUDGE_BUFSIZE);
101
102   /* format a "unique" NewWindowTitle */
103
104   wsprintf(pszNewWindowTitle,"%d/%d",
105            GetTickCount(),
106            GetCurrentProcessId());
107
108   /* change current window title */
109
110   SetConsoleTitle(pszNewWindowTitle);
111
112   /* ensure window title has been updated */
113
114   Sleep(40);
115
116   /* look for NewWindowTitle */
117
118   hwndFound=FindWindow(NULL, pszNewWindowTitle);
119
120   /* restore original window title */
121
122   SetConsoleTitle(pszOldWindowTitle);
123
124   return(hwndFound);
125
126
127 HWND
128 msw_get_console_hwnd (void)
129 {
130   if (!msw_console_hwnd)
131     msw_console_hwnd = GetConsoleHwnd ();
132   return msw_console_hwnd;
133 }
134
135 int
136 msw_ensure_console_allocated (void)
137 {
138   HWND fgwin = GetForegroundWindow ();
139   /* stupid mswin api won't let you create the console window
140      hidden!  creating it changes the focus!  fuck me! */
141   if (AllocConsole ())
142     {
143       SetForegroundWindow (fgwin);
144       return 1;
145     }
146   return 0;
147 }
148
149 static Lisp_Object
150 mswindows_canonicalize_console_connection (Lisp_Object connection,
151                                            Error_behavior errb)
152 {
153   /* Do not allow more than one mswindows device, by explicitly
154      requiring that CONNECTION is nil, the only allowed connection in
155      Windows. */
156   if (!NILP (connection))
157     {
158       if (ERRB_EQ (errb, ERROR_ME))
159         signal_simple_error
160           ("Invalid (non-nil) connection for mswindows device/console",
161            connection);
162       else
163         return Qunbound;
164     }
165
166   return Qnil;
167 }
168
169 static Lisp_Object
170 mswindows_canonicalize_device_connection (Lisp_Object connection,
171                                           Error_behavior errb)
172 {
173   return mswindows_canonicalize_console_connection (connection, errb);
174 }
175
176 void
177 msw_hide_console (void)
178 {
179   ShowWindow (msw_get_console_hwnd (), SW_HIDE);
180 }
181
182 void
183 msw_show_console (void)
184 {
185   HWND hwnd = msw_get_console_hwnd ();
186   ShowWindow (hwnd, SW_SHOWNA);
187
188   /* I tried to raise the window to the top without activating
189      it, but this fails.  Apparently Windows just doesn't like
190      having the active window not be on top.  So instead, we
191      at least put it just below our own window, where part of it
192      will likely be seen. */
193   SetWindowPos (hwnd, GetForegroundWindow (), 0, 0, 0, 0,
194                 SWP_NOSIZE | SWP_NOMOVE | SWP_NOSENDCHANGING |
195                 SWP_NOACTIVATE);
196 }
197
198 static int msw_console_buffered = 0;
199 HANDLE msw_console_buffer;
200
201 static void
202 msw_ensure_console_buffered (void)
203 {
204   if (!msw_console_buffered)
205     {
206       COORD new_size;
207
208       new_size.X = 80;
209       new_size.Y = 1000;
210       msw_ensure_console_allocated ();
211       msw_console_buffer =
212         CreateConsoleScreenBuffer (GENERIC_WRITE, 0, NULL,
213                                    CONSOLE_TEXTMODE_BUFFER, NULL);
214       SetConsoleScreenBufferSize (msw_console_buffer, new_size);
215       SetConsoleActiveScreenBuffer (msw_console_buffer);
216       msw_console_buffered = 1;
217     }
218 }
219
220 int
221 msw_output_console_string (CONST Extbyte *str, Extcount len)
222 {
223   DWORD num_written;
224
225   msw_ensure_console_buffered ();
226   msw_show_console ();
227   return WriteConsole (msw_console_buffer, str, len, &num_written, NULL);
228 }
229
230 /* Determine if running on Windows 9x and not NT */
231 int
232 msw_windows9x_p (void)
233 {
234   return GetVersion () & 0x80000000;
235 }
236
237
238 #ifdef DEBUG_XEMACS
239
240 /*
241  * Random helper functions for debugging.
242  * Intended for use in the MSVC "Watch" window which doesn't like
243  * the aborts that the error_check_foo() functions can make.
244  */
245 struct lrecord_header *
246 DHEADER (Lisp_Object obj)
247 {
248   return LRECORDP (obj) ? XRECORD_LHEADER (obj) : NULL;
249 }
250
251 void *
252 DOPAQUE_DATA (Lisp_Object obj)
253 {
254   return OPAQUEP (obj) ? OPAQUE_DATA (XOPAQUE (obj)) : NULL;
255 }
256
257 Lisp_Event *
258 DEVENT (Lisp_Object obj)
259 {
260   return EVENTP (obj) ? XEVENT (obj) : NULL;
261 }
262
263 Lisp_Cons *
264 DCONS (Lisp_Object obj)
265 {
266   return CONSP (obj) ? XCONS (obj) : NULL;
267 }
268
269 Lisp_Cons *
270 DCONSCDR (Lisp_Object obj)
271 {
272   return (CONSP (obj) && CONSP (XCDR (obj))) ? XCONS (XCDR (obj)) : 0;
273 }
274
275 Bufbyte *
276 DSTRING (Lisp_Object obj)
277 {
278   return STRINGP (obj) ? XSTRING_DATA (obj) : NULL;
279 }
280
281 Lisp_Vector *
282 DVECTOR (Lisp_Object obj)
283 {
284   return VECTORP (obj) ? XVECTOR (obj) : NULL;
285 }
286
287 Lisp_Symbol *
288 DSYMBOL (Lisp_Object obj)
289 {
290   return SYMBOLP (obj) ? XSYMBOL (obj) : NULL;
291 }
292
293 Bufbyte *
294 DSYMNAME (Lisp_Object obj)
295 {
296   return SYMBOLP (obj) ? string_data (XSYMBOL (obj)->name) : NULL;
297 }
298
299 #endif /* DEBUG_XEMACS */
300
301 DEFUN ("mswindows-message-box", Fmswindows_message_box, 1, 3, 0, /*
302 Pop up an MS Windows message box.
303 MESSAGE is the string to display.  Optional argument FLAG controls
304 what appears in the box and how it behaves; it is a symbol or list of
305 symbols, described below.  Second optional argument TITLE controls the
306 title bar; if omitted, a standard title bar will be used, probably
307 displaying "XEmacs".
308
309 Possible flags are
310
311
312 -- To specify the buttons in the message box:
313
314 abortretryignore 
315   The message box contains three push buttons: Abort, Retry, and Ignore. 
316 ok 
317   The message box contains one push button: OK. This is the default. 
318 okcancel 
319   The message box contains two push buttons: OK and Cancel. 
320 retrycancel 
321   The message box contains two push buttons: Retry and Cancel. 
322 yesno 
323   The message box contains two push buttons: Yes and No. 
324 yesnocancel 
325   The message box contains three push buttons: Yes, No, and Cancel. 
326
327
328 -- To display an icon in the message box:
329  
330 iconexclamation, iconwarning
331   An exclamation-point icon appears in the message box. 
332 iconinformation, iconasterisk
333   An icon consisting of a lowercase letter i in a circle appears in
334   the message box. 
335 iconquestion
336   A question-mark icon appears in the message box. 
337 iconstop, iconerror, iconhand
338   A stop-sign icon appears in the message box. 
339
340
341 -- To indicate the default button: 
342
343 defbutton1
344   The first button is the default button.  This is the default.
345 defbutton2
346   The second button is the default button. 
347 defbutton3
348   The third button is the default button. 
349 defbutton4
350   The fourth button is the default button. 
351
352
353 -- To indicate the modality of the dialog box:
354  
355 applmodal
356   The user must respond to the message box before continuing work in
357   the window identified by the hWnd parameter. However, the user can
358   move to the windows of other applications and work in those windows.
359   Depending on the hierarchy of windows in the application, the user
360   may be able to move to other windows within the application. All
361   child windows of the parent of the message box are automatically
362   disabled, but popup windows are not.  This is the default.
363 systemmodal
364   Same as applmodal except that the message box has the WS_EX_TOPMOST
365   style. Use system-modal message boxes to notify the user of serious,
366   potentially damaging errors that require immediate attention (for
367   example, running out of memory). This flag has no effect on the
368   user's ability to interact with windows other than those associated
369   with hWnd.
370 taskmodal
371   Same as applmodal except that all the top-level windows belonging to
372   the current task are disabled if the hWnd parameter is NULL. Use
373   this flag when the calling application or library does not have a
374   window handle available but still needs to prevent input to other
375   windows in the current application without suspending other
376   applications.
377
378
379 In addition, you can specify the following flags: 
380
381 default-desktop-only 
382   The desktop currently receiving input must be a default desktop;
383   otherwise, the function fails. A default desktop is one an
384   application runs on after the user has logged on.
385 help 
386   Adds a Help button to the message box. Choosing the Help button or
387   pressing F1 generates a Help event.
388 right 
389   The text is right-justified. 
390 rtlreading 
391   Displays message and caption text using right-to-left reading order
392   on Hebrew and Arabic systems.
393 setforeground 
394   The message box becomes the foreground window. Internally, Windows
395   calls the SetForegroundWindow function for the message box.
396 topmost 
397   The message box is created with the WS_EX_TOPMOST window style. 
398 service-notification 
399   Windows NT only: The caller is a service notifying the user of an
400   event. The function displays a message box on the current active
401   desktop, even if there is no user logged on to the computer.  If
402   this flag is set, the hWnd parameter must be NULL. This is so the
403   message box can appear on a desktop other than the desktop
404   corresponding to the hWnd.
405
406
407
408 The return value is one of the following menu-item values returned by
409 the dialog box:
410  
411 abort
412   Abort button was selected. 
413 cancel
414   Cancel button was selected. 
415 ignore
416   Ignore button was selected. 
417 no
418   No button was selected. 
419 ok
420   OK button was selected. 
421 retry
422   Retry button was selected. 
423 yes
424   Yes button was selected. 
425
426 If a message box has a Cancel button, the function returns the
427 `cancel' value if either the ESC key is pressed or the Cancel button
428 is selected.  If the message box has no Cancel button, pressing ESC has
429 no effect.  */
430        (message_, flags, title))
431 {
432   Lisp_Object tail;
433   Extbyte *msgout;
434   Extbyte *titleout = 0;
435   UINT sty = 0;
436
437   if (noninteractive)
438     return Qcancel;
439
440   if (!CONSP (flags))
441     {
442       CHECK_SYMBOL (flags);
443       flags = list1 (flags);
444     }
445
446   CHECK_STRING (message_);
447   TO_EXTERNAL_FORMAT (LISP_STRING, message_,
448                       C_STRING_ALLOCA, msgout,
449                       Qmswindows_tstr);
450   
451   if (!NILP (title))
452     {
453       CHECK_STRING (title);
454       TO_EXTERNAL_FORMAT (LISP_STRING, title,
455                           C_STRING_ALLOCA, titleout,
456                           Qmswindows_tstr);
457     }
458
459   EXTERNAL_LIST_LOOP (tail, flags)
460     {
461       Lisp_Object st = XCAR (tail);
462       CHECK_SYMBOL (st);
463       if (0)
464         ;
465 #define FROB(sym, val) else if (EQ (st, sym)) sty |= val
466       FROB (Qabortretryignore, MB_ABORTRETRYIGNORE);
467       FROB (Qapplmodal, MB_APPLMODAL);
468       FROB (Qdefault_desktop_only, MB_DEFAULT_DESKTOP_ONLY);
469       FROB (Qdefbutton1, MB_DEFBUTTON1);
470       FROB (Qdefbutton2, MB_DEFBUTTON2);
471       FROB (Qdefbutton3, MB_DEFBUTTON3);
472       FROB (Qdefbutton4, MB_DEFBUTTON4);
473       FROB (Qhelp, MB_HELP);
474       FROB (Qiconasterisk, MB_ICONASTERISK);
475       FROB (Qiconexclamation, MB_ICONEXCLAMATION);
476       FROB (Qiconhand, MB_ICONHAND);
477       FROB (Qiconinformation, MB_ICONINFORMATION);
478       FROB (Qiconquestion, MB_ICONQUESTION);
479       FROB (Qiconstop, MB_ICONSTOP);
480       FROB (Qok, MB_OK);
481       FROB (Qokcancel, MB_OKCANCEL);
482       FROB (Qretrycancel, MB_RETRYCANCEL);
483       FROB (Qright, MB_RIGHT);
484       FROB (Qrtlreading, MB_RTLREADING);
485       FROB (Qservice_notification, MB_SERVICE_NOTIFICATION);
486       FROB (Qsetforeground, MB_SETFOREGROUND);
487       FROB (Qsystemmodal, MB_SYSTEMMODAL);
488       FROB (Qtaskmodal, MB_TASKMODAL);
489       FROB (Qtopmost, MB_TOPMOST);
490       FROB (Qyesno, MB_YESNO);
491       FROB (Qyesnocancel, MB_YESNOCANCEL);
492 #undef FROB
493
494       else
495         signal_simple_error ("Unrecognized flag", st);
496     }
497
498   {
499     int retval = MessageBox (NULL, msgout, titleout, sty);
500
501     if (retval == 0)
502       error ("Out of memory when calling `mswindows-message-box'");
503
504 #define FROB(sym, val) if (retval == val) return sym
505     FROB (Qabort, IDABORT);
506     FROB (Qcancel, IDCANCEL);
507     FROB (Qignore, IDIGNORE);
508     FROB (Qno, IDNO);
509     FROB (Qok, IDOK);
510     FROB (Qretry, IDRETRY);
511     FROB (Qyes, IDYES);
512 #undef FROB
513     
514     signal_simple_error ("Unknown return value from MessageBox()",
515                          make_int (retval));
516   }
517
518   return Qnil;
519 }
520
521 void
522 mswindows_output_last_error (char *frob)
523 {
524   LPVOID lpMsgBuf;
525   int errval = GetLastError();
526   
527   FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER
528                  | FORMAT_MESSAGE_FROM_SYSTEM,
529                  NULL, errval,
530                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
531                  (LPTSTR) &lpMsgBuf,
532                  0,
533                  NULL);
534   stderr_out ("last error during %s is %d: %s\n",
535               frob, errval, (char*)lpMsgBuf);
536 }
537
538 \f
539 /************************************************************************/
540 /*                            initialization                            */
541 /************************************************************************/
542
543 void
544 syms_of_console_mswindows (void)
545 {
546   defsymbol (&Qabortretryignore, "abortretryignore");
547   defsymbol (&Qapplmodal, "applmodal");
548   defsymbol (&Qdefault_desktop_only, "default-desktop-only");
549   defsymbol (&Qdefbutton1, "defbutton1");
550   defsymbol (&Qdefbutton2, "defbutton2");
551   defsymbol (&Qdefbutton3, "defbutton3");
552   defsymbol (&Qdefbutton4, "defbutton4");
553   /* defsymbol (&Qhelp, "help"); */
554   defsymbol (&Qiconasterisk, "iconasterisk");
555   defsymbol (&Qiconexclamation, "iconexclamation");
556   defsymbol (&Qiconhand, "iconhand");
557   defsymbol (&Qiconinformation, "iconinformation");
558   defsymbol (&Qiconquestion, "iconquestion");
559   defsymbol (&Qiconstop, "iconstop");
560   /* defsymbol (&Qok, "ok"); */
561   defsymbol (&Qokcancel, "okcancel");
562   defsymbol (&Qretrycancel, "retrycancel");
563   /* defsymbol (&Qright, "right"); */
564   defsymbol (&Qrtlreading, "rtlreading");
565   defsymbol (&Qservice_notification, "service-notification");
566   defsymbol (&Qsetforeground, "setforeground");
567   defsymbol (&Qsystemmodal, "systemmodal");
568   defsymbol (&Qtaskmodal, "taskmodal");
569   defsymbol (&Qtopmost, "topmost");
570   defsymbol (&Qyesno, "yesno");
571   defsymbol (&Qyesnocancel, "yesnocancel");
572
573   /* defsymbol (&Qabort, "abort"); */
574   /* defsymbol (&Qcancel, "cancel"); */
575   /* defsymbol (&Qignore, "ignore"); */
576   /* defsymbol (&Qno, "no"); */
577   /* defsymbol (&Qok, "ok"); */
578   /* defsymbol (&Qretry, "retry"); */
579   /* defsymbol (&Qyes, "yes"); */
580
581   DEFSUBR (Fmswindows_message_box);
582 }
583
584 void
585 console_type_create_mswindows (void)
586 {
587   INITIALIZE_CONSOLE_TYPE (mswindows, "mswindows", "console-mswindows-p");
588
589   /* console methods */
590 /*  CONSOLE_HAS_METHOD (mswindows, init_console); */
591 /*  CONSOLE_HAS_METHOD (mswindows, mark_console); */
592   CONSOLE_HAS_METHOD (mswindows, initially_selected_for_input);
593 /*  CONSOLE_HAS_METHOD (mswindows, delete_console); */
594   CONSOLE_HAS_METHOD (mswindows, canonicalize_console_connection);
595   CONSOLE_HAS_METHOD (mswindows, canonicalize_device_connection);
596 /*  CONSOLE_HAS_METHOD (mswindows, semi_canonicalize_console_connection); */
597 /*  CONSOLE_HAS_METHOD (mswindows, semi_canonicalize_device_connection); */
598
599   INITIALIZE_CONSOLE_TYPE (msprinter, "msprinter", "console-msprinter-p");
600 }
601
602 void
603 reinit_console_type_create_mswindows (void)
604 {
605   REINITIALIZE_CONSOLE_TYPE (mswindows);
606   REINITIALIZE_CONSOLE_TYPE (msprinter);
607 }
608
609 void
610 vars_of_console_mswindows (void)
611 {
612   Fprovide (Qmswindows);
613 }