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