/*-------------------------------------------------------------------+ | | | Copyright (c) 1996, SAS Institute Inc. | | Unpublished - All Rights Reserved | | S A S / C S A M P L E | | | | NAME: WDHYPER | | LANGUAGE: C | | PURPOSE: Demonstrates how a hypertext window can be | | incorporated into a windowing application. | | See SAS/C Full-Screen Support Library User's Guide, | | Second Edition, Chapters 5, 6, and 7. | | INSTALLATION: Before compiling, you will need to modify the | | filename value in the browse_init function. | | To quickly find it, search for the string "fopen". | | SYSTEM NOTES: Proper execution will require that the SAS/C | | transient library, release 5.00H or higher, be | | available at runtime. | | INPUT/OUTPUT: A series of windows including PF values, browsing | | the source code for this very program, playing | | a simple number guessing game, and viewing a | | hypertext file are demonstrated. | | | | MVS - | | COMPILE/LINK: SUBMIT prefix.SAMPLE.AUX(WDHYPER) | | where "prefix" is the installation defined high-level| | qualifier for the SAS/C product. | | EXECUTE: execute under TSO, see below | | TSO - | | COMPILE: LC370 CLIST | | LINK: CLK370 CLIST | | EXECUTE: CALL your.load.lib(WDHYPER) | | CMS - | | COMPILE: GLOBAL MACLIB LC370 L$FSSL | | LC370 WDHYPER (EXTNAME | | LINK: GLOBAL TXTLIB LC370BAS LC370STD L$FSSL | | CLINK WDHYPER (GENMOD | | EXECUTE: WDHYPER | +-------------------------------------------------------------------*/ #eject /* Some useful macro's to make field manipulation understandable */ #define GAME_RESULT_LINE 6 #define GAME_RESULT_GUESSES_LEFT 10 #define GAME_RESULT_LAST_GUESS 24 #define GAME_RESULT_FIELD 36 #define GAME_GUESS_LINE 9 #define GAME_GUESS_FIELD 24 /* Header files */ #include /* Window interface header */ #include /* Standard I/O header */ #include /* Standard library header */ #include /* Time functions header */ /* A very few special purpose external variables */ char title_string(|44|); /* Title and time string */ char game_result_message(|36|); /* Win or lose message */ int game_number; /* The object of the game */ char err_buf(|136|); /* FSSL error msg buffer */ /* Prototypes for application functions */ void fs_error_warn(char *fn, int rc, int line); int wd_error_warn(char *, WDWI *, int parm1, void *parm2); int event_hd(WDWI *wdwi, int event, int parm1, void *parm2); int gamepop_hd(WDWI *wdwi, int event, int parm1, void *parm2); int browse_init(WDWI *); void pfbars_init(WDWI *); void game_init(WDWI *); void game_play(WDWI *, WDINPUT *); void time_update(WDWI *); int line_count(void); /* Common open list structure used to open the BROWSER */ /* window, the GAME window and the HYPER window. */ static WDOLST olst_common = { "COMMON ", /* Window name */ 0, 0, /* Physical screen origin */ 0, 0, /* Number of rows, number of cols */ 3, 6, /* Minimum window size */ NULL, /* Border, title, cmdline color */ /* Window flags */ WDW_BORDER+WDW_TITLE+ WDW_ERROR_EVT+WDW_WARN_EVT, /* Border, title, cmdline attr. */ BRIGHT+REVERSE_VIDEO, 0, 0, /* Initial cursor row, col */ &event_hd, /* Event handler function ptr */ 100, /* Event handler priority 16-239 */ NULL, /* Title text */ NULL, /* Cmdline text */ NULL, /* Border chars */ NULL}; /* Address of "user data" field */ /* The window structure used to open the GAMEPOP popup */ /* window. */ static WDOLST olst_gamepop = { "GAMEPOP ", /* Window name */ 15,18, /* Physical screen origin */ 3, 45, /* Number of rows, number of cols */ 3, 45, /* Minimum window size */ YELLOW, /* Border, title, cmdline color */ /* Window flags */ WDW_BORDER+WDW_TITLE+ WDW_ERROR_EVT+WDW_WARN_EVT, /* Border, title, cmdline attr. */ BRIGHT, 0, 0, /* Initial cursor row, col */ &gamepop_hd,/* Event handler function ptr */ 239, /* Event handler priority 16-239 */ /* Title text */ "SAS/C Sample Game Results", NULL, /* Cmdline text */ NULL, /* Border chars */ NULL}; /* Address of "user data" field */ /* The main function begins here */ void main() { struct FS_TERMATTR *term_attr; /* Terminal attribute structure ptr */ WDWI *browser, *game, *pfbars, /* Pointers to the 4 primary windows*/ *hyper; WDOLST *olst_pfbars, *olst_hyper; /* Pointer to open list structures */ int rc; char *init_string; /* Initial search string for HYPER */ double mask; /* Event handling mask for HYPER */ memset(&mask,0xFF,8); term_attr = wdinit(err_buf); /* Begin Window interface operations*/ if (term_attr == NULL) { fs_error_warn("wdinit",-1 ,(__LINE__-3)); } /* Modify the common open list structure with the */ /* specifics of the the BROWSER window and then */ /* open the window. */ strcpy(olst_common.name, "BROWSER "); olst_common.beg_row = 0; olst_common.beg_col = 0; olst_common.height = term_attr->prim_row - 4; olst_common.width = term_attr->prim_col; olst_common.color = MAGENTA; olst_common.title = "SAS/C Sample Browser"; browser = wdopen(&olst_common, &rc); if (browser == NULL) { fs_error_warn("wdopen", rc ,(__LINE__-3)); } /* Modify the common open list structure with the */ /* specifics of the the GAME window and then open */ /* the window. */ strcpy(olst_common.name, "GAME "); olst_common.beg_row = 0; olst_common.beg_col = 15; olst_common.height = term_attr->prim_row - 4; olst_common.width = term_attr->prim_col - 30; olst_common.color = BLUE; olst_common.title = "SAS/C Sample Game"; game = wdopen(&olst_common, &rc); if (game == NULL) { fs_error_warn("wdopen", rc ,(__LINE__-3)); } /* Use the wdrtolst() function to make a copy of */ /* the common open list structure, modify it with */ /* the specifics of the PFBARS window, then open */ /* the window. */ olst_pfbars = wdrtolst(game); strcpy(olst_pfbars->name, "PFBARS "); olst_pfbars->beg_row = term_attr->prim_row - 4; olst_pfbars->beg_col = 0; olst_pfbars->height = 4; olst_pfbars->width = term_attr->prim_col; olst_pfbars->color = RED; olst_pfbars->title = "PF Key Assignments"; pfbars = wdopen(olst_pfbars, &rc); if (pfbars == NULL) { fs_error_warn("wdopen", rc ,(__LINE__-3)); } free(olst_pfbars); /* Once the window is open, the WDOLST */ /* structure is no longer required. */ /* Use the wdrtolst() function to make a copy of */ /* the common open list structure, modify it with */ /* the specifics of the HYPER window, then open */ /* the window. */ olst_hyper = wdrtolst(game); strcpy(olst_hyper->name, "HYPER "); olst_hyper->beg_row = 0; olst_hyper->beg_col = 0; olst_hyper->height = term_attr->prim_row - 4; olst_hyper->width = term_attr->prim_col; olst_hyper->color = GREEN; olst_hyper->title = "SAS/C Sample Hypertext Document Viewer"; hyper = wdhyopn(olst_hyper, "dsn:SASC.WDHYPOUT.HYP", NULL, mask, NULL, &rc); if (hyper == NULL) { fs_error_warn("wdopen", rc ,(__LINE__-3)); } free(olst_hyper); /* Once the window is open, the WDOLST */ /* structure is no longer required. */ /* Present the windows in the desired order(BROWSER on */ /* top, GAME behind it, PFBARS behind game, and HYPER */ /* at the bottom. */ wdsettop(hyper); wdsettop(pfbars); wdsettop(game); wdsettop(browser); /* Call the Window interface dispatcher. All further */ /* activity will be controlled by her. */ wdwait(); /* The flow of control will only reach this logical */ /* point when all windows have been terminated. */ wdterm(); } /* end of the main() function */ #eject /* This is the handler for all display manager events */ /* (except those events associatted with the GAMEPOP */ /* popup window, which has its own seperate handler). */ int event_hd(WDWI *wdwi, int event, int parm1, void *parm2) { WDWI *pfbars; /* Pointer to the PFBARS window */ WDWI *game; /* Pointer to the GAME window */ WDWI *browser; /* Pointer to the BROWSER window */ WDWI *hyper; /* Pointer to the HYPER window */ /* Get the pointer values from the dispatcher. */ pfbars = wdinqwd("PFBARS "); game = wdinqwd("GAME "); browser = wdinqwd("BROWSER "); hyper = wdinqwd("HYPER "); /* Update the time and date on all events except */ /* initialization and termination events. At */ /* these two events, the PFBARS window may not */ /* always be available. */ if (event != WEVT_INIT && event != WEVT_TERM) time_update(pfbars); /* The action taken by the handler depends on what kind of */ /* event has occurred. */ switch (event) { case WEVT_PFK: /* A PF Key was depressed, */ switch (parm1) /* which one was it? */ { case PF_7: /* Scroll all eligible subwindows up a full page */ case PF_19: /* if the active window is the BROWSER window. */ if (strcmp(wdwi->name,"BROWSER ") == 0) wdscroll(wdwi, WD_SCROLL_ALL, 0, WD_UP, WSCR_PAGE); break; case PF_8: /* Scroll all eligible subwindows down a full page */ case PF_20: /* if the active window is the BROWSER window. */ if (strcmp(wdwi->name,"BROWSER ") == 0) wdscroll(wdwi, WD_SCROLL_ALL, 0, WD_DOWN, WSCR_PAGE); break; case PF_10: /* Scroll all eligible subwindows left a full page */ case PF_22: wdscroll(wdwi, WD_SCROLL_ALL, 0, WD_LEFT, WSCR_PAGE); break; case PF_11: /* Scroll all eligible subwindows right a full page */ case PF_23: wdscroll(wdwi, WD_SCROLL_ALL, 0, WD_RIGHT, WSCR_PAGE); break; case PF_3: /* Terminate the window interface environment */ case PF_15: return(WRET_TERM); break; case PF_9: /* Select a new "top" window */ case PF_21: wdsetnxt(); /* Put the next window on top... */ /* if that window happens to be PFBARS.... */ if (strcmp(wdinqtop()->name,"PFBARS ") == 0) { /* if PFBARS is not overlapping ANY other window...*/ /* put some other window on top. (Because the */ /* PFBARS window doesn't DO anything! It just pre- */ /* sents information about what the PF Keys do.) */ if (wdovlap(pfbars,game) == 0 && wdovlap(pfbars,browser) == 0 && wdovlap(pfbars,hyper) == 0) wdsetnxt(); } break; case PF_2: /* Move the current window according to subsequent */ case PF_14: /* cursor position. */ wdmove(WD_ANY_KEY, 0); break; case PF_4: /* Change the size of the current window according */ case PF_16: /* to subsequent cursor position. */ wdgrow(WD_ANY_KEY, 0); break; case PF_5: /* Zoom the current window to the physical maximum */ case PF_17: /* -OR- Unzoom the current window to original size */ wdzoom(wdwi, WD_ZOOM_TOGGLE); break; default: /* No action is defined for these PF Keys */ case PF_1: case PF_6: case PF_12: case PF_13: case PF_18: case PF_24: break; } /* end of switch(parm1) */ return (WRET_PASSTHRU); break; case WEVT_WARN: /* A Window interface warning has been detected, */ /* Call the routine that prints the message. */ return(wd_error_warn("WEVT_WARN", wdwi, parm1, parm2)); break; case WEVT_ERROR: /* A Window interface error has been detected, */ /* Call the routine that prints the message. */ return(wd_error_warn("WEVT_ERROR", wdwi, parm1, parm2)); break; case WEVT_INIT: /* A new window has just opened up! Go paint it */ /* up with the appropriate stuff! */ if (strcmp(wdwi->name,"BROWSER ") == 0) return(browse_init(wdwi)); if (strcmp(wdwi->name,"PFBARS ") == 0) pfbars_init(wdwi); if (strcmp(wdwi->name,"GAME ") == 0) game_init(wdwi); return(WRET_PASSTHRU); break; case WEVT_ENTER: /* The operator pressed enter while the cursor */ /* was in some window. If it was the GAME */ /* window, play the game; otherwise do nothing.*/ if (strcmp(wdwi->name,"GAME ") == 0) game_play(wdwi,parm2); return (WRET_PASSTHRU); break; default: /* For all other events, do nothing and return */ return (WRET_PASSTHRU); break; } /* end of switch(event) */ } /* end of event_hd() */ #eject /* This function is called by the event handler to initialize the */ /* BROWSER window. */ int browse_init(WDWI *browser) { WDSW *sub_browser; /* Pointer to the browsing subwindow */ char buf(|80|); /* A buffer to read the records into */ int program_size; /* Number of source lines in this pgm */ FILE *fp; int i, rc; /* Call the line_count function to determine the number of source */ /* lines in this program. That number will be used to determine */ /* the size of the subwindow in which the browsing takes place. */ program_size = line_count(); /* Define a large subwindow in which to place records for browsing */ /* This particular technique is not put forward as "best" or even */ /* as particularly effecient, but it does illustrate the use of */ /* subwindows and subwindow scrolling nicely. */ sub_browser = wddfsw(browser, 0, 0, program_size, browser->use_width, 0 ,0 ," ", PROTECTED, WHITE, ' ', WD_VERT_DATA, &rc); /* IMPORTANT!!!!! You will need to modify the filename to whatever */ /* your local installer choose. A nice feature of this sample is */ /* that it browses its own source code; allowing you to following */ /* the bouncing ball! Also, the actual file name is operating */ /* system dependent; which is no problem since SAS/C automatically */ /* defines CMS as a symbol when compiling on CMS and OSVS when */ /* compiling on an OS-derivative operating system. */ #ifdef CMS fp = fopen("cms:LCSAMPLE MACLIB * MEMBER WDHYPER","rb"); #else fp = fopen("dsn:SASC.SAMPLE(WDHYPER)","rb"); #endif if (fp == NULL) { printf("Unable to open file for browsing\n"); return(WEVT_TERM); } /* Read the records and fill up the subwindow */ for(i=0; i < program_size ; i++) { rc = fread(buf,80,1,fp); if (feof(fp)) break; if (rc == 0 ) { printf("Unsuccessful fread during subwindow load\n"); return(WRET_FAILED); } /* Place the record into the subwindow */ wdupdsw(sub_browser, UPD_TEXT, i, 0, buf); } /* end of for(...) */ } /* end of browse_init() */ /* This function is called by the event handler to initialize the */ /* PFBARS window. */ void pfbars_init(WDWI *pfbars) { char *pf_string1 = " PF1 PF2 PF3 PF4 PF5 PF6 PF7 " "PF8 PF9 PF10 PF11 PF12"; char *pf_string2 = " NULL Move End Grow Zoom NULL Up " "Down Next LEFT RIGHT NULL"; /* Cause the current time and date to be placed in the PFBARS title */ time_update(pfbars); /* Paint in the PF Key numbers and values */ wdupdln(pfbars, DEF_FIELD, 0, 0, pf_string1, PROTECTED, RED, USE_TEXT_LEN, 0); wdupdln(pfbars, DEF_FIELD, 1, 0, pf_string2, PROTECTED, RED, USE_TEXT_LEN, 0); } /* end of pfbars_init() */ /* This function is called by pfbars_init() and event_hd() to */ /* keep the time and date in the PFBARS title up-to-date. */ void time_update(WDWI *pfbars) { char *time_string; time_t now; time(&now); /* Get the current Greenwich Mean Time */ time_string = ctime(&now); /* and convert it to something readable */ sprintf(title_string,"PF Key Assignments--%.24s",time_string); /* Update the title field in the PFBARS window */ wdupdln(pfbars, UPD_TEXT, WD_TITLE, 0, title_string, 0,0, USE_TEXT_LEN, BLANK); } /* end of time_update() */ /* This function is called by the event handler to initialize the */ /* GAME window. */ void game_init(WDWI *game) { time_t now; /* Use the current "seconds after the minute" to seed the psuedo- */ /* random number generator. Then generate a psuedorandom number. */ time(&now); srand(gmtime(&now)->tm_sec); for(;;) { game_number = rand(); /* Ensure that the number is between 1 and 1000, inclusive */ if (game_number > 0 && game_number <= 1000) break; } /* end of for(...) */ /* Paint in the game instructions and initial field values */ /* Notice that when defining a field, all of the parameters*/ /* to wdupdln() are required. */ wdupdln(game,DEF_FIELD, 0, 5, "I have randomly chosen a number between", PROTECTED, BLUE, USE_TEXT_LEN, 0); wdupdln(game,DEF_FIELD, 1, 5, "1 and 1000. I'll give you 20 guesses.", PROTECTED, BLUE, USE_TEXT_LEN, 0); wdupdln(game,DEF_FIELD, 2, 5, "Each time I'll tell you if you are too", PROTECTED, BLUE, USE_TEXT_LEN, 0); wdupdln(game,DEF_FIELD, 3, 5, "high or too low. ", PROTECTED, BLUE, USE_TEXT_LEN, 0); /* Line 4 is blank */ wdupdln(game,DEF_FIELD, 5, 5, "Guesses Left Last Guess Result", PROTECTED, BLUE, USE_TEXT_LEN, 0); wdupdln(game,DEF_FIELD, GAME_RESULT_LINE, GAME_RESULT_GUESSES_LEFT, "20", PROTECTED, GREEN, USE_TEXT_LEN, 0); wdupdln(game,DEF_FIELD, GAME_RESULT_LINE, GAME_RESULT_LAST_GUESS, "0000", PROTECTED, GREEN, USE_TEXT_LEN, 0); wdupdln(game,DEF_FIELD, GAME_RESULT_LINE, GAME_RESULT_FIELD, "Good Luck", PROTECTED, GREEN, USE_TEXT_LEN, 0); /* Line 7 is blank */ wdupdln(game,DEF_FIELD, 8, 21, "Next Guess", PROTECTED, BLUE, USE_TEXT_LEN, 0); wdupdln(game,DEF_FIELD, GAME_GUESS_LINE, GAME_GUESS_FIELD, "0000", BRIGHT+NUMERIC, CYAN, USE_TEXT_LEN, 0); wdsetlcr(game,GAME_GUESS_LINE,GAME_GUESS_FIELD); } /* end of game_init() */ /* This function is called by the event handler to actually play */ /* the game when the enter key is pressed. */ void game_play(WDWI *game, WDINPUT *input) { int guess_value; char *text_ptr; int text_max_len; char guess_left_text(|3|) = "\0\0\0"; int guess_left_value; char guess_result_text(|10|); char previous_guess_text(|5|) = "\0\0\0\0\0"; /* Determine if the player has correctly guessed the number.*/ /* If the player actually entered a guess, use the WD_INPUT */ /* structure to access the guess value. If the player did */ /* not "modify" the guess field, then the program is forced */ /* to "reuse" the previous guess. The previous guess is */ /* recovered by directly accessing the data in the window */ /* via the wdgetln() function. */ if (input->first_inp_field != NULL) guess_value = atoi(input->first_inp_field->inp_data); else { wdgetln(game, GET_TEXT, GAME_GUESS_LINE, GAME_GUESS_FIELD, &text_ptr, NULL, NULL, &text_max_len, NULL); guess_value = atoi(text_ptr); } if (guess_value == game_number) { strcpy(game_result_message,"You won! Hit any PF to continue."); /* Cause a popup window to be opened and displayed. Note */ /* that the GAMEPOP window has a unique event handler. */ /* This is not required, but it does lead to a clearer */ /* programming style. */ wdpopup(&olst_gamepop,0); /* Cause another initialization event to happen for the */ /* GAME window. This is how the game is reset for further*/ /* play. (The event will be handled asynchronously.) */ wdpostev(game,WEVT_INIT,NULL,NULL,WD_WINDOW_PRIORITY,NULL); return; } /* Retrieve the number of guesses remaining directly from the */ /* data in the window. Of course, this same data could have */ /* been kept in a program variable, but this is a sample pgm! */ wdgetln(game, GET_TEXT, GAME_RESULT_LINE, GAME_RESULT_GUESSES_LEFT, &text_ptr, NULL, NULL, &text_max_len, NULL); guess_left_value = atoi(text_ptr); /* Deterimine if the player has run out of guesses */ if (guess_left_value == 1) { strcpy(game_result_message,"You lost. Hit any PF to continue."); /* Process the popup window in the same manner as above. */ wdpopup(&olst_gamepop,0); wdpostev(game,WEVT_INIT,NULL,NULL,WD_WINDOW_PRIORITY,NULL); return; } /* Having neither won nor lost; prepare for another round */ /* by decrementing the number of remaining guesses, saving */ /* the previous guess, and determining whether the player */ /* guessed high or low. */ sprintf(guess_left_text,"%-2d\0",--guess_left_value); wdupdln(game,UPD_TEXT, GAME_RESULT_LINE, GAME_RESULT_GUESSES_LEFT, guess_left_text, 0, 0, USE_TEXT_LEN, 0); sprintf(previous_guess_text,"%-4d\0",guess_value); wdupdln(game,UPD_TEXT, GAME_RESULT_LINE, GAME_RESULT_LAST_GUESS, previous_guess_text, 0, 0, USE_TEXT_LEN, 0); if (guess_value > game_number) strcpy(guess_result_text,"Too big"); else strcpy(guess_result_text,"Too small"); wdupdln(game,UPD_TEXT, GAME_RESULT_LINE, GAME_RESULT_FIELD, guess_result_text, 0, 0, USE_TEXT_LEN, 0); wdsetlcr(game,GAME_GUESS_LINE,GAME_GUESS_FIELD); } /* end of game_play() */ /* This is the unique event handler for the GAMEPOP popup window */ /* A unique handler is NOT required for a window, but this one */ /* demonstrates that it is a possible style consideration. */ int gamepop_hd(WDWI *wdwi, int event, int parm1, void *parm2) { switch (event) { case WEVT_INIT: /* Display the result of the game */ wdalarm(); /* Sound the horn, in glory or shame */ wdupdln(wdwi,DEF_FIELD, 0, 0, game_result_message, PROTECTED, YELLOW, USE_TEXT_LEN, 0); return(WRET_PASSTHRU); break; case WEVT_PFK: /* Close the popup window on any PF key */ return(WRET_TERM); break; case WEVT_WARN: /* A Window interface warning has been detected, */ /* Call the routine that prints the message. */ return(wd_error_warn("WEVT_WARN", wdwi, parm1, parm2)); break; case WEVT_ERROR: /* A Window interface error has been detected, */ /* Call the routine that prints the message. */ return(wd_error_warn("WEVT_ERROR", wdwi, parm1, parm2)); break; default: return(WRET_PASSTHRU); break; } /* end of switch(event) */ } /* end of gamepop_hd() */ #eject /* This function is called by both the common event handler and */ /* the unique GAMEPOP handler. It's job is to emit Window */ /* interface warnings and errors. */ int wd_error_warn(char *type, WDWI *wdwi, int parm1, void *parm2) { char window_name(|8|); if (wdwi) memcpy(window_name,wdwi->name,8); else strcpy(window_name,"NULL "); printf("%s rc = %d for window = %.8s warn_text = %.80s\n", type, parm1, window_name, (char *) parm2); return(parm1); /* reflect back error code */ } /* end of wd_error_warn() */ /* This function issues FSSL warnings and errors */ void fs_error_warn(char *fn, int rc, int line) { if (!rc) return; if (rc > 0) printf("%s warning rc = %d at line %d\n", fn, rc, line); else printf("%s error rc = %d at line %d\n", fn, rc, line); printf("error text %s\n\n", err_buf); } /* end of fs_error_warn() */ /* This is an unusual function. It makes use of the __LINE__ */ /* ANSI macro which always contains the number of the current */ /* source line. Since this function is at the bottom of the */ /* source program, the actual size of the source can be dyn- */ /* amically determined at runtime. */ int line_count(void) { return(__LINE__ + 1); } /* end of line_count() and the end of all source code */