1 /* The routines in this file provide support for various OS-related
2    functions under the Microsoft Windows environment on an IBM-PC or
3    compatible computer.
4 
5    Must be compiled with Borland C++ 2.0 or MSC 6.0 or later versions.
6 
7    It should not be compiled if the WINDOW_MSWIN symbol is not set */
8 
9 #define	    OEMRESOURCE	1   /* to have access to OBM_CLOSE from windows.h */
10 #include    "estruct.h"
11 #include    "elang.h"
12 #include    <stdio.h>
13 #include    <time.h>
14 #include    "eproto.h"
15 #include    "edef.h"
16 
17 #include    "mswin.h"
18 
19 #if WINDOW_MSWIN32
20 #include <setjmp.h>
21 #endif
22 
23 #define MAXPARAM    10      /* max command line parameters */
24 #define TXTSIZ      NFILEN  /* all purpose string length */
25 #define T_SLEEP      1      /* Sleep timer ID */
26 
27 /* message codes for EmacsBroadcastMsg */
28 #define EMACS_PROCESS   1
29 #define EMACS_STARTING  1
30 #define EMACS_ENDING    2
31 
32 /* variables */
33 static char     *argv[MAXPARAM];
34 static int      argc;
35 
36 static char FrameClassName [] = PROGNAME ":frame";
37 
38 #if WINDOW_MSWIN32
39 /* The Catch/Throw API is replaced by setjmp/longjmp */
40 static jmp_buf ExitCatchBuf;
41 #define Throw(buf,n) longjmp(buf,n)
42 #define Catch(buf)   setjmp(buf)
43 #else
44 static CATCHBUF ExitCatchBuf;
45 #endif
46 
47 static int  Sleeping = 0;           /* flag for TakeANap() */
48 static int  TimeSlice;
49 static BOOL LongOperation = FALSE;  /* for longop() and GetCharacter() */
50 static BOOL HourglassOn = FALSE;    /* between SetHourglass & UpdateCursor */
51 static BOOL SystemModal = FALSE;    /* indicates that the Yes/No message
52 				       box should be system-modal */
53 
54 static UINT EmacsBroadcastMsg;
55 static DWORD BroadcastVal;          /* used by the EmacsBroadcast function */
56 
57 static WNDPROC  MDIClientProc;
58 
59 #if GRINDERS != 0
60 static HCURSOR  hRealHourglass;
61 #endif
62 
63 /* prototypes */
64 static void  PASCAL MessageLoop (BOOL WaitMode);
65 static BOOL  PASCAL UpdateCursor (HWND hWnd, UINT wParam, LONG lParam);
66 static void  PASCAL SetHourglass (BOOL hg);
67 
68 /* timeset: return a system-dependant time string */
69 /* =======                                        */
timeset()70 char *PASCAL timeset()
71 
72 {
73     register char *sp;	/* temp string pointer */
74     time_t buf;		/* time data buffer */
75 
76     time(&buf);
77     sp = ctime(&buf);
78     sp[strlen(sp)-1] = 0;
79     return(sp);
80 }
81 
82 /* longop:    to be called regularly while a long operation is in progress */
83 /* ========                                                                */
84 
longop(int f)85 PASCAL longop (int f)
86 
87 /* f is TRUE to set long operation status and FALSE to reset that status */
88 /* when a long operation is signaled at least twice, the hourglass
89    cursor comes up */
90 /* While the long operation is in progress, this function runs the
91    message loop approximately every 100 ms to yield to other
92    applications and allow a reduced set of commands (among which quit)
93    to be input by the user */
94 {
95     static DWORD    LastYield;  /* time of last yield in milliseconds */
96 
97     if (f) {
98         if (!LongOperation) {
99             LongOperation = TRUE;
100             LastYield = GetCurrentTime ();
101         }
102 	else {  /* 2nd (or more) call in a row, let's yield if enough
103 		   time has elapsed */
104 	    DWORD   Time;
105 
106 	    if ((Time = GetCurrentTime ()) - LastYield >= TimeSlice) {
107 #if GRINDERS != 0
108                 if (++GrinderIndex >= GRINDERS) GrinderIndex = 0;
109 #endif
110 		SetHourglass (TRUE);
111 	        LastYield = Time;
112 	        MessageLoop (FALSE);
113 	    }
114 	}
115     }
116     else {
117 	if (LongOperation) {
118 	    LongOperation = FALSE;
119 	    SetHourglass (FALSE);
120 	}
121     }
122 } /* longop */
123 
124 /* mlyesno: ask a yes/no question */
125 /* =======                        */
126 
mlyesno(char * prompt)127 PASCAL mlyesno (char *prompt)
128 
129 /* This function replaces the mlyesno from input.c. Instead of asking a
130    question on the message line, it pops up a message box */
131 {
132     if (MessageBox (hFrameWnd, prompt, ProgName,
133                     MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION |
134 		    (SystemModal ? MB_SYSTEMMODAL : 0)) == IDYES) return TRUE;
135     return FALSE;
136 } /* mlyesno */
137 
138 /* mlabort: display a serious error message (proposes abort) */
139 /* =======                                                   */
140 
mlabort(char * s)141 VOID PASCAL NEAR mlabort (char *s)
142 {
143     char    text[NSTRING];  /* hopefully sufficient! */
144 
145     while (*s == '%') s++;  /* remove those strange % signs in some messages */
146     strcpy (text, s);
147     strcat (text, "\tAbort ");
148     strcat (text, ProgName);
149     strcat (text, " ?");
150     if (MessageBox (hFrameWnd, text, NULL,
151                     MB_YESNO | MB_DEFBUTTON2 |
152                     MB_ICONHAND | MB_APPLMODAL) == IDYES) {
153         eexitval = -1;
154         Throw (ExitCatchBuf, ABORT);
155      }
156 } /* mlabort */
157 
158 /* WinInit: all the window initialization crap... */
159 /* =======                                        */
160 
WinInit(LPSTR lpCmdLine,int nCmdShow)161 BOOL FAR PASCAL WinInit (LPSTR lpCmdLine, int nCmdShow)
162 
163 /* returns FALSE if failed init */
164 {
165     WNDCLASS    wc;
166     HMENU       hSysMenu;
167     char        text [TXTSIZ];
168     POINT       InitPos;
169     int         Colors;
170     int         i;
171     static char HomeDir [NFILEN] = "HOME=";
172     WORD        w;
173 
174     InitializeFarStorage ();
175 #if WINDOW_MSWIN32
176     Win386Enhanced = FALSE;
177     Win31API = TRUE;
178 #else
179     Win386Enhanced = GetWinFlags () & WF_ENHANCED;
180     w = LOWORD(GetVersion());
181     Win31API = ((w & 0xFF) > 3) || ((w >> 8) >= 10);
182 #endif
183 
184     GetPrivateProfileString (ProgName, "HelpFile", "", text, TXTSIZ, IniFile);
185 
186     if (text[0] != '\0') MainHelpFile = copystr (text);
187     else MainHelpFile = NULL;   /* default supplied below... */
188     MainHelpUsed = FALSE;
189     HelpEngineFile[0] = '\0';
190 
191     i = GetModuleFileName (hEmacsInstance, text, NFILEN-9);
192     for (; i >= 0; i--) {
193         if (text[i] == '\\') {
194             break;
195         }
196     }
197     if (!MainHelpFile) {    /* default WinHelp file name */
198         if (i > 0) text[i+1] = '\0';
199         else text[0] = '\0';
200         strcat (text, "mewin.hlp");
201         MainHelpFile = copystr (text);
202     }
203     if (i > 0) {
204         text[i] = '\0';
205         strcat (HomeDir, text);
206         putenv (HomeDir);   /* set HOME environment var to point to
207 			       MicroEMACS executable directory */
208     }
209 
210     TimeSlice = GetPrivateProfileInt (ProgName, "TimeSlice", 100, IniFile);
211 
212     Colors = GetPrivateProfileInt (ProgName, "Colors", 0, IniFile);
213 
214     GetPrivateProfileString (ProgName, "InitialSize", "", text,
215 				TXTSIZ, IniFile);
216     strlwr (text);
217     if (strstr (text, "optimize")) {
218 	InitPos.x = InitPos.y = 0;
219     }
220     else {
221 	InitPos.x = InitPos.y = CW_USEDEFAULT;
222     }
223 
224     if (nCmdShow == SW_SHOWNORMAL) {
225 	if (strstr (text, "maximize")) {
226 	    nCmdShow = SW_SHOWMAXIMIZED;
227 	}
228 	else if (strstr (text, "minimize") || strstr (text, "icon")) {
229 	    nCmdShow = SW_SHOWMINNOACTIVE;
230 	}
231     }
232 
233     GetPrivateProfileString (ProgName, "CaretShape", "", text,
234 				TXTSIZ, IniFile);
235     strlwr (text);
236     if (strstr(text, "horizontal"))
237     	caret_shape = 0;
238     if (strstr(text, "vertical"))
239     	caret_shape = 1;
240     if (strstr(text, "fullcell"))
241     	caret_shape = 2;
242 
243     /*-Register the broadcast message */
244     strcpy (text, ProgName);
245     strcat (text, ":Broadcast_1.1");
246     EmacsBroadcastMsg = RegisterWindowMessage(text);
247 
248     /*-Register the frame window class */
249     wc.style        = 0;
250     wc.lpfnWndProc  = (void*)&FrameWndProc;
251     wc.cbClsExtra   = 0;
252     wc.cbWndExtra   = FRMWNDEXTRA;
253     wc.hInstance    = hEmacsInstance;
254     wc.hIcon        = LoadIcon (hEmacsInstance, "AppIcon");
255     wc.hCursor      = NULL;
256     wc.hbrBackground= (HBRUSH)(COLOR_WINDOW + 1);
257     wc.lpszMenuName = "Menu";
258     wc.lpszClassName= FrameClassName;
259     RegisterClass (&wc);
260 
261     /*-Register the MDI child (screen) window class */
262     strcpy (text, ProgName);
263     strcat (text, "_screen");
264     ScreenClassName = copystr(text);
265     wc.style        = 0;
266     wc.lpfnWndProc  = (void*)&ScrWndProc;
267     wc.cbClsExtra   = 0;
268     wc.cbWndExtra   = SCRWNDEXTRA;
269     wc.hInstance    = hEmacsInstance;
270     wc.hIcon        = LoadIcon (hEmacsInstance, "ScreenIcon");
271     wc.hCursor      = NULL;
272     wc.hbrBackground= (HBRUSH)(COLOR_WINDOW + 1);
273     wc.lpszMenuName = NULL;
274     wc.lpszClassName= ScreenClassName;
275     RegisterClass (&wc);
276 
277     /*-Create the frame window */
278     hFrameWnd = CreateWindow (FrameClassName,       /* class */
279 			      PROGNAME " " VERSION, /* title */
280 			      WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
281 			      InitPos.x,            /* positions */
282 			      InitPos.y,
283 			      CW_USEDEFAULT,        /* dimensions */
284 			      CW_USEDEFAULT,
285 			      NULL,                 /* parent handle */
286 			      NULL,                 /* menu */
287 			      hEmacsInstance,
288 			      NULL);
289 
290     if (hFrameWnd == 0) return FALSE;
291 
292     if (Colors == 0) {
293         /* try to detect monochrome displays */
294 	HDC     hDC;
295 
296 	hDC = GetDC (hFrameWnd);
297         ColorDisplay = (GetDeviceCaps (hDC, NUMCOLORS) > 2);
298 	ReleaseDC (hFrameWnd, hDC);
299     }
300     else {
301         ColorDisplay = (Colors > 2);
302     }
303 
304     ShowWindow (hFrameWnd, nCmdShow);
305 
306     hScreenCursor = LoadCursor (hEmacsInstance, "ScreenCursor");
307     hTrackCursor = hScreenCursor;
308     hNotQuiescentCursor = LoadCursor (hEmacsInstance,
309                                       "NotQuiescentCursor");
310 #if GRINDERS == 0
311     hHourglass = LoadCursor (NULL, IDC_WAIT);
312 #else
313     strcpy (text, "Grinder1");
314     for (i = 0; i < GRINDERS; i++) {
315         text[7] = (char)i + '1';    /* this assumes GRINDERS < 10 */
316         GrinderCursor[i] = LoadCursor (hEmacsInstance, text);
317     }
318     hRealHourglass = LoadCursor (NULL, IDC_WAIT);
319     GrinderIndex = 0;
320     hHourglass = GrinderCursor[0];
321 #endif
322 
323     in_init ();                 /* sets up the input stream */
324 
325     argv [0] = ProgName;        /* dummy program name */
326     {
327 	register char   *s;
328 
329 	argc = 1;
330 	s = copystr (lpCmdLine);
331 	while (*s != '\0') {
332 	    argv[argc] = s;
333 	    if (++argc >= MAXPARAM) goto ParsingDone;
334 	    while (*++s != ' ') if (*s == '\0') goto ParsingDone;
335 	    *s = '\0';
336 	    while (*++s == ' ');
337 	}
338     }
339 ParsingDone:
340     return TRUE;
341 } /* WinInit */
342 
343 /* SetFrameCaption: sets the frame window's text according to the app Id */
344 /* ===============                                                       */
345 
SetFrameCaption(void)346 static void PASCAL  SetFrameCaption (void)
347 {
348     char    text[sizeof(PROGNAME) + sizeof(VERSION)+20];
349     char    *t;
350     int     Id;
351 
352     strcpy (text, PROGNAME " " VERSION);
353     Id = GetWindowWord (hFrameWnd, GWW_FRMID);
354     if (Id) {
355 	for (t = text; *t != '\0'; t++) ;   /* look for the end of text */
356 	*t++ = ' ';
357 	*t++ = '#';
358 	itoa (Id, t, 10);   /* append the App Id */
359     }
360     SetWindowText (hFrameWnd, text);
361     if (IsIconic(hFrameWnd))  /* force redrawing of the icon title */
362 	SendMessage(hFrameWnd, WM_NCACTIVATE, TRUE, 0L);
363 } /* SetFrameCaption */
364 
365 /* BroadcastEnumProc:   used by EmacsBroadcast */
366 /* =================                           */
BroadcastEnumProc(HWND hWnd,LONG lParam)367 BOOL EXPORT FAR PASCAL BroadcastEnumProc (HWND hWnd, LONG lParam)
368 {
369     char    ClassName [sizeof(FrameClassName)+1];
370     UINT    RetVal;
371 
372     if (hWnd != hFrameWnd) {
373         ClassName[0] = '\0';
374         GetClassName (hWnd, (LPSTR)&ClassName[0], sizeof(FrameClassName)+1);
375         if (strcmp (ClassName, FrameClassName) == 0) {
376             /* The enumerated window is a MicroEMACS frame */
377             if (lParam != 0) {
378                 /*-compute max of all returned values */
379                 RetVal = SendMessage (hWnd, EmacsBroadcastMsg,
380                                       (UINT)hFrameWnd, lParam);
381 		BroadcastVal = max(BroadcastVal, RetVal);
382 	    }
383 	    else {
384 		/*-compute number of applications */
385 		BroadcastVal++;
386 	    }
387 	}
388     }
389     return TRUE;
390 } /* BroadcastEnumProc */
391 
392 /* EmacsBroadcast:  send a broadcast message to all Emacs applications */
393 /* ==============                                                      */
394 
EmacsBroadcast(DWORD MsgParam)395 static DWORD PASCAL   EmacsBroadcast (DWORD MsgParam)
396 
397 /* If MsgParam is not zero, the broadcast is sent as an EmacsBroadcastMsg
398    to all the Emacs frame windows, except the one specified by hFrameWnd.
399    The wParam of that message is hFrameWnd, lParam is set to MsgParam.
400    The value returned is the highest returned value from the broadcast
401    or zero if no other emacs instance was found.
402 
403  - If MsgParam is 0, this function simply returns the number of Emacs
404    instances found, not counting the one identified by hFrameWnd.
405 
406 */
407 {
408     FARPROC ProcInstance;
409 
410     ProcInstance = MakeProcInstance ((FARPROC)BroadcastEnumProc,
411 				     hEmacsInstance);
412     BroadcastVal = 0;
413     EnumWindows (ProcInstance, MsgParam);
414     FreeProcInstance (ProcInstance);
415     return BroadcastVal;
416 } /* EmacsBroadcast */
417 
418 /* MDIClientSubProc:    Subclassing window proc for the MDI Client window */
419 /* ================                                                       */
420 
MDIClientSubProc(HWND hWnd,UINT wMsg,UINT wParam,LONG lParam)421 LONG EXPORT FAR PASCAL MDIClientSubProc (HWND hWnd, UINT wMsg, UINT wParam,
422 				         LONG lParam)
423 {
424     switch (wMsg) {
425 
426     case WM_CREATE:
427         if (Win31API) DragAcceptFiles (hWnd, TRUE);
428         goto DefaultProc;
429 
430     case WM_DROPFILES:
431 	DropMessage (hWnd, (HDROP)wParam);
432 	break;
433 
434     case WM_SETCURSOR:
435 	if (UpdateCursor (hWnd, wParam, lParam)) return TRUE;
436 	goto DefaultProc;
437 
438     case WM_KEYDOWN:
439     case WM_SYSKEYDOWN:
440     case WM_CHAR:
441     case WM_SYSCHAR:
442 	if (EatKey (wMsg, wParam, lParam)) break;
443         goto DefaultProc;
444 
445     case WM_PARENTNOTIFY:
446         if (notquiescent) break;    /* blocks mouse selection of an MDI
447 				       child (except on caption clicks!)
448 				       */
449         goto DefaultProc;
450 
451     default:
452 DefaultProc:
453 	return CallWindowProc (MDIClientProc, hWnd, wMsg, wParam, lParam);
454     }
455     return 0L;
456 } /* MDIClientSubProc */
457 
458 /* FrameInit:   Frame window's WM_CREATE */
459 /* =========                             */
460 
FrameInit(CREATESTRUCT * cs)461 void FAR PASCAL FrameInit (CREATESTRUCT *cs)
462 {
463     RECT    Rect;
464     CLIENTCREATESTRUCT  ccs;
465     HMENU   hMenu;
466     UINT    FrameId;
467 
468     if (Win31API) DragAcceptFiles (hFrameWnd, TRUE);
469 
470     /*-advertise our birth to other Emacs instances and set the frame's
471        caption with an Id if necessary */
472     if ((FrameId = (UINT)EmacsBroadcast (MAKELONG(EMACS_STARTING,
473                                                   EMACS_PROCESS))) != 0) {
474         FrameId++;
475     }
476     SetWindowWord (hFrameWnd, GWW_FRMID, (WORD)FrameId);
477     SetFrameCaption ();
478 
479     /*-add "optimize" to system menu */
480     hMenu = GetSystemMenu (hFrameWnd, 0);
481     InsertMenu (hMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED, SC_OPTIMIZE,
482                 "&Optimize");
483 
484     /*-initialize fonts and Cell Metrics */
485     FontInit ();
486 
487     /*-Create the MDI Client window */
488     hMDIClientWnd = NULL;   /* the NULL initial value allows
489 			       DefFrameProc to behave properly if
490 			       FrameInit fails */
491     ccs.hWindowMenu = GetScreenMenuHandle ();
492     ccs.idFirstChild = IDM_FIRSTCHILD;
493     GetClientRect (hFrameWnd, &Rect);
494     hMDIClientWnd = CreateWindow ("MDICLIENT",      /* class */
495 				  NULL,             /* title */
496 				  WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL |
497 				      WS_HSCROLL | WS_VISIBLE,  /* style */
498 			          0,                   /* positions */
499 				  0,
500 				  Rect.right,          /* dimensions */
501 				  Rect.bottom - EmacsCM.MLHeight,
502 				  hFrameWnd,           /* parent handle */
503 				  NULL,                /* menu */
504 				  hEmacsInstance,
505 				  (LPSTR) (LPCLIENTCREATESTRUCT) &ccs);
506     if (hMDIClientWnd) {
507         /* we subclass the MDIClient */
508         FARPROC ProcInstance;
509 
510         MDIClientProc = (WNDPROC)GetWindowLong (hMDIClientWnd, GWL_WNDPROC);
511         ProcInstance = MakeProcInstance ((FARPROC)MDIClientSubProc,
512 				         hEmacsInstance);
513         SetWindowLong (hMDIClientWnd, GWL_WNDPROC, (DWORD)ProcInstance);
514     }
515 } /* FrameInit */
516 
517 /* CloseEmacs:   handle WM_CLOSE of WM_QUERYENDSESSION messages */
518 /* ==========                                                   */
519 
CloseEmacs(UINT wMsg)520 static BOOL  PASCAL CloseEmacs (UINT wMsg)
521 
522 /* returns TRUE if emacs should exit */
523 {
524     if (eexitflag) return TRUE;     /* emacs has already quited */
525     if (wMsg == WM_QUERYENDSESSION) SystemModal = TRUE;
526     if (fbusy != FWRITING) {   /* no file write in progress */
527         quit (FALSE, 0);    /* check if it's ok with emacs to terminate */
528     }
529     else {                  /* there is, indeed a file write in progress
530         MessageBeep (0);    /* wake the user up! */
531         if (MessageBox (hFrameWnd, TEXT333,
532                             /* "File write in progress. Quit later!" */
533                         ProgName,
534                         MB_OKCANCEL | MB_ICONSTOP |
535 		        (SystemModal ? MB_SYSTEMMODAL : 0)) == IDCANCEL) {
536             quit (TRUE, 0); /* give up! */
537         }
538     }
539     if (wMsg == WM_QUERYENDSESSION) SystemModal = FALSE;
540     if (eexitflag) {        /* emacs agrees to quit */
541         ClipboardCleanup ();    /* close clipboard if we had it open */
542         if (fbusy != FALSE) ffclose (); /* cleanup file IOs */
543         return TRUE;
544     }
545     update (TRUE);          /* restore the caret that quit() moved into
546 			       the message line */
547     return FALSE;	    /* we refuse to die! */
548 } /* CloseEmacs */
549 
550 /* ScrWndProc:  MDI child (screen) window function */
551 /* ==========                                      */
ScrWndProc(HWND hWnd,UINT wMsg,UINT wParam,LONG lParam)552 LONG EXPORT FAR PASCAL ScrWndProc (HWND hWnd, UINT wMsg, UINT wParam,
553 				   LONG lParam)
554 {
555     switch (wMsg) {
556 
557     case WM_CREATE:
558         if (Win31API) DragAcceptFiles (hWnd, TRUE);
559         if (!hscrollbar) ShowScrollBar (hWnd, SB_HORZ, FALSE);
560         if (!vscrollbar) ShowScrollBar (hWnd, SB_VERT, FALSE);
561 	{   /*-init WindowWords for ScrReSize function */
562 	    RECT    Rect;
563 
564 	    GetClientRect (hWnd, &Rect);
565 	    SetWindowWord (hWnd, GWW_SCRCX, (WORD)Rect.right);
566 	    SetWindowWord (hWnd, GWW_SCRCY, (WORD)Rect.bottom);
567 	}
568 	{   /*-setup the stuff for display.c */
569 	    SCREEN  *sp;
570 
571 	    sp = (SCREEN*)(((MDICREATESTRUCT*)(((CREATESTRUCT*)lParam)->
572 					       lpCreateParams))->lParam);
573 	    SetWindowLong (hWnd, GWL_SCRPTR, (LONG)sp);
574 	    sp->s_drvhandle = hWnd;
575 	}
576 	goto DefaultProc;
577 
578     case WM_DESTROY:
579 	vtfreescr ((SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR));
580 	break;
581 
582     case WM_KILLFOCUS:
583 	EmacsCaret (FALSE);
584 	goto DefaultProc;
585     case WM_SETFOCUS:
586 	EmacsCaret (TRUE);
587 	goto DefaultProc;
588     case WM_ACTIVATE:
589 	if (LOWORD(wParam)) EmacsCaret (TRUE);
590 	goto DefaultProc;
591 
592     case WM_MDIACTIVATE:
593 #if WINDOW_MSWIN32
594         if ((HWND)lParam == hWnd) {
595 #else
596 	if (wParam) {
597 #endif
598             /* this one is becoming active */
599 	    if (!InternalRequest) {
600 	        InternalRequest = TRUE;
601 	        select_screen ((SCREEN *) GetWindowLong (hWnd, GWL_SCRPTR),
602                                FALSE);
603 		InternalRequest = FALSE;
604 	    }
605 	    else {
606 		SCREEN  *sp;
607 
608 		sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
609 		if (sp->s_virtual == NULL) {
610 		    /* this is initialization time! */
611 		    vtinitscr (sp, DisplayableRows (hWnd, 0, &EmacsCM),
612 		               DisplayableColumns (hWnd, 0, &EmacsCM));
613 		}
614 	    }
615 	}
616 	goto DefaultProc;
617 
618     case WM_KEYDOWN:
619     case WM_SYSKEYDOWN:
620     case WM_CHAR:
621     case WM_SYSCHAR:
622 	if (EatKey (wMsg, wParam, lParam)) break;
623         goto DefaultProc;
624 
625     case WM_DROPFILES:
626         DropMessage (hWnd, (HDROP)wParam);
627         break;
628 
629     case WM_MOUSEMOVE:
630     case WM_LBUTTONDOWN:
631     case WM_LBUTTONUP:
632     case WM_MBUTTONDOWN:
633     case WM_MBUTTONUP:
634     case WM_RBUTTONDOWN:
635     case WM_RBUTTONUP:
636 	MouseMessage (hWnd, wMsg, wParam, lParam);
637 	break;
638 
639     case WM_VSCROLL:
640     case WM_HSCROLL:
641         ScrollMessage (hWnd, wMsg, LOWORD(wParam),
642 #if WINDOW_MSWIN32
643                        (int)HIWORD(wParam));
644 #else
645                        (int)LOWORD(lParam));
646 #endif
647         break;
648 
649     case WM_SETCURSOR:
650 	if (UpdateCursor (hWnd, wParam, lParam)) return TRUE;
651 	goto DefaultProc;
652 
653     case WM_PAINT:
654 	ScrPaint (hWnd);
655 	break;
656 
657     case WM_SIZE:
658 	if (!IsIconic (hFrameWnd)) {
659 	    if (wParam == SIZEICONIC) EmacsCaret (FALSE);
660 	    else EmacsCaret (TRUE);
661 	    ScrReSize (hWnd, wParam, LOWORD(lParam), HIWORD(lParam));
662 	}
663 	goto DefaultProc;
664 
665     case WM_GETMINMAXINFO:
666 	GetMinMaxInfo (hWnd, (LPPOINT)lParam);
667 	goto DefaultProc;
668 
669     case WM_MOUSEACTIVATE:
670 	if (notquiescent) break;    /* blocks mouse selection of an MDI
671 				       child's caption */
672 	goto DefaultProc;
673 
674     case WM_SYSCOMMAND:
675 	switch (wParam & 0xFFF0) {
676 	case SC_RESTORE:
677 	case SC_SIZE:
678 	case SC_MAXIMIZE:
679 	case SC_MINIMIZE:
680 	case SC_NEXTWINDOW:
681 	case SC_PREVWINDOW:
682 	    if (notquiescent) break;    /* sizing commands disabled */
683 	    goto DefaultProc;
684 	case SC_CLOSE:
685 	    if (!notquiescent) {
686 		SCREEN  *sp;
687 
688 		/* this must be done here, before any MDI mumbo-jumbo */
689 	        sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
690 		if (sp == first_screen) {
691 		    cycle_screens (FALSE, 0);
692                 }
693 	        if (sp != first_screen) {
694 	            unlist_screen (sp);
695                     free_screen (sp);
696 		}
697 		update (TRUE);  /* to restore the caret */
698 	    }
699 	    break;
700 	case SC_MOVE:
701 	    if (notquiescent) {
702 	        /* prevent moving an MDI client other than the already
703 		   active one, since it would transfer the activation */
704 	        if (hWnd != (HWND)SendMessage (hMDIClientWnd, WM_MDIGETACTIVE,
705 				               0, 0L)) break;
706 	    }
707 	    goto DefaultProc;
708 	default:
709 	    goto DefaultProc;
710 	}
711 	break;
712 
713     default:
714 DefaultProc:
715 	return DefMDIChildProc (hWnd, wMsg, wParam, lParam);
716     }
717     return 0L;
718 } /* ScrWndProc */
719 
720 /* FrameWndProc:    frame window function */
721 /* ============                           */
722 LONG EXPORT FAR PASCAL FrameWndProc (HWND hWnd, UINT wMsg, UINT wParam,
723 				     LONG lParam)
724 {
725     switch (wMsg) {
726 
727     case WM_DROPFILES:
728 	DropMessage (hWnd, (HDROP)wParam);
729 	break;
730 
731     case WM_INITMENUPOPUP:
732 	InitMenuPopup ((HMENU)wParam, lParam);
733 	goto DefaultProc;
734 
735     case WM_MENUCHAR:
736 	{
737 	    LONG    Code;
738 
739 	    Code = DefFrameProc (hWnd, hMDIClientWnd, wMsg, wParam, lParam);
740 	    if (HIWORD(Code) != 0) return Code; /* matches a menu command */
741 	    if (EatKey (wMsg, wParam, lParam)) return MAKELONG(0,1);
742 	        /* MicroEMACS ate that character, close the current menu! */
743 	    else return Code;
744 	}
745 
746     case WM_COMMAND:
747         if (!MenuCommand (wParam, lParam)) goto DefaultProc;
748 	break;
749 
750     case WM_SYSCOMMAND:
751 	switch (wParam & 0xFFF0) {
752 	case SC_RESTORE:
753 	    if (IsIconic (hFrameWnd)) goto DefaultProc;
754 	case SC_SIZE:
755 	case SC_MAXIMIZE:
756 	    if (notquiescent) break;    /* sizing commands disabled */
757 	    goto DefaultProc;
758 	case SC_OPTIMIZE:
759 	    if (notquiescent) break;
760 	    if (IsZoomed (hFrameWnd) || IsIconic (hFrameWnd)) {
761 	        /* must restore it first */
762 	        ShowWindow (hFrameWnd, SW_RESTORE);
763 	    }
764 	    {
765 	        int     IconHeight;
766 
767 	        if (Win31API) {
768 	            IconHeight = GetSystemMetrics (SM_CYICONSPACING);
769 	        }
770 	        else {
771 	            IconHeight = GetSystemMetrics (SM_CYICON) +
772 			         GetSystemMetrics (SM_CYCAPTION) +
773 	                         GetSystemMetrics (SM_CYBORDER);
774 	        }
775 	        MoveWindow (hFrameWnd, 0, 0,
776 	                    GetSystemMetrics (SM_CXSCREEN),
777 	                    GetSystemMetrics (SM_CYSCREEN) - IconHeight,
778 	                    TRUE);
779 	    }
780 	    break;
781 	default:
782 	    goto DefaultProc;
783 	}
784 	break;
785 
786     case WM_SETCURSOR:
787 	if (UpdateCursor (hWnd, wParam, lParam)) return TRUE;
788 	goto DefaultProc;
789 
790     case WM_TIMER:
791 	if (wParam == T_SLEEP) {
792 	    --Sleeping;     /* signal wake up to TakeANap() */
793 	    break;
794 	}
795 	goto DefaultProc;
796 
797     case WM_PAINT:  /* must be the message line */
798 	MLPaint ();
799 	break;
800 
801     case WM_SIZE:   /* must adjust the MDIClient's size to fit the
802 		       Message Line */
803 	MoveWindow (hMDIClientWnd, 0, 0,
804                     LOWORD(lParam), HIWORD(lParam) - EmacsCM.MLHeight, TRUE);
805         InvalidateRect (hFrameWnd, NULL, FALSE);
806         break;
807 
808 #if !WINDOW_MSWIN32
809     case WM_NCLBUTTONDBLCLK:
810 	if (Win31API) goto DefaultProc;
811 	else {
812            /* This corrects a Windows 3.0 bug that prevents a maximized
813 	       child from being closed by double-clicking the close-box.
814 	       See "Windows 3: A Developer's Guide" by Jeffrey M.
815 	       Richter, page 480 */
816 	    RECT    Rect;
817 	    HBITMAP hBmp;
818 	    BITMAP  Bmp;
819 	    LONG    MDIGetActivateResult;
820 
821 	    if (wParam != HTMENU) goto DefaultProc;
822 	    MDIGetActivateResult = SendMessage (hMDIClientWnd,
823 						WM_MDIGETACTIVE, 0, 0);
824 	    if (!HIWORD(MDIGetActivateResult)) goto DefaultProc;
825 	    GetWindowRect (hWnd, &Rect);
826 	    Rect.top += GetSystemMetrics(SM_CYCAPTION) +
827 			GetSystemMetrics (SM_CYFRAME);
828 	    Rect.left += GetSystemMetrics(SM_CXFRAME);
829 	    hBmp = LoadBitmap (NULL, MAKEINTRESOURCE(OBM_CLOSE));
830 	    GetObject (hBmp, sizeof(BITMAP), (LPSTR)(LPBITMAP)&Bmp);
831 	    Rect.bottom = Rect.top + Bmp.bmHeight;
832 	    Rect.right = Rect.left + Bmp.bmWidth / 2;
833 	    if (!PtInRect(&Rect, MAKEPOINT(lParam))) goto DefaultProc;
834 	    SendMessage(LOWORD(MDIGetActivateResult), WM_SYSCOMMAND,
835                         SC_CLOSE, lParam);
836 	}
837 	break;
838 #endif
839 
840     case WM_ACTIVATE:
841 	EmacsCaret (LOWORD(wParam));
842 	    /* This call matters only when the active MDI Child is
843 	       iconic, in which case it never receives the focus and
844 	       thus fails to create the caret (that should still appear
845 	       if we are getting input on the message line) */
846 	goto DefaultProc;
847 
848     case WM_CREATE:
849 	hFrameWnd = hWnd;
850 	FrameInit ((CREATESTRUCT *)lParam);
851 	goto DefaultProc;
852 
853     case WM_QUERYENDSESSION:
854 	if (CloseEmacs (wMsg)) return 1L;
855 	break;
856 
857     case WM_CLOSE:
858         if (CloseEmacs (wMsg)) goto DefaultProc;
859 	break;
860 
861     case WM_DESTROY:
862 	if (MainHelpUsed) WinHelp (hFrameWnd, MainHelpFile, HELP_QUIT, NULL);
863 	if (HelpEngineFile[0] != '\0') WinHelp (hFrameWnd, HelpEngineFile,
864                                                 HELP_QUIT, NULL);
865 	EmacsBroadcast (MAKELONG(EMACS_ENDING,EMACS_PROCESS));
866 	PostQuitMessage (0);
867 	break;
868 
869     default:
870         if ((wMsg == EmacsBroadcastMsg) &&
871 	    (HIWORD(lParam) == EMACS_PROCESS)) {
872 	    UINT    Id;
873 
874             switch (LOWORD(lParam)) {
875 	    case EMACS_STARTING:
876 	        /* another instance of emacs is starting. If our ID was
877 	           0, it should now be 1 */
878 	        if (GetWindowWord (hFrameWnd, GWW_FRMID) == 0) {
879 	            SetWindowWord (hFrameWnd, GWW_FRMID, 1);
880 	            SetFrameCaption ();
881 	        }
882 	        break;
883 	    case EMACS_ENDING:
884 	        /* another instance of emacs is ending. If we are going to be
885 	           alone, let's switch our Id back to 0 */
886 	        Id = (UINT)EmacsBroadcast (0);
887 	        /* Id here is the number of Emacs applications, except for us */
888 	        if (Id == 1) {
889 		    SetWindowWord (hFrameWnd, GWW_FRMID, 0);
890 		    SetFrameCaption ();
891 	        }
892 	    }
893 	    /* send back our Id */
894 	    return (LONG)GetWindowWord (hFrameWnd, GWW_FRMID);
895         }
896 DefaultProc:
897 	return DefFrameProc (hWnd, hMDIClientWnd, wMsg, wParam, lParam);
898     }
899     return 0L;
900 } /* FrameWndProc */
901 
902 /* WinMain: Application entry point */
903 /* =======                          */
904 
905 int PASCAL  WinMain (HANDLE hInstance, HANDLE hPrevInstance,
906                      LPSTR lpCmdLine, int nCmdShow)
907 {
908     hEmacsInstance = hInstance;
909     if (!WinInit (lpCmdLine, nCmdShow)) return -1;
910 
911     switch (Catch (ExitCatchBuf)) {
912     case 0:
913 	emacs (argc, argv);
914 	/* If we exit through an emacs command, we pass here. Otherwise
915 	   (exit through a close in system menu or a windows session
916 	   termination), the application terminates from the call to
917 	   mswgetc that received the signal */
918     case ABORT:
919         eexitflag = TRUE;
920         longop (FALSE);
921 	PostMessage (hFrameWnd, WM_CLOSE, 0, 0L);
922 	for (;;) MessageLoop (TRUE);/* go into message loop until WM_QUIT */
923 /*  case TRUE:
924         break; */
925     }
926     return eexitval;
927 } /* WinMain */
928 
929 /* ModifyCursor:    forces a WM_SETCURSOR */
930 /* ============                           */
931 
932 static void PASCAL  ModifyCursor (void)
933 {
934     POINT   pt;
935 
936     GetCursorPos (&pt);
937     SetCursorPos (pt.x, pt.y);
938 } /* ModifyCursor */
939 
940 /* MessageLoop: Main message loop */
941 /* ===========                    */
942 
943 static void  PASCAL MessageLoop (BOOL WaitMode)
944 
945 /* If WaitMode is TRUE this function uses GetMessage the first time and
946    PeekMessage after that, until the input queue is empty and there is
947    something in the in_put pipe. Thus, WM_PAINTs are processed and the
948    input queue is emptied into the in_put pipe.
949    If WaitMode is FALSE, this function uses PeekMessage from the
950    beginning and returns only when the input queue is empty. In that
951    case, WM_PAINTs are not processed, but the processor still gets
952    relinquished. Note that while peeking for messages, this function
953    returns if the in_put pipe is about to overflow */
954 {
955     MSG     Msg;
956     BOOL    Peeking;
957 
958     ++notquiescent; /* this restrics some actions in nested calls */
959 
960     ScrollBars ();  /* hide or show as needed (may generate WM_SIZE) */
961     ModifyCursor ();
962 
963     do {
964         Peeking = !WaitMode;
965         for (;;) {
966             if(defferupdate) {
967                 ShowEmacsCaret (FALSE);
968                 update (TRUE);
969                 ShowEmacsCaret (TRUE);
970             }
971 	    if (Peeking) {
972 	        if (!in_room (5)) break; /* input stream about to overflow */
973 	        if (!PeekMessage (&Msg, NULL, 0, 0, PM_REMOVE)) break;
974 	    }
975 	    else {
976                 longop (FALSE); /* we are going to wait for input */
977 	        GetMessage (&Msg, NULL, 0, 0);
978 	        Peeking = TRUE; /* from now on... */
979 	    }
980 	    if (Msg.message == WM_QUIT) {   /* time to leave... */
981 	        JettisonFarStorage ();
982 	        if (hEmacsFont) DeleteObject (hEmacsFont);
983 	        Throw (ExitCatchBuf, TRUE);
984 	        /* we're gone out of business ! */
985 	        /* **************************** */
986 	    }
987 	    if (!TranslateMDISysAccel (hMDIClientWnd, &Msg)) {
988 	        TranslateMessage (&Msg);
989 	        DispatchMessage (&Msg);
990 	    }
991 	}
992     } while (WaitMode && !in_check ()); /* keep waiting if pipe is empty */
993 
994     --notquiescent;
995     ModifyCursor ();
996     return;
997 } /* MessageLoop */
998 
999 /* GetInput: wait for user input (called by mswgetc from mswdrv.c) */
1000 /* ========                                                        */
1001 
1002 /* The returned value is the next character from the input stream */
1003 
1004 int FAR PASCAL GetInput (void)
1005 {
1006     if (!in_check ()) {
1007 	ShowEmacsCaret (TRUE);
1008 	MessageLoop (TRUE);
1009 	ShowEmacsCaret (FALSE);
1010     }
1011     return in_get ();       /* return next character */
1012 } /* GetInput */
1013 
1014 /* TakeANap:    put emacs to sleep for a few milliseconds */
1015 /* ========                                               */
1016 
1017 int FAR PASCAL  TakeANap (int t)
1018 /* this function is used by mswsleep(). It returns TRUE unless the timer
1019    could not be created. Note that for a null time, it simply
1020    relinquishes the processor */
1021 {
1022     TakingANap = TRUE;
1023     if ((t == 0) ||
1024         (!SetTimer (hFrameWnd, T_SLEEP, t, (FARPROC)NULL))) {
1025 	ShowEmacsCaret (TRUE);
1026 	MessageLoop (FALSE);    /* let's do one relinquish anyway */
1027 	ShowEmacsCaret (FALSE);
1028 	TakingANap = FALSE;
1029 	if (t == 0) return TRUE;
1030         else return FALSE;
1031     }
1032     ShowEmacsCaret (TRUE);
1033     Sleeping = 1;   /* this gets reset by WM_TIMER processing */
1034     do {
1035         MessageLoop (FALSE);
1036         WaitMessage ();
1037     } while (Sleeping > 0);
1038     ShowEmacsCaret (FALSE);
1039     KillTimer (hFrameWnd, T_SLEEP);
1040     TakingANap = FALSE;
1041     return TRUE;
1042 } /* TakeANap */
1043 
1044 /* UpdateCursor:    sets the apropriate Emacs cursor shape */
1045 /* ============                                            */
1046 
1047 static BOOL  PASCAL UpdateCursor (HWND hWnd, UINT wParam, LONG lParam)
1048 
1049 /* this function should be called on each WM_SETCURSOR message, to
1050    display the appropriate cursor. It returns TRUE if all processing has
1051    been done (the calling window function should return TRUE). it
1052    returns FALSE if the default window proc sould be called. */
1053 {
1054     HCURSOR hCursor;
1055 
1056     switch (LOWORD(lParam)) {   /* hit-test area code as in WM_NCHITTEST */
1057     case HTCLIENT:
1058         if (HourglassOn) hCursor = hHourglass;
1059 	else {
1060 	    if ((HWND)wParam == hFrameWnd) {
1061 	        if (notquiescent) hCursor = hNotQuiescentCursor;
1062 	        else hCursor = NULL;
1063 	    }
1064 	    else {  /* a screen window or the MDIClient */
1065 		if (notquiescent) hCursor = hNotQuiescentCursor;
1066 		else if ((HWND)wParam == hMDIClientWnd) {
1067 		    hCursor = NULL;
1068 		}
1069 		else if (mouseflag) {
1070 		    if (MouseTracking) hCursor = hTrackCursor;
1071 		    else hCursor = hScreenCursor;
1072 		}
1073 		else hCursor = 0;
1074 	    }
1075 	}
1076 	if (!hCursor) hCursor = LoadCursor (NULL, IDC_ARROW);
1077 	break;
1078     case HTHSCROLL:
1079     case HTVSCROLL:
1080     case HTREDUCE:
1081         if (((HWND)wParam == hMDIClientWnd) ||
1082             ((HWND)wParam == hFrameWnd)) return FALSE;
1083     case HTBOTTOM:
1084     case HTBOTTOMLEFT:
1085     case HTBOTTOMRIGHT:
1086     case HTLEFT:
1087     case HTRIGHT:
1088     case HTTOP:
1089     case HTTOPLEFT:
1090     case HTTOPRIGHT:
1091     case HTSIZE:
1092     case HTZOOM:
1093         /* all those are unuseable when notquiescent */
1094         if (notquiescent) {
1095             if (HourglassOn) hCursor = hHourglass;
1096 	    else hCursor = hNotQuiescentCursor;
1097         }
1098         else return FALSE;
1099 	break;
1100     default:
1101 	return FALSE;
1102     }
1103     SetCursor (hCursor);
1104     return TRUE;
1105 } /* UpdateCursor */
1106 
1107 /* SetHourglass:    sets or removes the hourglass cursor */
1108 /* ============                                          */
1109 
1110 static void  PASCAL SetHourglass (BOOL hg)
1111 
1112 /* hg = TRUE sets the hourglass, hg = FALSE removes it */
1113 {
1114     POINT   Point;
1115     HWND    hWnd;
1116 #if GRINDERS != 0
1117     static  int PrevGrinderIndex = -1;
1118 #endif
1119 
1120     if (hg == HourglassOn) {
1121 #if GRINDERS == 0
1122         return;   /* nothing to do */
1123 #else
1124         if ((hg == FALSE) || (PrevGrinderIndex == GrinderIndex)) return;
1125         PrevGrinderIndex = GrinderIndex;
1126 #endif
1127     }
1128     HourglassOn = hg;
1129     GetCursorPos (&Point);
1130     hWnd = WindowFromPoint (Point);
1131     if (hWnd && ((hWnd == hFrameWnd) || IsChild (hFrameWnd, hWnd))) {
1132         /* the cursor is above one of MicroEMACS's windows */
1133 #if GRINDERS != 0
1134         if (flickcode) {
1135             hHourglass = hRealHourglass;
1136 	}
1137         else {
1138             hHourglass = GrinderCursor[GrinderIndex];
1139         }
1140 #endif
1141 	SetCursorPos (Point.x, Point.y);
1142 	/* this should cause a dummy cursor movement to be processed,
1143 	   ending up in UpdateCursor being invoked */
1144     }
1145 } /* SetHourglass */
1146