/**************************************************************** * * Name: WASHER * * Function: Emulate a washing machine control panel. * * Shows how to: 1. construct complex menus (dialogues) including * select, input, output, and inactive fields. * 2. change field types dynamically. * 3. implement "radio button" select fields. * 4. use a timer object to measure time intervals. * 5. use an objectq to wait for multiple events. * ****************************************************************/ #include #include "dvapi.h" /* minimum API version required */ #define required 0x201 /* possible values of the "temperature" variable */ #define HOT 0 #define WARM 1 #define COLD 2 /* possible values of the "state" variable */ #define IDLE 0 #define WASHING 1 #define FIRST_RINSE 2 #define FIRST_SPIN 3 #define FINAL_RINSE 4 #define FINAL_SPIN 5 /* actual API version number */ int version; /* object handles */ ulong winme,pan,win,kbd,tim,obj; /* variables used when reading from the menu */ char *kptr,*kend,field,field1[3]; int klng,kstatus,fsize; /* variables set according to menu input */ int wash_time,temperature,second_rinse,bell; /* state related variables */ int state,indicator,done; /* variables for saving the cursor position */ int row,col; /* panel library filename in ASCIIZ string format */ char lib[] = "examples.plb\x00"; /* name of panel within panel library */ char name[] = "washer"; /* status of last pan_open and pan_apply operation */ int status; /********************************************************************** * main - check for DESQview present and enable required extensions. ***********************************************************************/ main () { /* initialize C interfaces and get API version number */ version = api_init(); /* if DESQview is not running or version is too low, display a message */ if (version < required) { printf ("This program requires DESQview version %d.02%d or later.\n", required/256,required%256); } /* tell DESQview what extensions to enable and start application */ else { api_level (required); program_body(); } /* disable C interfaces and return from program */ api_exit(); } /********************************************************************** * program_body - initialize application and loop processing events. ***********************************************************************/ program_body () { /* get task window handle and open objectq */ winme = win_me(); obq_open(); /* create timer object */ tim = tim_new(); /* create and open panel object, and associate it with panel library */ pan = pan_new(); status = pan_open (pan,lib,sizeof (lib)); /* apply named panel, and return window & keyboard handles */ status = pan_apply (pan,winme,name,strlen (name),&win,&kbd); /* preselect "hot water". Jump cursor to field 1. Set "state" to idle */ radio_button (win,2,4,2); fld_cursor (win,1); change_state (IDLE,0); done = 0; /* position menu, mark as displayable, and make topmost in application. */ win_top (win); /* loop until "done" becomes TRUE */ while (!done) { /* wait for input from any open object and return its handle */ obj = obq_read(); /* determine which object it is and process accordingly */ if (obj == kbd) process_menu_event(); else if (obj == tim) process_timer_event(); }; /* free all allocated objects and return */ key_free (kbd); win_free (win); pan_free (pan); tim_free (tim); } /********************************************************************** * process_menu_event - process data returned from the menu. ***********************************************************************/ process_menu_event () { /* get menu data and determine what event caused data to be returned */ key_read (kbd,&kptr,&klng); kstatus = key_status (kbd); /* beep and return if anything but a field selection */ if (kstatus != 1) { api_sound (1000,5); return; }; /* point just past returned data. Save current cursor position. */ kend = kptr+klng; qry_cursor (win,&row,&col); /* loop once for each field returned */ while (kptr < kend) { /* get field # and length. Log field info to task window. */ field = *kptr; fsize = *(int *)(kptr+1); win_printf (winme,"field = %d length = %d contents = ",field,fsize); win_write (winme,kptr+3,fsize); win_printf (winme,"\n"); /* dispatch based on field number */ switch (field) { case 1: /* wash time changed */ /* copy returned data to string variable and zero terminate */ memcpy (field1,kptr+3,2); field1[2] = 0; /* convert to integer, clip at zero, and set state to IDLE */ wash_time = atoi (field1); if (wash_time < 0) wash_time = 0; change_state (IDLE,0); break; case 2: /* Hot water selected - Select field 2. Deselect fields 3 and 4. Log temperature. */ radio_button (win,2,4,2); temperature = HOT; break; case 3: /* Warm water selected - Select field 3. Deselect fields 2 and 4. Log temperature. */ radio_button (win,2,4,3); temperature = WARM; break; case 4: /* Cold water selected - Select field 4. Deselect fields 2 and 3. Log temperature. */ radio_button (win,2,4,4); temperature = COLD; break; case 5: /* Second rinse - if the field data is "Y", the field is selected. Otherwise, the data will be "N". */ second_rinse = (*(kptr+3) == 'Y'); break; case 6: /* Beep when done - if the field data is "Y", the field is selected. Otherwise, the data will be "N". */ bell = (*(kptr+3) == 'Y'); break; case 7: /* Start button */ /* deselect field so it does not remain highlighted */ fld_type (win,7,FLT_DESELECT); /* ignore if no wash time has been selected. Otherwise ... */ if (wash_time != 0) { /* convert field 1 to an output field. Disable the start button and enable the stop button */ fld_type (win,1,FLT_OUTPUT); fld_type (win,7,FLT_INACTIVE); fld_type (win,8,FLT_DESELECT); /* set timer to run 1 second. If IDLE, set state to WASHING. */ tim_addto (tim,100L); if (state == IDLE) change_state (WASHING,10); } break; case 8: /* Stop button - stop cycle and reset field types. */ stop_cycle(); break; case 9: /* Exit button - stop cycle, reset fields, and set "done". */ stop_cycle(); done = 1; break; default: /* unknown field number - should never happen. */ win_printf (winme,"impossible!\n"); } /* bump pointer to next field and loop */ kptr += (fsize+3); } /* restore original cursor position */ win_cursor (win,row,col); } /********************************************************************** * process_timer_event - process timer expiration ***********************************************************************/ process_timer_event () { long time; /* read the timer object to clear the event */ time = tim_read(tim); /* save cursor position. Decrement time remaining and display. */ wash_time -= 1; qry_cursor (win,&row,&col); fld_cursor (win,1); win_printf (win,"%2d",wash_time); /* if the clock has expired, dispatch based on current state. In each case, switch to the next state and light the appropriate indicator. */ if (wash_time == 0) { switch (state) { case WASHING: change_state ((second_rinse) ? FIRST_RINSE:FINAL_RINSE, 11); break; case FIRST_RINSE: change_state (FIRST_SPIN,12); break; case FIRST_SPIN: change_state (FINAL_RINSE,11); break; case FINAL_RINSE: change_state (FINAL_SPIN,12); break; case FINAL_SPIN: /* Cycle complete - switch to IDLE state, beep if requested. Restore original field types. */ change_state (IDLE,0); if (bell == 1) api_sound (2000,18); stop_cycle(); break; } /* unless we are now IDLE, we need to start a rinse or spin cycle. do so by setting the clock to 3 seconds and setting the timer to expire in 1 second. */ if (state != IDLE) { wash_time = 3; tim_addto (tim,100L); } } /* if clock is still counting, simply set timer for another second */ else tim_addto (tim,100L); /* restore cursor to its original position */ win_cursor (win,row,col); } /********************************************************************** * radio_button - select a specified field and deselect all others in * the given range. ***********************************************************************/ radio_button (win,first,last,chosen) ulong win; int first,last,chosen; { int i; /* loop for each field in range "first" through "last" */ for (i=first; i<=last; i++) /* change "chosen" field type to SELECTed, others to DESELECTed */ fld_type (win,i,(i==chosen) ? FLT_SELECT : FLT_DESELECT); } /********************************************************************** * change_state - changes the current state of the wash cycle and * lights the specified indicator. The previously * lighted indicator, if any, is turned off. ***********************************************************************/ change_state (newstate,field) int newstate,field; { /* log new state */ state = newstate; /* if an indicator is ON, turn it OFF */ if (indicator != 0) fld_attr (win,indicator,1); /* turn ON the requested indicator and remember it */ if (field != 0) fld_attr (win,field,5); indicator = field; } /********************************************************************** * stop_cycle - stops the current timer, if any. Changes field 1 back * to an input field, enables the start button, and * disables the stop button. ***********************************************************************/ stop_cycle () { tim_erase (tim); fld_type (win,1,FLT_INPUT); fld_type (win,7,FLT_DESELECT); fld_type (win,8,FLT_INACTIVE); }