1 /* The routines in this file provide display support under
2    the Microsoft Windows environment on an IBM-PC or compatible
3    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 /* The functions in this module are mostly concerned with the mapping
10    between character cells and client coordinates. */
11 
12 #include    "estruct.h"
13 #include    "elang.h"
14 #include    <stdio.h>
15 #include    <time.h>
16 #include    "eproto.h"
17 #include    "edef.h"
18 
19 #include    "mswin.h"
20 
21 #if     COLOR
22 /* palette table. It is initialized for the default colors, but can be
23    changed by the spal function */
24 static COLORREF EmacsPalette [16] = {
25     0x00000000,     /* black */
26     0x00000080,     /* red */
27     0x00008000,     /* green */
28     0x00008080,     /* yellow */
29     0x00800000,     /* blue */
30     0x00800080,     /* magenta */
31     0x00808000,     /* cyan */
32     0x00C0C0C0,     /* grey (light) */
33     0x00808080,     /* gray (dark) */
34     0x000000FF,     /* light red */
35     0x0000FF00,     /* light green */
36     0x0000FFFF,     /* light yellow*/
37     0x00FF0000,     /* light blue */
38     0x00FF00FF,     /* light magenta*/
39     0x00FFFF00,     /* light cyan */
40     0x00FFFFFF      /* white */
41 };
42 #endif
43 
44 static HWND hCaretWnd = 0;          /* window where the caret belongs */
45 static int  CaretVisible = 0;       /* the caret should be visible if not 0 */
46 static int  CaretCol, CaretRow;     /* caret position */
47 /* Text Metrics values (from the CellMetrics structure) are used as follows:
48 
49     -------------------------------- Client area upper boundary
50     |          ^
51     |       OffsetY
52     |          v
53     |          ^
54     |      HalfLeadingY
55     |          v
56     |       ---------- ----------
57     |<---->|          |          |  ^
58   OffsetX  |   cell   |   cell   |  |    . . .
59     |      |    0,0   |    1,0   | SizeY
60     |      |          |          |  |
61     |      |          |          |  v
62     |       ---------- ----------
63     |                         ^
64     |       <- SizeX->     LeadingY = 2 * HalfLeadingY
65     |                         v
66     |       ---------- ----------
67     |      |          |          |
68     |      |   cell   |   cell   |
69     |      |    0,1   |    1,1   |
70     |      |          |          |
71     |
72     |                ...
73     |
74    Client area left boundary
75 */
76 
77 /* BuildCellMetrics:   fills a CellMetrics structure from a font description */
78 /* ================                                                          */
79 
BuildCellMetrics(CellMetrics * cm,HFONT hFont)80 void FAR PASCAL BuildCellMetrics (CellMetrics *cm, HFONT hFont)
81 {
82     HDC     hDC;
83     TEXTMETRIC Metrics;
84     HANDLE  hPrevFont;
85 
86     hDC = GetDC (hFrameWnd);
87     hPrevFont = SelectFont (hDC, hFont);
88     GetTextMetrics (hDC, &Metrics);
89     SelectObject (hDC, hPrevFont);
90     ReleaseDC (hFrameWnd, hDC);
91     cm->SizeX = Metrics.tmAveCharWidth;
92     if (cm->SizeX == 0) cm->SizeX = 1;  /* ATM gives 0 sometimes !!! */
93     cm->SizeY = Metrics.tmHeight;
94     if (cm->SizeY == 0) cm->SizeY = 1;
95     cm->HalfLeadingY = Metrics.tmExternalLeading / 2;
96     cm->OffsetX = cm->SizeX / 4;
97     if ((cm->OffsetY = (cm->SizeY / 8) - cm->HalfLeadingY) < 0) {
98 	cm->OffsetY = 0;
99 	/* for a reasonable upper boundary separation, we want
100 	   (SizeY / 8) <= OffsetY + HalfLeadingY */
101     }
102     cm->LeadingY = 2 * cm->HalfLeadingY;
103     cm->MLHeight = cm->SizeY + cm->LeadingY + (2 * cm->OffsetY) +
104                    GetSystemMetrics(SM_CYBORDER);
105 } /* BuildCellMetrics */
106 
107 /* InvalidateCells: marks character cells for repaint */
108 /* ===============                                    */
109 
InvalidateCells(HWND hWnd,int leftcol,int toprow,int rightcol,int bottomrow)110 void FAR PASCAL InvalidateCells (HWND hWnd, int leftcol, int toprow,
111                                  int rightcol, int bottomrow)
112 {
113     RECT    Rect;           /* used for Cell and Client coordinates */
114     RECT    ClientRect;     /* MDI window client area */
115     POINT   MaxBottomRight; /* Cell coordinates of bottom right of MDI
116 			       window */
117 
118     GetClientRect (hWnd, &ClientRect);
119     ClientToCell (hWnd, *(POINT *)&ClientRect.right, &MaxBottomRight);
120 
121     Rect.left = leftcol;
122     Rect.top = toprow;
123     CellToClient (hWnd, *(POINT *)&Rect.left, (POINT *)&Rect.left);
124     if (leftcol == 0) Rect.left = 0;
125     else Rect.left -= EmacsCM.SizeX / 2;    /* see Rect.right below... */
126     if (toprow == 0) Rect.top = 0;
127 
128     Rect.right = rightcol + 1;
129     Rect.bottom = bottomrow + 1;
130     CellToClient (hWnd, *(POINT *)&Rect.right, (POINT *)&Rect.right);
131     if (rightcol + 1 >= MaxBottomRight.x) Rect.right = ClientRect.right;
132     else Rect.right += EmacsCM.SizeX / 2;
133         /* this adjustment is done to avoid left-over pixels caused by
134            characters which display an overhang outside their cells
135            (for instance: w & W in Courier New). The corresponding
136            adjustment is done on Rect.left a few lines above */
137     if (bottomrow + 1 >= MaxBottomRight.y) Rect.bottom = ClientRect.bottom;
138 
139     InvalidateRect (hWnd, &Rect, FALSE);
140         /* the background does not need erasing since Paint will
141 	   completely fill the invalid rectangle */
142 } /* InvalidateCells */
143 
144 /* MinimumClientSize:  computes the minimum client area size */
145 /* =================                                         */
146 
MinimumClientSize(HWND hWnd,int NCols,int NRows,int * Width,int * Height)147 void FAR PASCAL MinimumClientSize (HWND hWnd, int NCols, int NRows,
148 				   int *Width, int *Height)
149 
150 /* The values pointed by Height and Width are set to the smallest value
151    that allows displaying NRows rows and NCols columns. A NULL pointer
152    disables the computing of the associated value */
153 {
154     if (Height) *Height = (2 * EmacsCM.OffsetY) +
155                           (NRows * (EmacsCM.SizeY + EmacsCM.LeadingY));
156     if (Width) *Width = (2 * EmacsCM.OffsetX) + (NCols * EmacsCM.SizeX);
157 } /* MinimumClientSize */
158 
159 /* DisplayableRows: returns the number of rows displayable in the client area */
160 /* ===============                                                            */
161 
DisplayableRows(HWND hWnd,int Height,CellMetrics * cm)162 int FAR PASCAL DisplayableRows (HWND hWnd, int Height, CellMetrics *cm)
163 
164 /* Heigh is the hypothetic heigh of the client area. If this parameter
165    is 0, the real client area is measured. If it is negative, the
166    maximal client area (maximized child in a maximized frame) is used.
167    */
168 {
169     RECT    Rect;
170     int     x;
171 
172     if (Height == 0) GetClientRect (hWnd, &Rect);
173     else {
174 	if (Height > 0) {
175 	    Rect.bottom = Height;
176 	}
177 	else {
178 	    Rect.bottom = GetSystemMetrics (SM_CYFULLSCREEN) -
179                           (GetSystemMetrics (SM_CYMENU) + cm->MLHeight);
180 	}
181     }
182     x = (Rect.bottom - (2 * cm->OffsetY)) / (cm->SizeY + cm->LeadingY);
183     if (x < 0) return 0;
184     return x;
185 } /* DisplayableRows */
186 
187 /* DisplayableColumns:  returns the number of columns displayable in the client area */
188 /* ==================                                                                */
189 
DisplayableColumns(HWND hWnd,int Width,CellMetrics * cm)190 int FAR PASCAL DisplayableColumns (HWND hWnd, int Width, CellMetrics *cm)
191 
192 /* Width is the hypothetic width of the client area. If this parameter
193    is 0, the real client area is measured. If it is negative, the
194    maximal client area (maximized child in a maximized frame) is used.
195    */
196 {
197     RECT    Rect;
198     int     x;
199 
200     if (Width== 0) GetClientRect (hWnd, &Rect);
201     else {
202 	if (Width > 0) {
203 	    Rect.right = Width;
204 	}
205 	else {
206 	    Rect.right = GetSystemMetrics (SM_CXFULLSCREEN);
207 	}
208     }
209     x = (Rect.right - (2 * cm->OffsetX)) / cm->SizeX;
210     if (x < 0) return 0;
211     return x;
212 } /* DisplayableColumns */
213 
214 /* UpdateEmacsCaretPos: position the caret according to CaretCol/CaretRow */
215 /* ===================                                                    */
216 
UpdateEmacsCaretPos(void)217 static void PASCAL UpdateEmacsCaretPos (void)
218 {
219     POINT   pt;
220 
221     pt.x = CaretCol;
222     pt.y = CaretRow;
223     CellToClient (hCaretWnd, pt, &pt);
224 
225     if (caret_shape == 0 && hCaretWnd != hFrameWnd) {
226 		pt.y += EmacsCM.SizeY - (EmacsCM.SizeY / 4);
227     }
228 
229     SetCaretPos (pt.x, pt.y + EmacsCM.HalfLeadingY);
230 } /* UpdateEmacsCaretPos */
231 
232 /* EmacsCaret:  Creates or destroys the caret */
233 /* ==========                                 */
234 
EmacsCaret(BOOL Show)235 void FAR PASCAL EmacsCaret (BOOL Show)
236 
237 /* the Show parameter is TRUE if the caret should be created and FALSE
238    if it should be destroyed */
239 {
240 	short xsize, ysize;
241 
242     if (Show) {
243         if (hCaretWnd == 0) return;
244 		if (hFrameWnd == GetActiveWindow ()) {
245 		    if (!IsWindow (hCaretWnd)) {
246 		        /* this may happen in some transient cases when closing
247 			   down a screen */
248 		        hCaretWnd = 0;
249 		        return;
250 		    }
251 
252 		    if (hCaretWnd == hFrameWnd) {
253 		    	xsize = GetSystemMetrics (SM_CXBORDER);
254 		    	ysize = EmacsCM.SizeY;
255 		    }
256 		    else {
257 		    	xsize = (caret_shape == 1)? EmacsCM.SizeX/4: EmacsCM.SizeX;
258 		    	ysize = (caret_shape == 0)? EmacsCM.SizeY/4: EmacsCM.SizeY;
259 		    }
260 
261             CreateCaret (hCaretWnd, NULL, xsize, ysize);
262             UpdateEmacsCaretPos();
263             if (CaretVisible && !IsIconic (hCaretWnd))
264 				ShowCaret (hCaretWnd);
265         }
266     }
267     else /* destroy the caret */
268 		DestroyCaret ();
269 
270 } /* EmacsCaret */
271 
272 /* MoveEmacsCaret:  updates the caret position */
273 /* ==============                              */
274 
MoveEmacsCaret(HWND hWnd,int col,int row)275 void FAR PASCAL MoveEmacsCaret (HWND hWnd, int col, int row)
276 {
277     CaretCol = col;
278     CaretRow = row;
279     if (hWnd != hCaretWnd) {
280 	hCaretWnd = hWnd;
281         EmacsCaret (TRUE);
282     }
283     else {
284 	hCaretWnd = hWnd;
285 	UpdateEmacsCaretPos ();
286     }
287 } /* MoveEmacsCaret */
288 
289 /* ShowEmacsCaret:  shows or hides the caret used by emacs */
290 /* ==============                                          */
291 
ShowEmacsCaret(BOOL Show)292 void FAR PASCAL ShowEmacsCaret (BOOL Show)
293 
294 /* this function is used to make the caret visible only when waiting for
295    user input */
296 {
297     if (Show) {
298         if (!CaretVisible) {
299             if (!IsIconic (hCaretWnd)) ShowCaret (hCaretWnd);
300         }
301 	++CaretVisible;
302     }
303     else {
304 	--CaretVisible;
305         if (!CaretVisible) {
306             HideCaret (NULL);
307         }
308     }
309 } /* ShowEmacsCaret */
310 
311 /* InMessageLine:   non-zero if caret currently in the message line */
312 /* =============                                                    */
313 
InMessageLine(void)314 BOOL FAR PASCAL InMessageLine (void)
315 {
316     return (hCaretWnd == hFrameWnd);
317 } /* InMessageLine */
318 
319 /* CellToClient:    converts character cell coordinates into client coordinates */
320 /* ============                                                                 */
321 
CellToClient(HWND hWnd,POINT Cell,LPPOINT Client)322 void FAR PASCAL CellToClient (HWND hWnd, POINT Cell, LPPOINT Client)
323 
324 /* The resulting Client coordinates indicate the upper left pixel one
325    HalfLeadingY above the character cell */
326 {
327     Client->x = (Cell.x * EmacsCM.SizeX) + EmacsCM.OffsetX;
328     if (hWnd == hFrameWnd ) {
329         RECT    Rect;
330 
331         GetClientRect (hWnd, &Rect);
332         Client->y = (Rect.bottom - EmacsCM.MLHeight) + EmacsCM.OffsetY +
333                     GetSystemMetrics (SM_CYBORDER);
334     }
335     else Client->y = (Cell.y * (EmacsCM.SizeY + EmacsCM.LeadingY)) +
336                      EmacsCM.OffsetY;
337 } /* CellToClient */
338 
339 /* ClientToCell:    converts client coordinates into character cell coordinates */
340 /* ============                                                                 */
341 
ClientToCell(HWND hWnd,POINT Client,LPPOINT Cell)342 void FAR PASCAL ClientToCell (HWND hWnd, POINT Client, LPPOINT Cell)
343 
344 /* The area associated with a Cell is the character cell itself, plus
345    the HalfLeadingY-high areas above and under the cell */
346 {
347     int     MaxCol, MaxRow;
348 
349     if (hWnd == hFrameWnd) {    /* message line case */
350         MaxCol = MLSIZE - 1;
351 	MaxRow = 0;
352     }
353     else {                      /* screen case */
354         register SCREEN *sp;
355 
356         sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
357         MaxCol = sp->s_ncol - 1;
358         MaxRow = sp->s_nrow - 1;
359     }
360     if ((Cell->x = (Client.x - EmacsCM.OffsetX) /
361                    EmacsCM.SizeX) < 0) Cell->x = 0;
362     else Cell->x = min (Cell->x, MaxCol);
363     if ((Cell->y = (Client.y - EmacsCM.OffsetY) /
364                    (EmacsCM.SizeY + EmacsCM.LeadingY)) < 0) Cell->y = 0;
365     else Cell->y = min (Cell->y, MaxRow);
366 } /* ClientToCell */
367 
368 /* GetMinMaxInfo:  processes the WM_GETMINMAXINFO message for a screen */
369 /* =============                                                       */
370 
GetMinMaxInfo(HWND hWnd,LPPOINT rgpt)371 void FAR PASCAL GetMinMaxInfo (HWND hWnd, LPPOINT rgpt)
372 {
373     if (InternalRequest) return;	/* none of our business */
374 
375     if (notquiescent) {
376 	/* forbid all size changes */
377 	RECT    Rect;
378 
379 	GetWindowRect (hWnd, &Rect);
380 	rgpt[1].x = Rect.right - Rect.left;
381 	rgpt[1].y = Rect.bottom - Rect.top;
382 	rgpt[2] = *(POINT*)&Rect.left;
383 	rgpt[3] = rgpt[1];
384 	rgpt[4] = rgpt[1];
385     }
386     else {                  /* compute minimum tracking size */
387         int     X, Y;
388 
389         /* minimum displayed text width = 3 */
390         /* minimum displayed text  height = 10 */
391         MinimumClientSize (hWnd, term.t_margin, 2, &X, &Y);
392         rgpt[3].x = X + (2 * GetSystemMetrics (SM_CXFRAME)) +
393                     GetSystemMetrics (SM_CXVSCROLL);
394         rgpt[3].y = Y + GetSystemMetrics (SM_CYCAPTION) +
395                     (2 * GetSystemMetrics (SM_CYFRAME)) +
396                     GetSystemMetrics (SM_CYHSCROLL);
397     }
398 } /* GetMinMaxInfo */
399 
400 /* ScrReSize:    processes the WM_SIZE message */
401 /* =========                                   */
402 
ScrReSize(HWND hWnd,UINT wParam,WORD cx,WORD cy)403 BOOL FAR PASCAL ScrReSize (HWND hWnd, UINT wParam, WORD cx, WORD cy)
404 
405 /* returns TRUE only if real resizing performed */
406 {
407     BOOL    ChgWidth, ChgHeight;
408 
409     if ((wParam != SIZENORMAL) && (wParam != SIZEFULLSCREEN)) {
410         return FALSE;
411     }
412     ChgWidth = (cx != GetWindowWord (hWnd, GWW_SCRCX));
413     ChgHeight = (cy != GetWindowWord (hWnd, GWW_SCRCY));
414     if (!ChgWidth && !ChgHeight) return FALSE;
415 
416     SetWindowWord (hWnd, GWW_SCRCX, cx);
417     SetWindowWord (hWnd, GWW_SCRCY, cy);
418     if (!InternalRequest) {
419         SCREEN  *TopScreen;
420 
421         InternalRequest = TRUE;
422         TopScreen = first_screen;
423 	select_screen ((SCREEN *)GetWindowLong (hWnd, GWL_SCRPTR), FALSE);
424 	if (ChgWidth) {
425             newwidth (TRUE, DisplayableColumns (hWnd, cx, &EmacsCM));
426         }
427 	if (ChgHeight) {
428 	    newsize (TRUE, DisplayableRows (hWnd, cy, &EmacsCM));
429 	}
430 	select_screen (TopScreen, FALSE);
431 	update (FALSE);
432 	InternalRequest = FALSE;
433     }
434     return TRUE;
435 } /* ScrReSize */
436 
437 /* ScrPaint:   processes WM_PAINT messages for emacs screens */
438 /* ========                                                  */
439 
ScrPaint(HWND hWnd)440 void FAR PASCAL ScrPaint (HWND hWnd)
441 {
442     SCREEN  *sp;
443     PAINTSTRUCT ps;
444     HANDLE  hPrevFont;
445     RECT    Rect;
446     int     Row, BottomRow;
447     int     Col;
448     int     Length;
449 
450     /* note that if the WM_PAINT is not passed through the message loop
451        (as would hapen with use of UpdateWindow), it is possible to
452        attempt repainting while defferupdate is TRUE which means update
453        has been deffered */
454 
455     BeginPaint (hWnd, &ps);
456     sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
457 
458     /*-calculate the row/col loop control variables and normalize the
459        coordinates of the first line's rectangle into Rect */
460     CopyRect (&Rect, &ps.rcPaint);
461     ClientToCell (hWnd, *(POINT *)&Rect.left, (POINT *)&Rect.left);
462     Col = Rect.left;
463     Row = Rect.top;
464     --Rect.right;       /* in rectangle conventions, */
465     --Rect.bottom;      /* the lower/right border is excluded */
466     ClientToCell (hWnd, *(POINT *)&Rect.right, (POINT *)&Rect.right);
467     ++Rect.right;
468     Length = Rect.right - Col;
469     BottomRow = Rect.bottom;
470     Rect.bottom = Rect.top + 1;
471     CellToClient (hWnd, *(POINT *)&Rect.left, (POINT *)&Rect.left);
472     CellToClient (hWnd, *(POINT *)&Rect.right, (POINT *)&Rect.right);
473     /* Rect now contains a bounding rectangle for the 1st row. This will
474        be used to paint the row's background and will be moved down one
475        row's height at each iteration of the painting loop */
476 
477     /*-loop from top to bottom, writing one line at a time */
478     hPrevFont = SelectFont (ps.hdc, hEmacsFont);
479     do {
480 	VIDEO   *vp;
481 
482 	vp = sp->s_physical[Row];
483 	if (!(vp->v_flag & VFNEW)) {    /* valid contents */
484 	    COLORREF FColor, BColor;
485             POINT   RevPt;
486             int     i;
487 	    RECT    BoundingRect;   /* clipping rectangle */
488             int     Boundary[4];    /* horizontal paint boundaries:
489                                        [0] to [1] is painted in normal video,
490                                        [1] to [2] is painted in reverse video,
491                                        [2] to [3] is painted in normal video */
492 
493 	    CopyRect (&BoundingRect, &ps.rcPaint);
494 	    if (Row > 0) BoundingRect.top = Rect.top;
495 	    if (Row < BottomRow) BoundingRect.bottom = Rect.bottom;
496 
497 	    Boundary[0] = BoundingRect.left;
498 	    Boundary[3] = BoundingRect.right;
499 	    if (vp->v_right) {  /* there is a reverse video area */
500 	        if (vp->v_left <= 0) {
501 	            /* special case: flush to the border */
502                     Boundary[1] = 0;
503                 }
504                 else {
505                     RevPt.y = 0;
506 	            RevPt.x = vp->v_left;
507 	            CellToClient (hWnd, RevPt, &RevPt);
508 	            Boundary[1] = max(BoundingRect.left,RevPt.x);
509 	        }
510 	        RevPt.y = 0;
511 	        RevPt.x = vp->v_right;
512 	        CellToClient (hWnd, RevPt, &RevPt);
513 	        Boundary[2] = min(BoundingRect.right,RevPt.x);
514 	    }
515 	    else {  /* only normal video */
516 	        Boundary[1] = Boundary [0];
517 	        Boundary[2] = Boundary [0];
518 	    }
519 
520 	    /*-Paint the row in up to three parts, between the computed
521                boundaries (reverse video is applied between [1] and [2] */
522 	    for (i = 0; i < 3; i++) if (Boundary[i] < Boundary [i+1]) {
523 	        BoundingRect.left = Boundary[i];
524 	        BoundingRect.right = Boundary[i+1];
525 #if     COLOR
526 	        if (ColorDisplay) {
527 	            FColor = EmacsPalette[i == 1 ?
528                                           vp->v_bcolor : vp->v_fcolor];
529 	            BColor = EmacsPalette[i == 1 ?
530                                           vp->v_fcolor : vp->v_bcolor];
531                 }
532                 else {  /* monochrome display */
533 #else
534                 {
535 #endif
536 		    if (i == 1) {   /* "reverse" video */
537 		        FColor = GetSysColor (COLOR_HIGHLIGHTTEXT);
538 		        BColor = GetSysColor (COLOR_HIGHLIGHT);
539 		    }
540 		    else {
541 		        FColor = GetSysColor (COLOR_WINDOWTEXT);
542 		        BColor = GetSysColor (COLOR_WINDOW);
543 		    }
544 	        }
545 	        SetBkColor (ps.hdc, BColor);
546 	        SetTextColor (ps.hdc, FColor);
547 	        ExtTextOut (ps.hdc,
548 		            Rect.left, Rect.top + EmacsCM.HalfLeadingY,
549 			    ETO_OPAQUE | ETO_CLIPPED, &BoundingRect,
550 			    &vp->v_text[Col],
551 			    Length, NULL);
552 	    }
553 	}
554 	Rect.top = Rect.bottom;
555 	Rect.bottom += EmacsCM.SizeY + EmacsCM.LeadingY;
556     } while (++Row <= BottomRow);
557 
558     SelectObject (ps.hdc, hPrevFont);
559 EndScrPaint:
560     EndPaint (hWnd, &ps);
561 } /* ScrPaint */
562 
563 /* MLPaint: processes WM_PAINT messages for the Message Line */
564 /* =======                                                   */
565 
566 void FAR PASCAL MLPaint (void)
567 {
568     PAINTSTRUCT ps;
569     HANDLE  hPrev, hPen;
570     RECT    Rect;
571     POINT   Client;
572 
573     BeginPaint (hFrameWnd, &ps);
574 
575     /*-draw the text portion targetted for repaint */
576     hPrev = SelectFont (ps.hdc, hEmacsFont);
577     ClientToCell (hFrameWnd, *(POINT*)&ps.rcPaint.left, (POINT*)&Rect.left);
578     ClientToCell (hFrameWnd, *(POINT*)&ps.rcPaint.right, (POINT*)&Rect.right);
579     CellToClient (hFrameWnd, *(POINT*)&Rect.left, &Client);
580     ExtTextOut (ps.hdc,
581                 Client.x, Client.y + EmacsCM.HalfLeadingY,
582                 ETO_OPAQUE, &ps.rcPaint,
583                 &MLBuf[Rect.left],
584                 Rect.right - Rect.left + 1, NULL);
585     SelectObject (ps.hdc, hPrev);
586 
587     /*-draw the separation line at top of message line */
588     hPen = CreatePen (PS_SOLID, GetSystemMetrics (SM_CYBORDER), RGB(0,0,0));
589     hPrev = SelectObject (ps.hdc, hPen);
590     GetClientRect (hFrameWnd, &Rect);
591     Rect.top = Rect.bottom - EmacsCM.MLHeight;
592 #if WINDOW_MSWIN32
593     MoveToEx (ps.hdc, 0, Rect.top, NULL);
594 #else
595     MoveTo (ps.hdc, 0, Rect.top);
596 #endif
597     LineTo (ps.hdc, Rect.right, Rect.top);
598     SelectObject (ps.hdc, hPrev);
599     DeleteObject (hPen);
600 
601     EndPaint (hFrameWnd, &ps);
602 } /* MLPaint */
603 
604 /* spal:    set palette from $palette string */
605 /* ====                                      */
606 
607 PASCAL spal (char *pstr)
608 {
609 #if     COLOR
610     int     pal;	/* current palette position */
611 
612     for (pal = 0; pal < 16; pal++) {
613 	DWORD   clr;	/* current color value */
614 	int     i;
615         unsigned char n;
616 
617 	if (*pstr== 0) break;
618 	clr = 0;
619 	for (i = 0; i < 3; i++) if (*pstr) {
620 	    n = *pstr++ - '0';
621 	    if (n >= 8) n = 255;
622 	    else n *= 32;
623 	    clr |= (DWORD)n << (i * 8);
624 	}
625 	EmacsPalette[pal] = clr;
626     }
627 #endif
628     return 0;
629 } /* spal */
630