xref: /plan9-contrib/sys/src/cmd/gs/src/gspmdrv.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 /* Copyright (C) 1994 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gspmdrv.c,v 1.2 2000/09/19 19:00:31 lpd Exp $ */
20 /* Presentation Manager driver for Ghostscript */
21 /* Written by Russell Lang */
22 
23 /* To display output from os2pm driver: */
24 /*   gspmdrv -d id_string */
25 /* To display BMP file (used for testing display code) */
26 /*   gspmdrv -b filename.bmp */
27 
28 #define INCL_DOS
29 #define INCL_WIN
30 #define INCL_GPI
31 #include <os2.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/emxload.h>
36 #include "gspmdrv.h"
37 #include "gdevpm.h"
38 
39 #ifndef min
40 #define min(x,y)  ( (x) < (y) ? (x) : (y) )
41 #endif
42 #ifndef max
43 #define max(x,y)  ( (x) > (y) ? (x) : (y) )
44 #endif
45 
46 HEV update_event_sem;
47 HMTX bmp_mutex_sem;
48 
49 /* bitmap details */
50 typedef struct tagBM {
51     BOOL valid;
52     BOOL old_bmp;		/* bitmap type */
53     PBITMAPINFO2 pbmi;		/* pointer to bitmap info */
54     PBYTE bits;			/* pointer to bitmap bits */
55     int width;
56     int height;
57     int planes;
58     int depth;
59     int palsize;
60     int palimportant;
61     int old_width;
62     int old_height;
63     int old_planes;
64     int old_depth;
65     int old_palsize;
66     int old_palimportant;
67 } BMAP;
68 
69 typedef struct tagDISPLAY {
70     LONG planes;
71     LONG bitcount;
72     LONG hasPalMan;		/* Palette Manager */
73     BOOL hpal_exists;
74     HPAL hpal;
75 } DISPLAY;
76 
77 /* options that are saved in INI file */
78 typedef struct tagOPTIONS {
79     POINTL img_origin;
80     POINTL img_size;
81     BOOL img_max;
82 } OPTIONS;
83 
84 #define CW_USEDEFAULT 32768
85 
86 
87 BMAP bitmap;
88 DISPLAY display;
89 OPTIONS option;
90 PBYTE bbuffer;			/* for BMP file display */
91 POINTL scroll_pos;		/* not used *//* not used */
92 ULONG os_version;
93 char *section = "Ghostscript Image";
94 
95 HAB hab;			/* Anchor Block */
96 HWND hwnd_frame;
97 HWND hwnd_bmp;
98 HWND hwnd_gs;			/* window handle for CMD.EXE that started gs */
99 TID update_tid;
100 
101 #define WM_GSUPDATE WM_USER+1
102 #define SB_TOP 20
103 #define SB_BOTTOM 21
104 
105 
106 MRESULT EXPENTRY ClientWndProc(HWND, ULONG, MPARAM, MPARAM);
107 MRESULT EXPENTRY AboutDlgProc(HWND, ULONG, MPARAM, MPARAM);
108 APIRET init_window(void);
109 void fix_sysmenu(HWND);
110 APIRET restore_window_position(SWP * pswp);
111 BOOL scan_bitmap(BMAP * pbm);
112 void read_profile(void);
113 void write_profile(void);
114 APIRET init_display(int argc, char *argv[]);
115 APIRET init_bitmap(int argc, char *argv[]);
116 void copy_clipboard(void);
117 HBITMAP make_bitmap(BMAP * pbm, ULONG left, ULONG bottom, ULONG right, ULONG top, ULONG depth);
118 
119 void
120 debugbeep(int type)
121 {
122 #ifdef DEBUG
123     int i;
124 
125 /* current debug beeps are: */
126 /* 1. Null handle PS */
127 /* 2. make_bitmap() failed */
128 /* 3. GpiDrawBits() or WinDrawBitmap() failed */
129 /* 4. Null handle PS from WinBeginPaint() */
130     for (i = 0; i < type; i++) {
131 	DosBeep(400 + 100 * type, 50);
132 	DosSleep(50);
133     }
134 #endif
135 }
136 
137 
138 /* display message */
139 int
140 message_box(char *str, int icon)
141 {
142     return WinMessageBox(HWND_DESKTOP, hwnd_frame ? hwnd_frame : HWND_DESKTOP,
143 			 str, "gspmdrv.exe", 0, icon | MB_MOVEABLE | MB_OK);
144 }
145 
146 void
147 error_message(char *str)
148 {
149     WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, str, "gspmdrv.exe", 0, MB_MOVEABLE | MB_ICONHAND | MB_OK);
150     WinPostMsg(hwnd_frame, WM_QUIT, MPFROMLONG(0), MPFROMLONG(0));
151 }
152 
153 /* Update thread */
154 /* This thread waits for the update event semaphore from gs.exe */
155 /* then generates a WM_PAINT message for the bitmap */
156 /* This thread must NOT call C library functions */
157 VOID APIENTRY
158 update_func(ULONG unused)
159 {
160     APIRET rc;
161     BOOL flag;
162     ULONG count;
163 
164     unused = unused;		/* to shut up warning */
165     while (!DosQueryEventSem(update_event_sem, &count)) {
166 	/* loop while semaphore exists */
167 	DosWaitEventSem(update_event_sem, SEM_INDEFINITE_WAIT);
168 	DosResetEventSem(update_event_sem, &count);
169 	WinPostMsg(hwnd_bmp, WM_GSUPDATE, MPFROMLONG(0), MPFROMLONG(0));
170     }
171 }
172 
173 VOID APIENTRY
174 exit_func(ULONG code)
175 {
176     write_profile();
177     DosCloseEventSem(update_event_sem);
178     DosCloseMutexSem(bmp_mutex_sem);
179     DosFreeMem((PVOID) bitmap.pbmi);
180     DosExitList(EXLST_EXIT, 0);
181     code = code;		/* to shut up warning */
182 }
183 
184 void
185 find_hwnd_gs(char *gsid)
186 {
187     ULONG ulCount;
188     ULONG ulLength;
189     ULONG pBase;
190     ULONG cbBuf;
191     PSWBLOCK pswblk;
192     PSWENTRY pswentry;
193     SWCNTRL *pswc;
194     int i;
195     ULONG pid;
196     char buf[256];
197     char *p, *s;
198     PTIB pptib;
199     PPIB pppib;
200 
201     /* extract gs pid from command line id */
202     strcpy(buf, gsid);
203     for (p = buf; *p && *p != '_'; p++);
204     *p++ = '\0';
205     s = p;
206     for (p = buf; *p && *p != '_'; p++);
207     *p = '\0';
208     pid = atoi(s);		/* pid is Process ID of CMD.EXE that started gsos2.exe */
209 
210     ulCount = WinQuerySwitchList(hab, NULL, 0);		/* get num of items */
211     cbBuf = (ulCount * sizeof(SWENTRY)) + sizeof(HSWITCH);
212     pswblk = (PSWBLOCK) malloc(cbBuf + 32768);
213     ulCount = WinQuerySwitchList(hab, pswblk, cbBuf);	/* get num of items */
214     for (i = 0; i < ulCount; i++) {
215 	pswentry = &pswblk->aswentry[i];
216 	pswc = &pswentry->swctl;
217 	if (pid == pswc->idProcess)
218 	    hwnd_gs = pswc->hwnd;	/* save window handle */
219     }
220 }
221 
222 
223 int
224 main(int argc, char *argv[])
225 {
226     HMQ hand_mq;		/* message queue */
227     QMSG q_mess;		/* message queue */
228     APIRET rc = 0;
229 
230 
231     hab = WinInitialize(0);	/* Get the Anchor Block */
232 
233     hand_mq = WinCreateMsgQueue(hab, 0);	/* start a queue */
234 
235     if (argc < 2) {
236 	rc = 1;
237 	error_message("Usage: gspmdrv -d id_string");
238     }
239     if (!rc) {
240 	if (strcmp(argv[1], "-d") == 0) {
241 	    rc = init_display(argc, argv);
242 	} else if (strcmp(argv[1], "-b") == 0) {
243 	    rc = init_bitmap(argc, argv);
244 	} else {
245 	    rc = 1;
246 	    error_message("Usage: gspmdrv -d id_string");
247 	}
248     }
249     if (!rc) {
250 	rc = DosCreateThread(&update_tid, update_func, 0, 0, 8192);
251 	if (rc)
252 	    error_message("Failed to create update thread");
253     }
254     if (!rc)
255 	rc = init_window();
256 
257     if (!rc)
258 	WinShowWindow(hwnd_frame, TRUE);
259 
260     if (!rc) {
261 	/* keep gspmdrv.exe in memory for number of minutes specified in */
262 	/* environment variable GS_LOAD */
263 	_emxload_env("GS_LOAD");
264     }
265     DosExitList(EXLST_ADD, exit_func);
266 
267     /* message loop */
268     while (!rc && WinGetMsg(hab, &q_mess, 0L, 0, 0))
269 	WinDispatchMsg(hab, &q_mess);
270 
271     /* Shut down the application window and queue */
272     DosKillThread(update_tid);
273     WinDestroyWindow(hwnd_frame);
274     WinDestroyMsgQueue(hand_mq);
275     WinTerminate(hab);
276     return rc;
277 }
278 
279 APIRET
280 init_window()
281 {
282     ULONG version[3];
283     SWP swp;
284     APIRET rc = 0;
285     ULONG flFlags;		/* Window frame definition */
286     unsigned char class[] = "gspmdrvClass";	/* class name */
287 
288     if (DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_REVISION, &version, sizeof(version)))
289 	os_version = 201000;	/* a guess */
290     else {
291 	os_version = version[0] * 10000 + version[1] * 100 + version[2];
292     }
293 
294     /* define the frame constants */
295     flFlags = FCF_TITLEBAR |	/* have a title bar */
296 	FCF_SIZEBORDER |	/* have a sizeable window */
297 	FCF_MINMAX |		/* have a min and max button */
298 	FCF_SYSMENU |		/* include a system menu */
299 	FCF_VERTSCROLL |	/* vertical scroll bar */
300 	FCF_HORZSCROLL |	/* horizontal scroll bar */
301 	FCF_TASKLIST |		/* show it in window list */
302 	FCF_ICON;		/* Load icon from resources */
303 
304     /* save SHELL default size and location */
305     rc = WinQueryTaskSizePos(hab, 0, &swp);
306     if (rc)
307 	return rc;
308 
309     read_profile();
310     if ((option.img_size.x == 0) || (option.img_size.y == 0))
311 	option.img_size.x = option.img_size.y = CW_USEDEFAULT;
312 
313     if (!rc) {
314 	HPS ps = WinGetPS(HWND_DESKTOP);
315 	HDC hdc = GpiQueryDevice(ps);
316 
317 	DevQueryCaps(hdc, CAPS_COLOR_PLANES, 1, &display.planes);
318 	DevQueryCaps(hdc, CAPS_COLOR_BITCOUNT, 1, &display.bitcount);
319 	DevQueryCaps(hdc, CAPS_ADDITIONAL_GRAPHICS, 1, &display.hasPalMan);
320 	display.hasPalMan &= CAPS_PALETTE_MANAGER;
321 	WinReleasePS(ps);
322     }
323     if (!rc) {
324 	if (!WinRegisterClass(	/* register this window class */
325 				 hab,	/* anchor block */
326 				 (PSZ) class,	/* class name */
327 				 (PFNWP) ClientWndProc,		/* window function */
328 				 CS_SIZEREDRAW |	/* window style */
329 				 CS_MOVENOTIFY,
330 				 0))	/* no storage */
331 	    exit(1);
332 
333 	hwnd_frame = WinCreateStdWindow(
334 					   HWND_DESKTOP,	/* window type */
335 					   0,	/* frame style is not WS_VISIBLE */
336 					   &flFlags,	/* definitions */
337 					   (PSZ) class,		/* client class */
338 					   (PSZ) "Ghostscript Image",	/* title */
339 					   WS_VISIBLE,	/* client style */
340 					   0,	/* resource module */
341 					   ID_GSPMDRV,	/* resource identifier */
342 					   &hwnd_bmp);	/* pointer to client */
343 
344 	fix_sysmenu(hwnd_frame);
345     }
346     rc = restore_window_position(&swp);
347 
348     return rc;
349 }
350 
351 
352 void
353 write_profile(void)
354 {
355     char profile[64];
356 
357     sprintf(profile, "%d %d", option.img_origin.x, option.img_origin.y);
358     PrfWriteProfileString(HINI_USERPROFILE, section, "Origin", profile);
359     sprintf(profile, "%d %d", option.img_size.x, option.img_size.y);
360     PrfWriteProfileString(HINI_USERPROFILE, section, "Size", profile);
361     sprintf(profile, "%d", option.img_max);
362     PrfWriteProfileString(HINI_USERPROFILE, section, "Maximized", profile);
363 }
364 
365 void
366 read_profile(void)
367 {
368     char profile[64];
369 
370     PrfQueryProfileString(HINI_USERPROFILE, section, "Origin", "", profile, sizeof(profile));
371     if (sscanf(profile, "%d %d", &option.img_origin.x, &option.img_origin.y) != 2) {
372 	option.img_origin.x = CW_USEDEFAULT;
373 	option.img_origin.y = CW_USEDEFAULT;
374     }
375     PrfQueryProfileString(HINI_USERPROFILE, section, "Size", "", profile, sizeof(profile));
376     if (sscanf(profile, "%d %d", &option.img_size.x, &option.img_size.y) != 2) {
377 	option.img_size.x = CW_USEDEFAULT;
378 	option.img_size.y = CW_USEDEFAULT;
379     }
380     PrfQueryProfileString(HINI_USERPROFILE, section, "Maximized", "", profile, sizeof(profile));
381     if (sscanf(profile, "%d", &option.img_max) != 1)
382 	option.img_max = 0;
383 }
384 
385 void
386 fix_sysmenu(HWND hwnd)
387 {
388     MENUITEM mi;
389     HWND hwndSysMenu;
390 
391     if (!WinSendMsg(WinWindowFromID(hwnd, FID_SYSMENU), MM_QUERYITEM,
392 		    MPFROM2SHORT(SC_SYSMENU, TRUE), MPFROMP(&mi))) {
393 	message_box("failed getting system menu handle", 0);
394 	return;
395     }
396     hwndSysMenu = mi.hwndSubMenu;
397     mi.iPosition = MIT_END;
398     mi.afStyle = MIS_SEPARATOR;
399     mi.afAttribute = 0;
400     mi.id = 0;
401     mi.hwndSubMenu = 0;
402     mi.hItem = 0;
403     WinSendMsg(hwndSysMenu, MM_INSERTITEM, MPFROMP(&mi), NULL);
404     mi.afStyle = MIS_TEXT;
405     mi.id = IDM_ABOUT;
406     WinSendMsg(hwndSysMenu, MM_INSERTITEM, MPFROMP(&mi), "About...");
407     mi.id = IDM_COPY;
408     WinSendMsg(hwndSysMenu, MM_INSERTITEM, MPFROMP(&mi), "Copy");
409 }
410 
411 APIRET
412 restore_window_position(SWP * pswp)
413 {
414     SWP swp;
415 
416     swp.fl = SWP_MOVE | SWP_SIZE | SWP_SHOW;
417 
418     if (option.img_max) {
419 	/* Get maximized frame window position and size. */
420 	if (!WinGetMaxPosition(hwnd_frame, &swp))
421 	    return 1;
422 	swp.fl |= SWP_MAXIMIZE;
423     } else if ((option.img_size.x != CW_USEDEFAULT) &&
424 	       (option.img_size.y != CW_USEDEFAULT) &&
425 	       (option.img_origin.y != CW_USEDEFAULT) &&
426 	       (option.img_origin.y != CW_USEDEFAULT)) {
427 	LONG cxClientMax;
428 	LONG cyClientMax;
429 	LONG cyTitleBar;
430 	LONG cxSizeBorder;
431 	LONG cySizeBorder;
432 
433 	/* get maximum client window size */
434 	cxClientMax = WinQuerySysValue(HWND_DESKTOP, SV_CXFULLSCREEN);
435 	cyClientMax = WinQuerySysValue(HWND_DESKTOP, SV_CYFULLSCREEN);
436 	cyTitleBar = WinQuerySysValue(HWND_DESKTOP, SV_CYTITLEBAR);
437 	cxSizeBorder = WinQuerySysValue(HWND_DESKTOP, SV_CXSIZEBORDER);
438 	cySizeBorder = WinQuerySysValue(HWND_DESKTOP, SV_CYSIZEBORDER);
439 	cyClientMax += cyTitleBar;
440 
441 	/* Make sure x origin is within display boundaries */
442 	swp.x = option.img_origin.x;
443 	if (swp.x < -cxSizeBorder)
444 	    swp.x = 0;
445 
446 	/* Make sure window isn't too wide, or negative value */
447 	swp.cx = option.img_size.x;
448 	if (swp.cx >= cxClientMax || swp.cx < 0) {
449 	    swp.cx = cxClientMax;
450 	    swp.x = 0;
451 	}
452 	if ((swp.x + swp.cx) > (cxClientMax + cxSizeBorder))
453 	    swp.x = cxClientMax + cxSizeBorder - swp.cx;
454 
455 	/* Make sure y origin is within display boundaries */
456 	swp.y = option.img_origin.y;
457 	if (swp.y < -cySizeBorder)
458 	    swp.y = 0;
459 
460 	/* Make sure window isn't too high, or negative value */
461 	swp.cy = option.img_size.y;
462 	if (swp.cy > cyClientMax || swp.cy < 0) {
463 	    swp.cy = cyClientMax;
464 	    swp.y = 0;
465 	}
466 	if ((swp.y + swp.cy) > (cyClientMax + cySizeBorder))
467 	    swp.y = cyClientMax + cySizeBorder - swp.cy;
468     } else {			/* No saved position -- use supplied position */
469 	swp = *pswp;
470 	option.img_origin.x = swp.x;
471 	option.img_origin.y = swp.y;
472 	option.img_size.x = swp.cx;
473 	option.img_size.y = swp.cy;
474 	option.img_max = FALSE;
475 	swp.fl = SWP_MOVE | SWP_SIZE | SWP_SHOW;
476     }
477 
478     if (hwnd_gs)
479 	swp.fl |= SWP_ZORDER;
480     /* Position and size this frame window */
481     if (!WinSetWindowPos(hwnd_frame, hwnd_gs,
482 			 swp.x, swp.y, swp.cx, swp.cy, swp.fl))
483 	return 1;
484     return 0;
485 }
486 
487 APIRET
488 init_display(int argc, char *argv[])
489 {
490     char buf[256];
491     char name[256];
492     APIRET rc = 0;
493 
494     if (argc != 3) {
495 	rc = 1;
496 	error_message("Usage: gspmdrv -d id_string");
497     }
498     find_hwnd_gs(argv[2]);
499 
500     if (!rc) {
501 	sprintf(name, SHARED_NAME, argv[2]);
502 	rc = DosGetNamedSharedMem((PVOID *) & bitmap.pbmi, name, PAG_READ | PAG_WRITE);
503 	if (rc) {
504 	    sprintf(buf, "Failed to open: bmp shared memory \"%s\" rc = %d", argv[0], rc);
505 	    error_message(buf);
506 	}
507     }
508     if (!rc) {
509 	sprintf(name, SYNC_NAME, argv[2]);
510 	rc = DosOpenEventSem(name, &update_event_sem);
511 	if (rc) {
512 	    sprintf(buf, "Failed to open: update event semaphore \"%s\" rc = %d", argv[1], rc);
513 	    error_message(buf);
514 	}
515     }
516     if (!rc) {
517 	sprintf(name, MUTEX_NAME, argv[2]);
518 	rc = DosOpenMutexSem(name, &bmp_mutex_sem);
519 	if (rc) {
520 	    sprintf(buf, "Failed to open: bmp mutex semaphore \"%s\" rc = %d", argv[1], rc);
521 	    error_message(buf);
522 	}
523     }
524     if (!rc) {
525 	scan_bitmap(&bitmap);
526 	bitmap.valid = TRUE;
527     }
528     return rc;
529 }
530 
531 
532 APIRET
533 init_bitmap(int argc, char *argv[])
534 {
535     char buf[256];
536     APIRET rc = 0;
537     HFILE hf;
538     ULONG action, count, length;
539     PBITMAPFILEHEADER2 pbmfh;
540 
541     if (argc != 3)
542 	return 1;		/* error - no filename */
543 
544     /* open bitmap */
545     if ((rc = DosOpen(argv[2], &hf, &action, 0, FILE_NORMAL, FILE_OPEN,
546 		      OPEN_ACCESS_READONLY | OPEN_SHARE_DENYREADWRITE, 0))
547 	!= (APIRET) 0) {
548 	sprintf(buf, "Error opening: %s", argv[2]);
549 	error_message(buf);
550 	return rc;
551     }
552     rc = DosSetFilePtr(hf, 0, FILE_END, &length);
553     if (rc) {
554 	sprintf(buf, "failed seeking to EOF: error = %d", rc);
555 	error_message(buf);
556 	return rc;
557     }
558     rc = DosSetFilePtr(hf, 0, FILE_BEGIN, &count);
559     if (rc) {
560 	sprintf(buf, "failed seeking to BOF: error = %d", rc);
561 	error_message(buf);
562 	return rc;
563     };
564 
565     /* allocate memory for bitmap */
566     if ((rc = DosAllocMem((PPVOID) & bbuffer, length, PAG_READ | PAG_WRITE | PAG_COMMIT))
567 	!= (APIRET) 0) {
568 	sprintf(buf, "failed allocating memory");
569 	error_message(buf);
570 	return rc;
571     }
572     rc = DosRead(hf, bbuffer, length, &count);
573     DosClose(hf);
574     if (rc) {
575 	sprintf(buf, "failed reading bitmap, error = %u, count = %u", rc, count);
576 	error_message(buf);
577 	return rc;
578     }
579     /* extract some info about bitmap */
580     pbmfh = (PBITMAPFILEHEADER2) bbuffer;
581     bitmap.pbmi = (PBITMAPINFO2) (&pbmfh->bmp2);
582 
583     scan_bitmap(&bitmap);
584     bitmap.valid = TRUE;
585 
586     sprintf(buf, "bitmap width = %d, height = %d", bitmap.width, bitmap.height);
587     message_box(buf, 0);
588     return rc;
589 }
590 
591 #define MAX_PAL_SIZE 256
592 void
593 make_palette(BMAP * pbm)
594 {
595     ULONG tbl[MAX_PAL_SIZE];
596     PRGB2 palptr = (PRGB2) ((PBYTE) (pbm->pbmi) + pbm->pbmi->cbFix);
597     RGB *old_palptr = (RGB *) palptr;
598     int palcount = pbm->palimportant;
599     int i;
600     BOOL old_bmp = (pbm->pbmi->cbFix == sizeof(BITMAPINFOHEADER));
601 
602     if (old_bmp) {
603 	for (i = 0; i < palcount; i++) {
604 	    tbl[i] = (old_palptr->bRed << 16) + (old_palptr->bGreen << 8) + (old_palptr->bBlue);
605 	    palptr++;
606 	}
607     } else {
608 	for (i = 0; i < palcount; i++) {
609 	    tbl[i] = (palptr->bRed << 16) + (palptr->bGreen << 8) + (palptr->bBlue);
610 	    palptr++;
611 	}
612     }
613     if (display.hpal_exists)
614 	GpiDeletePalette(display.hpal);
615     display.hpal = GpiCreatePalette(hab, 0L, LCOLF_CONSECRGB, palcount, tbl);
616     display.hpal_exists = TRUE;
617 }
618 
619 
620 
621 /* scan bitmap */
622 /* update bitmap structure */
623 /* return value is TRUE if bitmap dimension has changed */
624 BOOL
625 scan_bitmap(BMAP * pbm)
626 {
627     PBITMAPINFO2 pbmi = pbm->pbmi;
628     PBITMAPINFO old_pbmi = (PBITMAPINFO) pbmi;
629     BOOL old_bmp = (pbmi->cbFix == sizeof(BITMAPINFOHEADER));
630 
631     if (old_bmp) {
632 	/* it is a BITMAPINFO */
633 	switch (old_pbmi->cBitCount) {
634 	    case 24:
635 		pbm->palsize = 0;
636 		break;
637 	    case 8:
638 		pbm->palsize = 256;
639 		break;
640 	    case 4:
641 		pbm->palsize = 16;
642 		break;
643 	    case 1:
644 		pbm->palsize = 2;
645 		break;
646 	    default:
647 		pbm->valid = FALSE;
648 		error_message("scan_bitmap: wrong number of bits");	/* panic */
649 		return FALSE;
650 	}
651 	pbm->palimportant = pbm->palsize;
652 	pbm->palsize = pbm->palsize * sizeof(RGB);
653 	pbm->bits = (PBYTE) old_pbmi + old_pbmi->cbFix + pbm->palsize;
654 	pbm->width = old_pbmi->cx;
655 	pbm->height = old_pbmi->cy;
656 	pbm->planes = old_pbmi->cPlanes;
657 	pbm->depth = old_pbmi->cBitCount;
658     } else {
659 	/* it is a BITMAPINFO2 */
660 	switch (pbmi->cBitCount) {
661 	    case 24:
662 		pbm->palsize = 0;
663 		break;
664 	    case 8:
665 		pbm->palsize = 256;
666 		break;
667 	    case 4:
668 		pbm->palsize = 16;
669 		break;
670 	    case 1:
671 		pbm->palsize = 2;
672 		break;
673 	    default:
674 		pbm->valid = FALSE;
675 		error_message("scan_bitmap: wrong number of bits");	/* panic */
676 		return FALSE;
677 	}
678 	if ((pbmi->cbFix > (&(pbmi->cclrUsed) - &(pbmi->cbFix)))
679 	    && (pbmi->cclrUsed != 0) && (pbmi->cBitCount != 24))
680 	    pbm->palsize = pbmi->cclrUsed;
681 	pbm->palimportant = pbm->palsize;
682 	if ((pbmi->cbFix > (&(pbmi->cclrImportant) - &(pbmi->cbFix)))
683 	    && (pbmi->cclrImportant != 0) && (pbmi->cBitCount != 24))
684 	    pbm->palimportant = pbmi->cclrImportant;
685 	pbm->palsize = pbm->palsize * sizeof(RGB2);
686 	pbm->bits = (PBYTE) pbmi + pbmi->cbFix + pbm->palsize;
687 	pbm->width = pbmi->cx;
688 	pbm->height = pbmi->cy;
689 	pbm->planes = pbmi->cPlanes;
690 	pbm->depth = pbmi->cBitCount;
691     }
692 
693     if ((pbm->palsize != pbm->old_palsize) || (pbm->palimportant != pbm->old_palimportant)) {
694 	if ((pbm->depth == 8) && display.hasPalMan)
695 	    make_palette(pbm);
696 	pbm->old_palimportant = pbm->palimportant;
697     }
698     if ((pbm->width == pbm->old_width) &&
699 	(pbm->height == pbm->old_height) &&
700 	(pbm->planes == pbm->old_planes) &&
701 	(pbm->depth == pbm->old_depth) &&
702 	(pbm->palsize == pbm->old_palsize) &&
703 	(pbm->old_bmp == old_bmp))
704 	return FALSE;
705 
706     /* bitmap has changed */
707     pbm->old_width = pbm->width;
708     pbm->old_height = pbm->height;
709     pbm->old_planes = pbm->planes;
710     pbm->old_depth = pbm->depth;
711     pbm->old_palsize = pbm->palsize;
712     pbm->old_bmp = old_bmp;
713     return TRUE;
714 }
715 
716 
717 void
718 update_scroll_bars(void)
719 {
720     /* Cause update of scroll bars etc. */
721     SWP swp;
722 
723     WinQueryWindowPos(hwnd_bmp, &swp);
724     WinSendMsg(hwnd_bmp, WM_SIZE, MPFROM2SHORT(swp.cx, swp.cy), MPFROM2SHORT(swp.cx, swp.cy));
725 }
726 
727 
728 /* copy bitmap to the clipboard */
729 void
730 copy_clipboard(void)
731 {
732     HBITMAP hbmp;
733 
734     if (!bitmap.valid) {
735 	message_box("Cannot copy to clipboard:\nNo Bitmap displayed", 0);
736 	return;
737     }
738     if (WinOpenClipbrd(hab)) {
739 	/* get bmp mutex to stop gs.exe changing bitmap while we copy it */
740 	DosRequestMutexSem(bmp_mutex_sem, 10000);
741 	if (scan_bitmap(&bitmap)) {
742 	    /* bitmap has changed */
743 	    update_scroll_bars();
744 	}
745 	hbmp = make_bitmap(&bitmap, 0, 0, bitmap.width, bitmap.height, bitmap.depth);
746 	if (hbmp) {
747 	    WinEmptyClipbrd(hab);
748 	    WinSetClipbrdData(hab, (ULONG) hbmp, CF_BITMAP, CFI_HANDLE);
749 	}
750 	DosReleaseMutexSem(bmp_mutex_sem);
751 	WinCloseClipbrd(hab);
752     }
753 }
754 
755 
756 HBITMAP
757 make_bitmap(BMAP * pbm, ULONG left, ULONG bottom, ULONG right, ULONG top, ULONG depth)
758 {
759     HDC hdc = DEV_ERROR, hdcMem = DEV_ERROR;
760     HPS hps = GPI_ERROR;
761     HBITMAP hbmp = GPI_ERROR, hbmr = HBM_ERROR;
762     SIZEL sizePS;
763     BITMAPINFOHEADER2 bmih;
764 
765     if ((left == right) || (bottom == top))
766 	return (HBITMAP) NULL;
767 
768     if (right > pbm->width)
769 	right = pbm->width;
770     if (left > pbm->width)
771 	left = 0;
772     if (top > pbm->height)
773 	top = pbm->height;
774     if (bottom > pbm->height)
775 	bottom = 0;
776 
777     memset(&bmih, 0, sizeof(bmih));
778     bmih.cbFix = sizeof(BITMAPINFOHEADER2);
779     bmih.cx = right - left;
780     bmih.cy = top - bottom;
781     bmih.cPlanes = 1;
782     bmih.cBitCount = depth;
783 
784     /* create memory DC compatible with screen */
785     hdcMem = DevOpenDC(hab, OD_MEMORY, "*", 0L, NULL, NULLHANDLE);
786 
787     sizePS.cx = right - left;
788     sizePS.cy = top - bottom;
789     if (hdcMem != DEV_ERROR)
790 	hps = GpiCreatePS(hab, hdcMem, &sizePS,
791 			  PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC);
792 
793     if (hps != GPI_ERROR)
794 	hbmp = GpiCreateBitmap(hps, &bmih, 0L, NULL, NULL);
795 
796     if (hbmp != GPI_ERROR)
797 	hbmr = GpiSetBitmap(hps, hbmp);
798 
799 
800     if (hbmr != HBM_ERROR) {
801 	LONG rc;
802 	ERRORID eid;
803 	POINTL apts[4];
804 
805 	/* target is inclusive */
806 	apts[0].x = 0;
807 	apts[0].y = 0;
808 	apts[1].x = right - left - 1;
809 	apts[1].y = top - bottom - 1;
810 	/* source is not inclusive of top & right borders */
811 	apts[2].x = left;
812 	apts[2].y = bottom;
813 	apts[3].x = right;
814 	apts[3].y = top;
815 
816 	rc = 0;
817 	eid = WinGetLastError(hab);
818 	rc = GpiDrawBits(hps, pbm->bits, pbm->pbmi, 4, apts,
819 		     (bitmap.depth != 1) ? ROP_SRCCOPY : ROP_NOTSRCCOPY, 0);
820 	if (rc == 0) {
821 	    char buf[256];
822 
823 	    eid = WinGetLastError(hab);
824 	    sprintf(buf, "make_bitmap: GpiDrawBits rc = %08x, eid = %08x", rc, eid);
825 	    message_box(buf, 0);
826 	}
827     }
828     if (hbmr != HBM_ERROR)
829 	GpiSetBitmap(hps, (ULONG) 0);
830     if (hps != GPI_ERROR)
831 	GpiDestroyPS(hps);
832     if (hdcMem != DEV_ERROR)
833 	DevCloseDC(hdcMem);
834 
835     if ((hbmr == HBM_ERROR) || (hdcMem == DEV_ERROR) ||
836 	(hbmp == GPI_ERROR) || (hps == GPI_ERROR)) {
837 	if (hbmp != GPI_ERROR)
838 	    GpiDeleteBitmap(hbmp);
839 	debugbeep(2);
840 	return 0;
841     }
842     return hbmp;
843 }
844 
845 MRESULT
846 paint_bitmap(HPS ps, PRECTL prect, int scrollx, int scrolly)
847 {
848     POINTL apts[4];
849     int wx, wy;
850 
851     if (WinIsRectEmpty(hab, prect))
852 	return 0;
853     if (ps == NULLHANDLE) {
854 	debugbeep(1);
855     }
856     /* source is not inclusive of top & right borders */
857     wx = prect->xRight - prect->xLeft;	/* update width */
858     wy = prect->yTop - prect->yBottom;	/* update height */
859     apts[2].x = prect->xLeft + scrollx;
860     apts[2].y = prect->yBottom + scrolly;
861     if (apts[2].x > bitmap.width)
862 	apts[2].x = bitmap.width;
863     if (apts[2].x + wx > bitmap.width)
864 	wx = bitmap.width - apts[2].x;
865     apts[3].x = apts[2].x + wx;
866     if (apts[2].y > bitmap.height)
867 	apts[2].y = bitmap.height;
868     if (apts[2].y + wy > bitmap.height)
869 	wy = bitmap.height - apts[2].y;
870     apts[3].y = apts[2].y + wy;
871     /* target is inclusive */
872     apts[0].x = prect->xLeft;
873     apts[0].y = prect->yBottom;
874     apts[1].x = prect->xLeft + wx - 1;
875     apts[1].y = prect->yBottom + wy - 1;
876 
877     if ((display.bitcount == 4)	/* standard VGA is buggy */
878 	||((os_version == 201100) && (display.bitcount == 8) && (bitmap.depth == 1))	/* S3 and ATI GU are buggy */
879 	) {
880 	/* slow code to dodge OS/2 bugs */
881 	/* this code double buffers the bitmap and works on a standard VGA
882 	 * but didn't work on an ATI Ultra Graphics Pro in 8514 emulation
883 	 */
884 	/* This won't work for version 2.11, S3 or ATI GU, 8bit/pixel display, 8bit/pixel bitmap */
885 	HBITMAP hbmp;
886 
887 	/* create a bitmap */
888 	hbmp = make_bitmap(&bitmap, apts[2].x, apts[2].y, apts[3].x, apts[3].y, bitmap.depth);
889 	/* Draw it to the display */
890 	if (hbmp) {
891 	    WinDrawBitmap(ps, hbmp, NULL, &apts[0], CLR_BLACK, CLR_WHITE, DBM_NORMAL);
892 	    GpiDeleteBitmap(hbmp);
893 	}
894     } else {
895 	/* fast code which doesn't always work */
896 	/* This code works on the Trident SVGA and 8514 in 256 color mode,
897 	 * but GpiDrawBits fails with a SYS3175 on the standard VGA.
898 	 */
899 	/* This won't work for version 2.11, S3 or ATI GU, 8bit/pixel display, 1bit/pixel bitmap */
900 	GpiDrawBits(ps, bitmap.bits, bitmap.pbmi, 4, apts,
901 		    (bitmap.depth != 1) ? ROP_SRCCOPY : ROP_NOTSRCCOPY, 0);
902     }
903 
904     return 0;
905 }
906 
907 
908 /* This is the window function */
909 MRESULT EXPENTRY
910 ClientWndProc(HWND hwnd, ULONG mess,
911 	      MPARAM mp1, MPARAM mp2)
912 {
913     char buf[256];
914     static int cxClient, cyClient;
915     static int cxAdjust, cyAdjust;
916     static int nHscrollMax, nHscrollPos;
917     static int nVscrollMax, nVscrollPos;
918     int nHscrollInc;
919     int nVscrollInc;
920     HWND hwndScroll;
921     HPS hps;
922     RECTL rect;
923     ULONG ulclr;
924 
925     switch (mess) {
926 	case WM_CREATE:
927 	    break;
928 	case WM_ERASEBACKGROUND:
929 	    /* by returning TRUE, the Presentation Manager automatically clears
930 	     * the window each time the window is resized or moved.
931 	     */
932 	    return (MRESULT) TRUE;
933 	case WM_GSUPDATE:
934 	    if (!WinInvalidateRect(hwnd_bmp, (PRECTL) NULL, TRUE))
935 		error_message("error invalidating rect");
936 	    if (!WinUpdateWindow(hwnd_bmp))
937 		error_message("error updating window");
938 	    return 0;
939 	case WM_COMMAND:
940 	    switch (LONGFROMMP(mp1)) {
941 		case IDM_ABOUT:
942 		    WinDlgBox(HWND_DESKTOP, hwnd, AboutDlgProc, 0, IDD_ABOUT, 0);
943 		    break;
944 		case IDM_COPY:
945 		    copy_clipboard();
946 		    break;
947 	    }
948 	    break;
949 	case WM_REALIZEPALETTE:
950 	    if ((bitmap.depth == 8) && display.hasPalMan && display.hpal_exists) {
951 		hps = WinGetPS(hwnd);
952 		if (hps == NULLHANDLE)
953 		    debugbeep(1);
954 		GpiSelectPalette(hps, display.hpal);
955 		if (WinRealizePalette(hwnd, hps, &ulclr) > 0)
956 		    WinInvalidateRect(hwnd, NULL, FALSE);
957 		GpiSelectPalette(hps, (HPAL) NULL);
958 		WinReleasePS(hps);
959 		return 0;
960 	    }
961 	    break;		/* use default processing */
962 	case WM_PAINT:
963 	    /* Refresh the window each time the WM_PAINT message is received */
964 
965 	    /* get bmp mutex to stop gs.exe changing bitmap while we paint */
966 	    DosRequestMutexSem(bmp_mutex_sem, 10000);
967 	    if (scan_bitmap(&bitmap))
968 		update_scroll_bars();	/* bitmap has changed */
969 
970 	    if (!bitmap.valid) {
971 		DosReleaseMutexSem(bmp_mutex_sem);
972 		hps = WinBeginPaint(hwnd, (ULONG) 0, &rect);
973 		if (hps == NULLHANDLE)
974 		    debugbeep(4);
975 		WinFillRect(hps, &rect, CLR_BACKGROUND);
976 		WinEndPaint(hwnd);
977 		return 0;
978 	    }
979 	    hps = WinBeginPaint(hwnd, (HPS) NULL, &rect);
980 	    if (hps == NULLHANDLE)
981 		debugbeep(4);
982 	    if ((bitmap.depth == 8) && display.hasPalMan && display.hpal_exists) {
983 		GpiSelectPalette(hps, display.hpal);
984 		WinRealizePalette(hwnd, hps, &ulclr);
985 		paint_bitmap(hps, &rect, nHscrollPos, nVscrollMax - nVscrollPos);
986 		GpiSelectPalette(hps, (HPAL) NULL);
987 	    } else
988 		paint_bitmap(hps, &rect, nHscrollPos, nVscrollMax - nVscrollPos);
989 	    WinEndPaint(hwnd);
990 
991 	    DosReleaseMutexSem(bmp_mutex_sem);
992 	    return 0;
993 	case WM_MOVE:
994 	    /* don't interrogate the window location immediately since */
995 	    /* it causes the Diamond Stealth VL24 with IBM S3 drivers */
996 	    /* to corrupt the display */
997 	    DosSleep(50);
998 	    if (hwnd_frame) {	/* save window position for INI file */
999 		SWP swp;
1000 
1001 		WinQueryWindowPos(WinQueryWindow(hwnd, QW_PARENT), &swp);
1002 		if (!(swp.fl & SWP_MINIMIZE)) {
1003 		    option.img_origin.x = swp.x;
1004 		    option.img_origin.y = swp.y;
1005 		    option.img_max = ((swp.fl & SWP_MAXIMIZE) != 0);
1006 		}
1007 	    }
1008 	    return 0;
1009 	case WM_SIZE:
1010 	    cyClient = SHORT2FROMMP(mp2);
1011 	    cxClient = SHORT1FROMMP(mp2);
1012 
1013 	    cyAdjust = min(bitmap.height, cyClient) - cyClient;
1014 	    cyClient += cyAdjust;
1015 
1016 	    nVscrollMax = max(0, bitmap.height - cyClient);
1017 	    nVscrollPos = min(nVscrollPos, nVscrollMax);
1018 	    scroll_pos.y = nVscrollMax - nVscrollPos;
1019 
1020 	    if (!bitmap.valid)
1021 		cyClient = cyAdjust = nVscrollMax = nVscrollPos;
1022 
1023 	    hwndScroll = WinWindowFromID(WinQueryWindow(hwnd, QW_PARENT), FID_VERTSCROLL);
1024 	    WinSendMsg(hwndScroll, SBM_SETSCROLLBAR, MPFROMLONG(nVscrollPos),
1025 		       MPFROM2SHORT(0, nVscrollMax));
1026 	    if (bitmap.valid)
1027 		WinSendMsg(hwndScroll, SBM_SETTHUMBSIZE, MPFROM2SHORT(cyClient, bitmap.height),
1028 			   MPFROMLONG(0));
1029 	    else
1030 		WinSendMsg(hwndScroll, SBM_SETTHUMBSIZE, MPFROM2SHORT(1, 1),
1031 			   MPFROMLONG(0));
1032 
1033 	    cxAdjust = min(bitmap.width, cxClient) - cxClient;
1034 	    cxClient += cxAdjust;
1035 
1036 	    nHscrollMax = max(0, bitmap.width - cxClient);
1037 	    nHscrollPos = min(nHscrollPos, nHscrollMax);
1038 	    scroll_pos.x = nHscrollPos;
1039 
1040 	    if (!bitmap.valid)
1041 		cxClient = cxAdjust = nHscrollMax = nHscrollPos;
1042 
1043 	    hwndScroll = WinWindowFromID(WinQueryWindow(hwnd, QW_PARENT), FID_HORZSCROLL);
1044 	    WinSendMsg(hwndScroll, SBM_SETSCROLLBAR, MPFROMLONG(nHscrollPos),
1045 		       MPFROM2SHORT(0, nHscrollMax));
1046 	    if (bitmap.valid)
1047 		WinSendMsg(hwndScroll, SBM_SETTHUMBSIZE, MPFROM2SHORT(cxClient, bitmap.width),
1048 			   MPFROMLONG(0));
1049 	    else
1050 		WinSendMsg(hwndScroll, SBM_SETTHUMBSIZE, MPFROM2SHORT(1, 1),
1051 			   MPFROMLONG(0));
1052 
1053 	    if ((cxAdjust != 0 || cyAdjust != 0)) {
1054 		SWP swp;
1055 
1056 		WinQueryWindowPos(WinQueryWindow(hwnd, QW_PARENT), &swp);
1057 		WinSetWindowPos(WinQueryWindow(hwnd, QW_PARENT), 0,
1058 				swp.x, swp.y - cyAdjust,
1059 		 swp.cx + cxAdjust, swp.cy + cyAdjust, SWP_SIZE | SWP_MOVE);
1060 		cxAdjust = cyAdjust = 0;
1061 	    }
1062 	    if (hwnd_frame) {	/* save window size for INI file */
1063 		SWP swp;
1064 
1065 		WinQueryWindowPos(WinQueryWindow(hwnd, QW_PARENT), &swp);
1066 		if (!(swp.fl & SWP_MINIMIZE)) {
1067 		    option.img_size.x = swp.cx;
1068 		    option.img_size.y = swp.cy;
1069 		    option.img_max = ((swp.fl & SWP_MAXIMIZE) != 0);
1070 		}
1071 	    }
1072 	    break;
1073 	case WM_VSCROLL:
1074 	    switch (SHORT2FROMMP(mp2)) {
1075 		case SB_LINEUP:
1076 		    nVscrollInc = -cyClient / 16;
1077 		    break;
1078 		case SB_LINEDOWN:
1079 		    nVscrollInc = cyClient / 16;
1080 		    break;
1081 		case SB_PAGEUP:
1082 		    nVscrollInc = min(-1, -cyClient);
1083 		    break;
1084 		case SB_PAGEDOWN:
1085 		    nVscrollInc = max(1, cyClient);
1086 		    break;
1087 		case SB_SLIDERPOSITION:
1088 		    nVscrollInc = SHORT1FROMMP(mp2) - nVscrollPos;
1089 		    break;
1090 		case SB_TOP:
1091 		    nVscrollInc = -nVscrollPos;
1092 		    break;
1093 		case SB_BOTTOM:
1094 		    nVscrollInc = nVscrollMax - nVscrollPos;
1095 		    break;
1096 		default:
1097 		    nVscrollInc = 0;
1098 	    }
1099 	    if ((nVscrollInc = max(-nVscrollPos,
1100 		       min(nVscrollInc, nVscrollMax - nVscrollPos))) != 0) {
1101 		LONG lComplexity;
1102 
1103 		hwndScroll = WinWindowFromID(WinQueryWindow(hwnd, QW_PARENT), FID_VERTSCROLL);
1104 		nVscrollPos += nVscrollInc;
1105 		scroll_pos.y = nVscrollMax - nVscrollPos;
1106 		lComplexity = WinScrollWindow(hwnd, 0, nVscrollInc, (PRECTL) NULL, (PRECTL) NULL,
1107 				     (HRGN) NULLHANDLE, (PRECTL) & rect, 0);
1108 		WinSendMsg(hwndScroll, SBM_SETPOS, MPFROMLONG(nVscrollPos), 0);
1109 		if (lComplexity != RGN_RECT) {
1110 		    WinInvalidateRect(hwnd, (PRECTL) NULL, FALSE);
1111 		    WinUpdateWindow(hwnd);
1112 		} else {
1113 		    /* redraw exposed area */
1114 		    hps = WinGetPS(hwnd);
1115 		    if (hps == NULLHANDLE)
1116 			debugbeep(1);
1117 		    if ((bitmap.depth == 8) && display.hasPalMan && display.hpal_exists) {
1118 			GpiSelectPalette(hps, display.hpal);
1119 			WinRealizePalette(hwnd, hps, &ulclr);
1120 			paint_bitmap(hps, &rect, nHscrollPos, nVscrollMax - nVscrollPos);
1121 			GpiSelectPalette(hps, (HPAL) NULL);
1122 		    } else
1123 			paint_bitmap(hps, &rect, nHscrollPos, nVscrollMax - nVscrollPos);
1124 		    WinReleasePS(hps);
1125 		}
1126 	    }
1127 	    break;
1128 	case WM_HSCROLL:
1129 	    switch (SHORT2FROMMP(mp2)) {
1130 		case SB_LINELEFT:
1131 		    nHscrollInc = -cxClient / 16;
1132 		    break;
1133 		case SB_LINERIGHT:
1134 		    nHscrollInc = cyClient / 16;
1135 		    break;
1136 		case SB_PAGELEFT:
1137 		    nHscrollInc = min(-1, -cxClient);
1138 		    break;
1139 		case SB_PAGERIGHT:
1140 		    nHscrollInc = max(1, cxClient);
1141 		    break;
1142 		case SB_SLIDERPOSITION:
1143 		    nHscrollInc = SHORT1FROMMP(mp2) - nHscrollPos;
1144 		    break;
1145 		default:
1146 		    nHscrollInc = 0;
1147 	    }
1148 	    if ((nHscrollInc = max(-nHscrollPos,
1149 		       min(nHscrollInc, nHscrollMax - nHscrollPos))) != 0) {
1150 		LONG lComplexity;
1151 
1152 		hwndScroll = WinWindowFromID(WinQueryWindow(hwnd, QW_PARENT), FID_HORZSCROLL);
1153 		nHscrollPos += nHscrollInc;
1154 		scroll_pos.x = nHscrollPos;
1155 		lComplexity = WinScrollWindow(hwnd, -nHscrollInc, 0, (PRECTL) NULL, (PRECTL) NULL,
1156 				     (HRGN) NULLHANDLE, (PRECTL) & rect, 0);
1157 		/* need to send next message BEFORE redrawing, otherwise S3 driver screws up */
1158 		WinSendMsg(hwndScroll, SBM_SETPOS, MPFROMLONG(nHscrollPos), 0);
1159 		if (lComplexity != RGN_RECT) {
1160 		    WinInvalidateRect(hwnd, (PRECTL) NULL, FALSE);
1161 		    WinUpdateWindow(hwnd);
1162 		} else {
1163 		    /* redraw exposed area */
1164 		    hps = WinGetPS(hwnd);
1165 		    if (hps == NULLHANDLE)
1166 			debugbeep(1);
1167 		    if ((bitmap.depth == 8) && display.hasPalMan && display.hpal_exists) {
1168 			GpiSelectPalette(hps, display.hpal);
1169 			WinRealizePalette(hwnd, hps, &ulclr);
1170 			paint_bitmap(hps, &rect, nHscrollPos, nVscrollMax - nVscrollPos);
1171 			GpiSelectPalette(hps, (HPAL) NULL);
1172 		    } else
1173 			paint_bitmap(hps, &rect, nHscrollPos, nVscrollMax - nVscrollPos);
1174 		    WinReleasePS(hps);
1175 		}
1176 	    }
1177 	    break;
1178 	case WM_CHAR:		/* process keystrokes here */
1179 	    if (SHORT1FROMMP(mp1) & KC_CHAR) {
1180 		/* pass control to gs if ENTER pressed */
1181 		if (hwnd_gs && (SHORT1FROMMP(mp2) == '\r'))
1182 		    WinSetActiveWindow(HWND_DESKTOP, hwnd_gs);
1183 	    }
1184 	    /* Process only key presses, not key releases */
1185 	    if (SHORT1FROMMP(mp1) & KC_KEYUP)
1186 		break;
1187 	    if (SHORT1FROMMP(mp1) & KC_VIRTUALKEY) {
1188 		USHORT vkey = SHORT2FROMMP(mp2);
1189 
1190 		switch (vkey) {
1191 		    case VK_HOME:
1192 			WinSendMsg(hwnd, WM_VSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_TOP));
1193 			break;
1194 		    case VK_END:
1195 			WinSendMsg(hwnd, WM_VSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_BOTTOM));
1196 			break;
1197 		    case VK_UP:
1198 			WinSendMsg(hwnd, WM_VSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_LINEUP));
1199 			break;
1200 		    case VK_DOWN:
1201 			WinSendMsg(hwnd, WM_VSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_LINEDOWN));
1202 			break;
1203 		    case VK_PAGEUP:
1204 			WinSendMsg(hwnd, WM_VSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_PAGEUP));
1205 			break;
1206 		    case VK_PAGEDOWN:
1207 			WinSendMsg(hwnd, WM_VSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_PAGEDOWN));
1208 			break;
1209 		    case VK_LEFT:
1210 			if (SHORT1FROMMP(mp1) & KC_CTRL)
1211 			    WinSendMsg(hwnd, WM_HSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_PAGELEFT));
1212 			else
1213 			    WinSendMsg(hwnd, WM_HSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_LINELEFT));
1214 			break;
1215 		    case VK_RIGHT:
1216 			if (SHORT1FROMMP(mp1) & KC_CTRL)
1217 			    WinSendMsg(hwnd, WM_HSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_PAGERIGHT));
1218 			else
1219 			    WinSendMsg(hwnd, WM_HSCROLL, MPFROMLONG(0), MPFROM2SHORT(0, SB_LINERIGHT));
1220 			break;
1221 		}
1222 	    }
1223 	    break;
1224 	default:
1225 	    /* All messages not handled by the ClientWndProc must be passed
1226 	     * along to the Presentation Manager for default processing
1227 	     */
1228 	    return WinDefWindowProc(hwnd, mess, mp1, mp2);
1229     }
1230     return (MRESULT) FALSE;
1231 }
1232 
1233 
1234 
1235 /* About Dialog Box */
1236 MRESULT EXPENTRY
1237 AboutDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
1238 {
1239     switch (msg) {
1240 	case WM_COMMAND:
1241 	    switch (COMMANDMSG(&msg)->cmd) {
1242 		case DID_OK:
1243 		    WinDismissDlg(hwnd, TRUE);
1244 		    return (MRESULT) TRUE;
1245 	    }
1246 	    break;
1247     }
1248     return WinDefDlgProc(hwnd, msg, mp1, mp2);
1249 }
1250