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