alen@crash.cts.com (Alen Shapiro) (12/08/90)
(extract from the readme)...
This is wfprintf(), it is packaged with a working example of its use,
(I wonder where I got that idea). Just unshar it on a friendly Unix
box, xmodem it to an empty PC directory, type nmk<CR> and
run the resulting wfprintf.exe under windows3.0.
Fits fairly easily into existing applications (2 hooks - see the readme).
--alen@crash.cts.com
Use it however you like, copy, modify, break it, for profit or not.
----------------8<-----8=------8<-----cut---here------------8<---------
#!/bin/sh
# this is a shar archive, run it through /bin/sh
# in an otherwise empty directory
#
echo extracting wfprintf.c
cat > wfprintf.c << 'MyEof'
/* wfprintf package for Windows 3.0 (c) 1990 Alen Shapiro and Carl Lambert
* Delphi Management Inc. Permission is hereby given for the copying, use and
* modification of this package as needed, for any purpose, providing
* the following copyright notice is included on this or any derived library.
* If you improve the package, please mail diffs and an explanation to
* alen%shappy@crash.cts.com. If improvements are incorporated, your name
* will appear with the original authors on the (c) notice.
* This is not public domain but it's pretty close. Hope it comes in handy.
* No warranty expressed or implied - WARNING, "as is" software.
*/
static char *SCCSId = "wfprintf v1.0 (c) 1990 Alen Shapiro & Carl Lambert, Delphi Management Inc.";
/* fprintf implementation to a Windows 3.0 window, up to WF_HORZ proportional
* characters per line, WF_VERT lines per window. Resize the window at your
* peril. Could be made resizable with some event catchers and a additional
* textmetric calls (any takers?). Is there a better way to do the variable
* number of args type interface demanded by fprintf?
* Someone wanna implement the ANSI control sequences :-) ?
*
* BTW - there is a much better way to do this - take the struct scr
* below and allocate an array of them (NWINS), build a wfopen function
* that returns a pointer to the next unused element in the array, mark
* it used, allocates "screen" space and gets font/window info. build a
* wfclose function to deallocate the space and mark the element unused
* one again. The return value from wfopen could be passed as the first
* arg to wfprintf and the stupid tests to stderr/stdout could be removed.
* What a lot of work for debugging print capability!!. Wrefresh would
* have to refresh all used elements in this hypothetical wio array.
*/
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "wfprintf.h"
extern HWND htxt_wnd;
struct scr {
TEXTMETRIC textmetric; /* current font-info */
int lpbuf[(int)(WF_TILDE-WF_SPACE)]; /* buffer for printable char widths */
char **screen; /* screen refresh array */
HWND txt_wnd; /* handle of text window */
RECT winrect; /* text window bounds rect */
};
static struct scr scr;
/* calloc that is in-line callable and checks for errors
*/
char *
ccalloc(no, sz)
unsigned no, sz; {
char *addr = calloc(no, sz);
if(addr == (char *)NULL) {
char msg[100];
sprintf(msg, "unable to calloc %u chunk(s) of %u byte(s)", no, sz);
MessageBox(scr.txt_wnd, msg, NULL, MB_OK|MB_ICONHAND);
exit(1);
}
}
/* in line callable getcd()
*/
HDC
ggetdc(hwin)
HWND hwin; {
HDC handl = GetDC(hwin);
if(handl == (HDC)NULL) {
MessageBox(scr.txt_wnd, "GetDC failure - expect catastrophic badness :-(",NULL,MB_OK | MB_ICONHAND);
}
return(handl);
}
/* refresh the whole screen from memory, called from event
* loop whenever the text window is activated
*/
void
wrefresh() {
int i, y=0;
HDC hdc;
char **sptr;
if(scr.screen != (char **)NULL) { /* in case refresh before first print */
InvalidateRgn(scr.txt_wnd, NULL, WF_ERASE_BK);
UpdateWindow(scr.txt_wnd);
hdc = ggetdc(scr.txt_wnd);
for(sptr=scr.screen, i=0 ; i < WF_VERT ; i++, sptr++) {
TextOut(hdc, 0, y, *sptr, (size_t)strlen(*sptr));
y += scr.textmetric.tmExternalLeading + scr.textmetric.tmHeight;
}
ReleaseDC(scr.txt_wnd, hdc);
}
}
/* rotate the addresses of the copies of screen lines
* kept in memory so that the top line is lost and becomes
* the store used for new characters added to the bottom of the screen
*/
static void
wshift(screen)
char **screen; {
int i;
char *tmpline = *screen;
/* NULL out the old "top line" */
*tmpline = (char)NULL;
/* shift "copy of screen" lines up one */
for(i=0 ; i < WF_VERT-1 ; i++)
*screen++ = *(screen+1);
/* old "top line" becomes new "last line", ready to accept
* fresh characters
*/
*screen = tmpline;
}
/* scroll the window up one line height (according to font
* details). Keep memory copy of screen chars in step by
* calling wshift(). Reset global pixel position and logical char
* position (*pposx resp. *xptr) to left of window.
*/
static void
wscroll(pposx, xptr, yptr, pscr)
int *pposx, *xptr, *yptr;
struct scr *pscr; {
pscr->screen[*yptr][*xptr] = (char)NULL;
wshift(pscr->screen);
ScrollWindow(pscr->txt_wnd, 0, -(pscr->textmetric.tmExternalLeading +
pscr->textmetric.tmHeight), NULL, NULL);
UpdateWindow(pscr->txt_wnd);
/* LOOK OUT - lots of global side effects */
*xptr = *pposx = 0;
*yptr = WF_VERT-1;
}
/* output a character to the current screen pixel-x position (*pposx)
* and the current pixel-y position (derived from font-info and y-index
* (*yptr). Advance pixe-x position by the width of the current character
* if line-wrap is indicated then call wscroll() to deal with both local and
* screen scrolling.
*/
static void
woutchar(ch, pposx, xptr, yptr, pscr)
char ch;
int *pposx, *xptr, *yptr;
struct scr *pscr; {
int chwidth;
HDC hdc;
int winwidth = pscr->winrect.right - pscr->winrect.left;
char *cptr; /* can't set it here in case of wscroll */
if(ch >= WF_SPACE && ch <= WF_TILDE)
chwidth = pscr->lpbuf[(int)(ch-WF_SPACE)];
else
chwidth = WF_DEFAULTWIDTH;
if(chwidth <= 0)
chwidth = WF_DEFAULTWIDTH;
if(((*pposx+chwidth) >= (winwidth-WF_WINBORDER)) || (*xptr >= (WF_HORZ-1)))
wscroll(pposx, xptr, yptr, pscr);
/* output a char to the current screen location [*xptr][*yptr] */
hdc = ggetdc(pscr->txt_wnd);
TextOut(hdc, *pposx, *yptr * (pscr->textmetric.tmExternalLeading + pscr->textmetric.tmHeight), &ch, 1);
ReleaseDC(pscr->txt_wnd, hdc);
*pposx += chwidth;
cptr = &(pscr->screen[*yptr][(*xptr)++]); /* LOOK OUT...global side-effect */
*cptr++ = ch;
*cptr = (char)NULL; /* keep string terminated */
}
/* output a chunk of text, expand tabs, optionally terminate the line
* by scrolling (if(nlflag)) . Host point for screen position statics.
* calls woutchar() and wscroll() to do the dirty work, passing pointers
* to the statics and globals that describe the state of the screen.
*/
static void
woutline(str, nlflag, pscr)
char *str;
int nlflag;
struct scr *pscr; {
int len = strlen(str), i, j;
static int x = 0, lx = 0, y = WF_VERT-1;
static int posx = 0;
if(str == (char *)NULL)
return;
for(i=0 ; i < len ; i++, str++) {
if(*str == WF_TAB) {
int fill = WF_TABSTOP-(lx%WF_TABSTOP);
for(j=0 ; j < (fill == 0 ? WF_TABSTOP : fill) ; j++) {
woutchar(WF_SPACE, &posx, &x, &y, pscr);
lx++;
}
}
else {
woutchar(*str, &posx, &x, &y, pscr);
lx++;
}
}
if(nlflag == WF_SCROLL) {
wscroll(&posx, &x, &y, pscr);
lx = 0;
}
}
/* chop up a string destined for the screen into line chunks, the last such chunk
* may not be NL terminated. Called recursively to deal with imbedded NL (sorry -
* I couldn't help it...this way was too cute to make iterative).
*/
static void
woutstr(cptr, pscr)
char *cptr;
struct scr *pscr; {
char *nlptr;
if(cptr == (char *)NULL)
return;
if((nlptr=strchr(cptr, WF_NL)) != (char *)NULL) {
*nlptr = (char)NULL;
woutline(cptr, WF_SCROLL, pscr);
woutstr(nlptr+1, pscr);
}
else {
woutline(cptr, WF_NOSCROLL, pscr);
}
}
/* called in place of fprintf(std{out,err}) to direct output to a windows
* text window. Scroll bars not implemented, proportional fonts are dealt
* with, lines autowrap if they can't be fitted....Very basic package. Max
* line length = WF_LINE (#defined). See the setup required in main(). NOTE
* stdout and stderr are merged into the same window, should not be difficult
* to parameterize calls to split the output (any takers?). Also, stdin,
* stdout, stderr etc. streams are still legal and available. Windows
* fopen() WILL assign these tokens to real files that may be the first
* to be opened (first fopen() will be stdin, second stdout etc.). This
* wreaks havock with my original idea to #define fprintf wfprintf and then
* call the real fprintf internal to wfprintf for non screen streams.
* Blasted MS do it again!!. I left the calling sequence intact though
* just in case some bright spark figured out how to use the preprocessor
* to redirect fprintf->wfprintf sensibly. For now though, all
* fprintf(std*) calls need to be manually changed to wfprintf(std*)
* in the application source.
* WARNING...I've noticed that long doubles do not print right, perhaps
* they are stacked differently (or perhaps I only tried them at the end
* of the argument list). Anyway, be careful and check the screen results
* as you test, whenever you are able.
*/
void
wfprintf(stream, fwin, a, b, c, d, e, f, h, i, j, k, l, m, n, o, p, q, r)
FILE *stream;
char *fwin;
void *a, *b, *c, *d, *e, *f, *h, *i, *j, *k, *l, *m, *n, *o, *p, *q, *r; {
char line[WF_LINE];
HDC hdc;
int wi;
if(scr.screen == (char **)NULL) { /* first time through */
scr.txt_wnd = htxt_wnd;
GetWindowRect(scr.txt_wnd, &scr.winrect);
hdc = ggetdc(scr.txt_wnd);
GetTextMetrics(hdc, &(scr.textmetric));
if(GetCharWidth(hdc, (WORD)WF_SPACE, (WORD)WF_TILDE, scr.lpbuf) == (BOOL)0) {
ReleaseDC(scr.txt_wnd, hdc);
MessageBox(scr.txt_wnd, "GetCharWidth() failed - using defaults" ,NULL,MB_OK | MB_ICONHAND);
}
else
ReleaseDC(scr.txt_wnd, hdc);
/* do this last cos it is the clue that wrefresh() uses
* to determine if there is a "screen" to refresh
*/
scr.screen = (char **)ccalloc((unsigned)WF_VERT, (unsigned)(sizeof(char *)));
for(wi=0 ; wi < WF_VERT ; wi++)
scr.screen[wi] = (char *)ccalloc((unsigned)WF_HORZ, (unsigned)(sizeof(char)));
}
if(stream == stdout || stream == stderr) { /* streams just serve as tokens */
sprintf(line, fwin, a, b, c, d, e, f, h, i, j, k, l, m, n, o, p, q, r);
woutstr(line, &scr);
}
else {
MessageBox(scr.txt_wnd, "wfprintf to bad stream!!" ,NULL,MB_OK | MB_ICONHAND);
}
}
MyEof
echo extracting wmain.c
cat > wmain.c << 'MyEof'
#include <windows.h>
#include <stdio.h>
#include "wfprintf.h"
HANDLE hInst;
HWND htxt_wnd;
char *mess[] = {
"\nmumble\n",
"\nstrike 2\n",
"\nyer outa there\n",
"\nforrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrre\n",
"\nlook out I'm gonna be.......\n",
"\n.....SIX - HUUUUUUEEEEEEYYYYYYYYY\n",
"\ngross eh - no that's more than seven\n",
"\nI've lost count - it must be something I eight\n",
"\n9 U C V F N 10 E M N X 4 T\n",
"\n10 jars of gelignite hanging on a wall...10 jars of gelignite hangin on a wall...and if one jar of gelignite should accidently fall\n",
"\nthere'll be no jars of gelignite and no blinking wall\n",
(char *)NULL
};
BOOL FAR PASCAL
About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam; {
switch(message) {
case WM_INITDIALOG:
return(TRUE);
case WM_COMMAND:
if(wParam == IDOK || wParam == IDCANCEL) {
EndDialog(hDlg, TRUE);
return (TRUE);
}
break;
}
return (FALSE);
}
long FAR PASCAL
t_handler(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam; {
FARPROC lpProcAbout;
int i;
static int acount = 0;
switch(message){
case WM_ACTIVATE:
wrefresh();
break;
case WM_COMMAND:
switch (wParam) {
case IDM_EXIT:
DestroyWindow(hWnd);
break;
case IDM_ABOUT:
wfprintf(stderr, "how \"ABOUT\" this - I've been called %d time%s%s\n", acount+1, (acount+1 == 1 ? "" : "s"), (acount+1 == 2 ? " (that's twice)" : ""));
for(i=0 ; i < acount+1 ; i++)
wfprintf(stderr, "abcdefghijklmnopqrstuvwxyz\t1234567890");
wfprintf(stderr, mess[acount++]);
if(mess[acount] == (char *)NULL)
acount = 0;
lpProcAbout = MakeProcInstance(About, hInst);
DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
FreeProcInstance(lpProcAbout);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
/* don't enable the following line or you'll get a message
* before the windows have been properly set up
*/
#ifdef DONT_DO_THIS
wfprintf(stderr, "message = (%d)\n", message);
#endif /* DONT_DO_THIS */
return(DefWindowProc(hWnd, message, wParam, lParam));
}
return (NULL);
}
BOOL
InitTxtWnd(hInstance)
HANDLE hInstance; {
WNDCLASS wc;
wc.style = NULL;
wc.lpfnWndProc = t_handler;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MwfprintfMenu";
wc.lpszClassName = "MwfprintfWClass";
return (RegisterClass(&wc));
}
BOOL
InitInstance(hInstance, nCmdShow)
HANDLE hInstance;
int nCmdShow; {
hInst = hInstance;
htxt_wnd = CreateWindow("MwfprintfWClass", "Wfprintf Demo Application",
WS_OVERLAPPEDWINDOW, 0, 0, 622, 470, NULL, NULL, hInstance, NULL);
if(!htxt_wnd)
return (FALSE);
ShowWindow(htxt_wnd, nCmdShow);
UpdateWindow(htxt_wnd);
}
int PASCAL
WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow; {
MSG msg;
/* Perform initializations that apply to a specific instance */
if(!InitTxtWnd(hInstance))
return(FALSE);
if(!InitInstance(hInstance, nCmdShow))
return (FALSE);
MessageBox(htxt_wnd, "starting wfprintf test - hit the File:About menu", NULL, MB_OK|MB_ICONHAND);
/* Acquire and dispatch messages until a WM_QUIT message is received. */
wfprintf(stderr, "Now you've done it, you activated the OK button!!\n\n");
while (GetMessage(&msg, NULL, NULL, NULL)) {
TranslateMessage(&msg); /* Translates virtual key codes */
DispatchMessage(&msg); /* Dispatches message to window */
}
return (msg.wParam); /* Returns the value from PostQuitMessage */
}
MyEof
echo extracting wfprintf.h
cat > wfprintf.h << 'MyEof'
/* what a complicated little program!! */
#define WF_HORZ 200 /* max chars per window width */
#define WF_VERT 26 /* window height in font lines */
#define WF_INSERT (VERT-1) /* where to insert new lines of text */
#define WF_SPACE ((unsigned char)(' '))
#define WF_TILDE ((unsigned char)('~'))
#define WF_WINBORDER 7
#define WF_LINE 512
#define WF_ERASE_BK 1
#define WF_DEFAULTWIDTH 16
#define WF_NL '\n'
#define WF_NOSCROLL 0
#define WF_SCROLL 1
#define WF_TAB '\t'
#define WF_TABSTOP 8
/* monkey see, monkey do */
#ifdef TEST_PROG
# define IDM_DEXIT 99
# define IDM_EXIT 101
# define IDM_ABOUT 102
# define ID_ABOUT 100
# define IDM_UNDO 200
# define IDM_CUT 201
# define IDM_COPY 202
# define IDM_PASTE 203
# define IDM_CLEAR 204
#endif /* TEST_PROG */
MyEof
echo extracting makefile
cat > makefile << 'MyEof'
DOSMODEL=L
# when using wfprintf.obj without the test program wmain.obj
# uncomment the following line and comment the one after it
# DEF =
DEF = -DTEST_PROG
CFLAGS = -qc $(DEF) -FPi -Ge -Gw -Oas -Zpe -A$(DOSMODEL)
SOURCES= wfprintf.c wmain.c
INCLUDES = wfprintf.h
XTRAS = makefile wfprintf.def wfprintf.rc readme
OBJECTS = wmain.obj wfprintf.obj
wfprintf.exe: wfprintf.def $(OBJECTS)
link /NOD $(OBJECTS), wfprintf, wfprintf, libw llibcew, \
wfprintf.def
rc wfprintf.res
wfprintf.exe: wfprintf.res
rc wfprintf.res
wfprintf.res: wfprintf.rc $(INCLUDES)
rc -r $(DEF) wfprintf.rc
wfprintf.obj: wfprintf.c $(INCLUDES)
clean:
del *.obj
del wfprintf.res
del wfprintf.exe
del tags
del *.map
tags:
ctags *.[ch]
wfprintf.shar: $(SOURCES) $(INCLUDES) $(XTRAS)
shar $(SOURCES) $(INCLUDES) $(XTRAS) > wfprintf.shar
MyEof
echo extracting wfprintf.def
cat > wfprintf.def << 'MyEof'
NAME wfprintf
DESCRIPTION 'test prog for wfprintf library'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 22000
STACKSIZE 5120
EXPORTS
t_handler @1
About @2
MyEof
echo extracting wfprintf.rc
cat > wfprintf.rc << 'MyEof'
#include <windows.h>
#include "wfprintf.h"
Dummy MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", IDM_DEXIT
END
END
MwfprintfMenu MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", IDM_EXIT
MENUITEM SEPARATOR
MENUITEM "&About Wfprintf...", IDM_ABOUT
END
POPUP "&Edit"
BEGIN
MENUITEM "&Undo\tAlt+BkSp", IDM_UNDO ,GRAYED
MENUITEM SEPARATOR
MENUITEM "Cu&t\tShift+Del", IDM_CUT ,GRAYED
MENUITEM "&Copy\tCtrl+Ins", IDM_COPY ,GRAYED
MENUITEM "&Paste\tShift+Ins", IDM_PASTE ,GRAYED
MENUITEM "&Delete\tDel", IDM_CLEAR ,GRAYED
END
END
AboutBox DIALOG 22, 17, 144, 75
STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
CAPTION "About wfprintf"
BEGIN
CTEXT "Delphi Management's" -1, 0, 5, 144, 8
CTEXT "wfprintf demo for windows" -1, 0, 14, 144, 8
CTEXT "hope this helps :-)" -1, 0, 34, 144, 8
DEFPUSHBUTTON "OK" IDOK, 53, 59, 32, 14, WS_GROUP
END
MyEof
echo extracting readme
cat > readme << 'MyEof'
Well, you asked for it (well, a lot of you did anyway), so here it is,
the biggest waste of time and effort since the invention of the RS232
protocol (but that's another story)...I mean why invent a standard and
then give Sun Microsystems an excuse not to fully implement flow
control on their SPARCstations....but that really is another story.
This is wfprintf(), it is packaged with a working example of its use,
(I wonder where I got that idea). Just unshar it on a friendly Unix
box, xmodem it to an empty directory on your PC, type nmk<CR> and
run the resulting wfprintf.exe under windows 3.0.
Wfprintf.obj is a fairly well isolated library, there are 2 hooks
to the outside world;
1) wrefresh() which should be called from your callbacks under
WM_ACTIVATE (it should probably be called when a child window
is dragged as well (try dragging the about box and see what a
large eraser looks like - gee I've emulated one of the functions
of PCpaint :-))),
2) the extern HWND htxt_wnd should refer to your text window handle
returned by CreateWindow (see wmain.c:InitInstance()).
See the comments that head up wfprintf.c for some hints about how
this could have been done better.
The software is provided "as-is", there is little chance that I'll
have time to enhance it but, if you could send me diffs, I'd like to
try and keep a current "up-to-date" version which I'll repost as
necessary. Use it commercially or not, I only ask that you keep
the copyright message in the source to wfprintf intact...it will grow
as people contribute (as will the version number).
Good luck and happy wfprintfing
--alen The Lisa Slayer - (learning how to turn a SPARC into a Flame)
alen%shappy.uucp@crash.cts.com (a mac+ uucp host - what a concept!!)
alen@crash.cts.com
MyEof