home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / listings / v_11_08 / weber / gui.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-12  |  24.1 KB  |  689 lines

  1. /***************************************************************
  2.  * file: GUI.C
  3.  * purpose: simple windowing interface based on the Zortech flash graphics library
  4.  * contains:
  5.  *  gui_open(void);             must be called before using gui
  6.  *  gui_close(void);            called after finished with gui
  7.  *  object_add(short id,void (*message_handler)(MESSAGE *message,void *data),void *data,fg_pbox_t screen); add object to active list
  8.  *  object_remove(void *data);  removes object from active list
  9.  *  object_exists(void *data);  returns pointer to object or NULL if none
  10.  *  message_get(MESSAGE *message);      gets next message from input devices or system
  11.  *  message_send(MESSAGE *message);     send a message down the list
  12.  *  message_send_object(MESSAGE *message,void *data);   send a message to a particular object
  13.  *  message_box(char *str);     puts a message box on the screen
  14.  *  error_box(short id);        displays a box for errors
  15.  *  yn_box(char *str);          displays a message box and waits for y/n response
  16.  *  exclusive_focus_set(void *data);    sets focus to a particular object
  17.  *  exclusive_focus_clear(void *data);  clears focus restriction of an object
  18.  *  input_handler_set_default(void (*message_handler)(MESSAGE *message)); message handler to be invoked when no other objects want message
  19.  *  screen_clear(void);         clears screen to system background color
  20.  * system: Written for the flash graphics library in Zortech 3.0
  21.  *          There is an MSDOS dependency in _bios_keybrd()
  22.  * copyright: 1991 by David Weber.  All rights reserved.
  23.  *  This software can be used for any purpose as object, library or executable.
  24.  *  It cannot be sold for profit as source code.
  25.  * history:
  26.  *  12-17-91 - initial code
  27.  *  01-31-93 - this code is now obsolete, see the CPP gui package
  28.  **************************************************************/
  29.  
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <bios.h>
  34. #include <ctype.h>
  35. #include <signal.h>
  36. #include <cerror.h>
  37. #define GUI_SOURCE
  38. #include "gui.h"
  39.  
  40. /* sizes of things */
  41. #define MESSAGE_QUEUE_SIZE 64   /* depth of message queue before messages get lost */
  42. #define EXCLUSIVE_FOCUS_STACK_SIZE 16   /* number of exclusive focus requests that can be stacked */
  43.  
  44. /* global data */
  45. short gui_errno;                                /* errno for gui, last error or 0 if OK */
  46. short gui_screen_width,gui_screen_height;       /* gui screen dimensions */
  47. short gui_char_width,gui_char_height;           /* gui text cell dimensions */
  48.  
  49. /* local data */
  50. /* objects */
  51. static GOB first_object = {OBJECT_NULL,NULL,NULL,-1,-1,-1,-1,NULL};
  52. static GOB *exclusive_focus[EXCLUSIVE_FOCUS_STACK_SIZE];    /* for restricting focus to a specific object */
  53. static short exclusive_focus_count = 0;
  54. static void (*default_input_handler)(MESSAGE *message) = NULL;  /* message handler when no other objects want the input message */
  55. /* mouse */
  56. static unsigned short last_mouse_state = 0;     /* mouse buttons */
  57. static short last_mouse_x,last_mouse_y;         /* mouse position */
  58. /* message queue */
  59. static MESSAGE message_queue[MESSAGE_QUEUE_SIZE];   /* circular message queue and pointers */
  60. static short message_queue_head,message_queue_tail;
  61.  
  62.  
  63. /* local prototypes */
  64. static short scan_object_list(MESSAGE *message);/* pass current message to all objects */
  65. static short getkey(void);                      /* get a key from the keyboard */
  66. static short checkey(void);                     /* see if there is a key at the keyboard */
  67. static short wait_key_or_mouse_click(short *x,short *y);        /* wait until the user presses a key or clicks the mouse */
  68. static void message_box_handler(MESSAGE *message,void *data);   /* handles message box messages */
  69. static short message_box_core(char *str,short yn);  /* core of message box display */
  70. static int _far critical_error_handler(int *ax,int *di);        /* handle critical errors */
  71.  
  72.  
  73.  
  74. /************************************************
  75.  * function: short gui_open(void)
  76.  *  This function must be called before using the gui
  77.  * parameters: none
  78.  * returns: 1 if OK or 0 if cannot open
  79.  ************************************************/
  80. short gui_open(void)
  81.     {
  82.     MESSAGE message;
  83.  
  84.     if (fg_init() == FG_NULL)       /* initialize */
  85.         return 0;
  86.     if (signal(SIGTERM,SIG_IGN) == SIG_ERR)     /* turn off ^C terminate */
  87.         return 0;
  88. #ifndef DEBUG
  89.     _cerror_handler = critical_error_handler;
  90.     cerror_open();                  /* turn on critical error redirection */
  91. #endif
  92.     if (fg.nsimulcolor < 16)        /* if monochrome then map colors appropriately */
  93.         {
  94.         /* black */
  95.         fg_setcolornum(FG_BLUE,FG_BLACK);
  96.         fg_setcolornum(FG_GREEN,FG_BLACK);
  97.         fg_setcolornum(FG_CYAN,FG_BLACK);
  98.         fg_setcolornum(FG_RED,FG_BLACK);
  99.         fg_setcolornum(FG_MAGENTA,FG_BLACK);
  100.         fg_setcolornum(FG_BROWN,FG_BLACK);
  101.         fg_setcolornum(FG_GRAY,FG_BLACK);
  102.         /* white */
  103.         fg_setcolornum(FG_LIGHT_BLUE,FG_WHITE);
  104.         fg_setcolornum(FG_LIGHT_GREEN,FG_WHITE);
  105.         fg_setcolornum(FG_LIGHT_CYAN,FG_WHITE);
  106.         fg_setcolornum(FG_LIGHT_RED,FG_WHITE);
  107.         fg_setcolornum(FG_LIGHT_MAGENTA,FG_WHITE);
  108.         fg_setcolornum(FG_YELLOW,FG_WHITE);
  109.         fg_setcolornum(FG_LIGHT_WHITE,FG_WHITE);
  110.         }
  111.     first_object.next = NULL;       /* initialize object list */
  112.     message_queue_head = message_queue_tail = 0;    /* initialize message queue */
  113.     exclusive_focus_count = 0;      /* initialize exclusive focus stack */
  114.     gui_errno = 0;                  /* initialize gui error */
  115.     gui_screen_width = fg_box_width(fg.displaybox);     /* set up gui dimensions */
  116.     gui_screen_height = fg_box_height(fg.displaybox);
  117.     gui_char_width = fg_box_width(fg.charbox);
  118.     gui_char_height = fg_box_height(fg.charbox);
  119.     if (fg.msm)
  120.         {
  121.         fg_msm_setcurpos((fg.displaybox[FG_X2]-fg.displaybox[FG_X1])/2+fg.displaybox[FG_X1],(fg.displaybox[FG_Y2]-fg.displaybox[FG_Y1])/2+fg.displaybox[FG_Y1]);
  122.         fg_msm_showcursor();
  123.         fg_flush();
  124.         last_mouse_state = fg_msm_getstatus((fg_coord_t *)&last_mouse_x,(fg_coord_t *)&last_mouse_y);
  125.         }
  126.     message.id = M_START;           /* send start message */
  127.     message_send(&message);
  128.     screen_clear();
  129.     return 1;
  130.     }
  131.  
  132.  
  133. /************************************************
  134.  * function: void gui_close(void)
  135.  *  must be called after finished with gui
  136.  * parameters: none
  137.  * returns: none
  138.  ************************************************/
  139. void gui_close(void)
  140.     {
  141.     GOB *o,*o2;
  142.  
  143.     o = first_object.next;      /* free all objects */
  144.     while (o != NULL)
  145.         {
  146.         o2 = o;
  147.         o = o->next;
  148.         fg_free_handle(o2->save_area);
  149.         free(o2);
  150.         }
  151.     fg_msm_hidecursor();
  152. #ifndef DEBUG
  153.     cerror_close();             /* turn off critical error redirection */
  154. #endif
  155.     fg_term();                  /* return to text mode */
  156.     }
  157.  
  158.  
  159. /************************************************
  160.  * function: short object_add(short id,void (*message_handler)(MESSAGE *message,void *data),void *data,fg_pbox_t screen)
  161.  *  add object to active list
  162.  * hide the cursor before calling this and restore it afterwards
  163.  * parameters: id for object, function pointer to message handler for object,
  164.  *             pointer to data for object, dimensions of object
  165.  * returns: 1 if OK or 0 if failed
  166.  *  note: if the message handler for object wishes to eat the message it should
  167.  *        change to message to M_NONE
  168.  ************************************************/
  169. short object_add(short id,void (*message_handler)(MESSAGE *message,void *data),void *data,fg_pbox_t screen)
  170.     {
  171.     GOB *o;
  172.     MESSAGE error;
  173.  
  174.     if ((o = (GOB *) malloc(sizeof (GOB))) == NULL) /* allocate object */
  175.         {
  176.         error.id = gui_errno = M_NOMEM;
  177.         error.data.ptr_data = object_add;
  178.         message_send(&error);
  179.         return 0;
  180.         }
  181.     o->next = NULL;
  182.     o->id = id;
  183.     o->message_handler = message_handler;
  184.     o->data = data;
  185.     fg_box_cpy(o->screen,screen);
  186.     fg_boxclip(fg.displaybox,o->screen,o->screen);
  187.     if ((o->save_area = fg_save(o->screen)) == NULL)
  188.         {
  189.         free(o);
  190.         error.id = gui_errno = M_NOMEM;
  191.         error.data.ptr_data = object_add;
  192.         message_send(&error);
  193.         return 0;
  194.         }
  195.     o->next = first_object.next;        /* add object to list */
  196.     first_object.next = o;
  197.     return 1;
  198.     }
  199.  
  200.  
  201. /************************************************
  202.  * function: short object_remove(void *data)
  203.  *  remove an object from the active object list
  204.  * hide the cursor before calling this and restore it afterwards
  205.  * parameters: pointer to data of object (this is essentially the "handle")
  206.  * returns: 1 if OK or 0 if FAIL
  207.  ************************************************/
  208. short object_remove(void *data)
  209.     {
  210.     GOB *o,*o2;
  211.  
  212.     o = first_object.next;
  213.     o2 = &first_object;
  214.     while (o != NULL)       /* scan entire object list */
  215.         {
  216.         if (data == o->data)    /* for object of interest */
  217.             {
  218.             o2->next = o->next; /* unlink it */
  219.             fg_restore(o->save_area);   /* restore background */
  220.             free(o);            /* clear it */
  221.             return 1;
  222.             }
  223.         o2 = o;
  224.         o = o->next;
  225.         }
  226.     return 0;
  227.     }
  228.  
  229.  
  230. /************************************************
  231.  * function: GOB *object_exists(void *data)
  232.  * parameters: pointer to data of an object
  233.  * returns: pointer to object or NULL if object does not exist in chain
  234.  ************************************************/
  235. GOB *object_exists(void *data)
  236.     {
  237.     GOB *o;
  238.  
  239.     for (o = first_object.next ; o != NULL ; o = o->next)
  240.         if (data == o->data)
  241.             break;
  242.     return o;
  243.     }
  244.  
  245.  
  246. /************************************************
  247.  * function: short message_get(MESSAGE *message)
  248.  * parameters: pointer to MESSAGE struct
  249.  * returns: message id
  250.  ************************************************/
  251. short message_get(MESSAGE *message)
  252.     {
  253.     short c;
  254.     unsigned short mouse_state;
  255.     short x,y;
  256.  
  257.     for (;;)
  258.         {
  259.         if (message_queue_tail != message_queue_head)   /* check message queue */
  260.             {
  261.             *message = message_queue[message_queue_head];
  262.             if (++message_queue_head >= MESSAGE_QUEUE_SIZE)
  263.                 message_queue_head = 0;
  264.             if (scan_object_list(message))
  265.                 break;
  266.             }
  267.         if ((c = checkey()) != -1)      /* check keyboard */
  268.             {
  269.             message->data.short_data.x = c;
  270.             message->id = M_KEY;
  271.             if (scan_object_list(message))
  272.                 break;
  273.             }
  274.         if (fg.msm)                     /* check mouse */
  275.             {
  276.             mouse_state = fg_msm_getstatus((fg_coord_t *)&x,(fg_coord_t *)&y);
  277.             if (mouse_state != last_mouse_state)    /* if buttons changed */
  278.                 {
  279.                 c = last_mouse_state;
  280.                 last_mouse_state = mouse_state;     /* update last state */
  281.                 last_mouse_x = x;
  282.                 last_mouse_y = y;
  283.                 if (c != 0)
  284.                     continue;                       /* button went up or playing chords */
  285.                 if (mouse_state & FG_MSM_MIDDLE)
  286.                     message->id = M_MOUSE_CENTER;
  287.                 if (mouse_state & FG_MSM_RIGHT)
  288.                     message->id = M_MOUSE_RIGHT;
  289.                 if (mouse_state & FG_MSM_LEFT)
  290.                     message->id = M_MOUSE_LEFT;
  291.                 message->data.short_data.x = x;     /* update message coordinates */
  292.                 message->data.short_data.y = y;
  293.                 if (scan_object_list(message))
  294.                     break;
  295.                 }
  296.             }
  297.         }
  298.     return message->id;
  299.     }
  300.  
  301.  
  302. /************************************************
  303.  * function: void message_send(MESSAGE *message)
  304.  *      queue up a message for message_get
  305.  * parameters: pointer to MESSAGE struct
  306.  * returns: nothing
  307.  ************************************************/
  308. void message_send(MESSAGE *message)
  309.     {
  310.     short old_tail;
  311.  
  312.     old_tail = message_queue_tail;
  313.     message_queue[message_queue_tail] = *message;
  314.     if (++message_queue_tail >= MESSAGE_QUEUE_SIZE)
  315.         message_queue_tail = 0;
  316.     if (message_queue_tail == message_queue_head)
  317.         {
  318.         message_queue_tail = old_tail;
  319.         message_queue[message_queue_tail].id = gui_errno = M_MESSAGE_OVERFLOW;
  320.         message_queue[message_queue_tail].data.ptr_data = message_send;
  321.         }
  322.     }
  323.  
  324.  
  325. /************************************************
  326.  * function: short message_send_object(MESSAGE *message,void *data)
  327.  *  send a message to a single object
  328.  * parameters: pointer to a message and pointer to data for object (used as a handle)
  329.  * returns: 1 if sent OK or 0 if not in active object queue
  330.  ************************************************/
  331. short message_send_object(MESSAGE *message,void *data)
  332.     {
  333.     GOB *o;
  334.  
  335.     if ((o = object_exists(data)) == NULL)
  336.         return 0;
  337.     (*(o->message_handler))(message,o->data);
  338.     return 1;
  339.     }
  340.  
  341.  
  342. /************************************************
  343.  * function: short message_box(char *str)
  344.  *      puts a message on the screen and waits for a key or mouse click
  345.  * parameters: string for message
  346.  * returns: 0 if failure (string too long or non memory), else 1 if OK
  347.  ************************************************/
  348. short message_box(char *str)
  349.     {
  350.     return message_box_core(str,0);
  351.     }
  352.  
  353.  
  354. /************************************************
  355.  * function: short error_box(short id)
  356.  *  displays an error box with appropriate message if message is in the range of error
  357.  * parameters: id of error message
  358.  * returns: 1 if displayed or 0 if not
  359.  ************************************************/
  360. short error_box(short id)
  361.     {
  362.     char *p;
  363.  
  364.     if (id >= M_MIN_ERROR && id <= M_MAX_ERROR)
  365.         {
  366.         switch (id)
  367.             {
  368.             case M_NOMEM:
  369.                 p = "Out of memory";
  370.                 break;
  371.             case M_MESSAGE_OVERFLOW:
  372.                 p = "Message queue overflow, messages lost";
  373.                 break;
  374.             case M_INVALID_PARMS:
  375.                 p = "A procedure received invalid parameters";
  376.                 break;
  377.             case M_NOT_OPEN:
  378.                 p = "Attempt to access an unopened object";
  379.                 break;
  380.             default:
  381.                 p = "Unknown error";
  382.                 break;
  383.             }
  384.         gui_errno = 0;
  385.         return message_box(p);
  386.         }
  387.     return 0;
  388.     }
  389.  
  390.  
  391.  
  392. /************************************************
  393.  * function: short yn_box(char *str)
  394.  *      puts a message on the screen and waits for a yes/no response
  395.  *  note: the string " Y/N?" is appended to the message
  396.  * parameters: string for message
  397.  * returns: 1 if Yes, 0 if No or failed
  398.  ************************************************/
  399. short yn_box(char *str)
  400.     {
  401.     char local_str[MESSAGE_MAX_STR+1];
  402.     static char yn_str[] = MESSAGE_YN;
  403.  
  404.     strncpy(local_str,str,MESSAGE_MAX_STR-sizeof(yn_str));
  405.     local_str[MESSAGE_MAX_STR-sizeof(yn_str)] = 0;
  406.     strcat(local_str,yn_str);
  407.     return message_box_core(local_str,1);
  408.     }
  409.  
  410.  
  411. /************************************************
  412.  * function: short exclusive_focus_set(void *data)
  413.  *  sets exclusive focus to an object, any object setting the
  414.  *  focus has the responsibility of clearing it.  The mouse is
  415.  *  restricted to the dimensions of the object
  416.  *  NOTE: the cursor should be turned off before calling this function
  417.  * parameters: pointer to an object's data, used as a handle
  418.  * returns: 1 if set or 0 if no such object in active list or stack is full
  419.  ************************************************/
  420. short exclusive_focus_set(void *data)
  421.     {
  422.     GOB *o;
  423.  
  424.     if (exclusive_focus_count >= EXCLUSIVE_FOCUS_STACK_SIZE)
  425.         return 0;
  426.     if ((o = object_exists(data)) == NULL)
  427.         return 0;
  428.     exclusive_focus[exclusive_focus_count++] = o;
  429.     fg_msm_setarea(o->screen);  /* restrict mouse to object */
  430.     fg_msm_setcurpos(fg_coord_midpoint(o->screen[FG_X1],o->screen[FG_X2]),fg_coord_midpoint(o->screen[FG_Y1],o->screen[FG_Y2]));
  431.     return 1;
  432.     }
  433.  
  434.  
  435. /************************************************
  436.  * function: short exclusive_focus_clear(void *data)
  437.  *  clears the exclusive focus of an object and restores the focus of the previous object
  438.  *  NOTE: the cursor should be turned off before calling this function
  439.  * parameters: pointer to an object's data, used as a handle
  440.  * returns: 1 if cleared or 0 if not
  441.  ************************************************/
  442. short exclusive_focus_clear(void *data)
  443.     {
  444.     GOB *o;
  445.     short i;
  446.  
  447.     if ((o = object_exists(data)) == NULL)
  448.         return 0;
  449.     for (i = 0 ; i < exclusive_focus_count ; i++)
  450.         if (exclusive_focus[i] == o)
  451.             {
  452.             exclusive_focus_count = i;
  453.             if (exclusive_focus_count == 0) /* allow mouse to roam previous object */
  454.                 fg_msm_setarea(fg.displaybox);
  455.             else
  456.                 fg_msm_setarea(exclusive_focus[i-1]->screen);
  457.             return 1;
  458.             }
  459.     return 0;
  460.     }
  461.  
  462.  
  463. /************************************************
  464.  * function: void input_handler_set_default(void (*message_handler))
  465.  *  sets up a default message handler for mouse or key events.  This handler
  466.  *  is automatically cleared everytime the object list is scanned with an event.
  467.  *  This means that it is a one shot function that can be set by the message handler
  468.  *  of an object.
  469.  * parameters: pointer to function for handling default messages
  470.  * returns: nothing
  471.  ************************************************/
  472. void input_handler_set_default(void (*message_handler)(MESSAGE *message))
  473.     {
  474.     default_input_handler = message_handler;
  475.     }
  476.  
  477.  
  478. /************************************************
  479.  * function: void screen_clear(void)
  480.  *  clears the screen to the SYSTEM_BACKGROUND color
  481.  * parameters: none
  482.  * returns: none
  483.  ************************************************/
  484. void screen_clear(void)
  485.     {
  486.     fg_msm_hidecursor();
  487.     fg_fillbox(COLOR_SYSTEM_BACKGROUND,FG_MODE_SET,~0,fg.displaybox);
  488.     fg_msm_showcursor();
  489.     fg_flush();
  490.     }
  491.  
  492.  
  493. /* ---------------- LOCAL FUNCTIONS ---------------- */
  494.  
  495. /* scan object list with a message, returns 1 if message available or 0 if M_NONE */
  496. static short scan_object_list(MESSAGE *message)
  497.     {
  498.     GOB *o;
  499.  
  500.     for (o = first_object.next ; o != NULL ; o = o->next)
  501.         {       /* for each object */
  502.         if (exclusive_focus_count > 0 && o != exclusive_focus[exclusive_focus_count-1])
  503.             continue;
  504.         (*(o->message_handler))(message,o->data);
  505.         if (message->id == M_NONE)
  506.             return 0;
  507.         }
  508.     if (default_input_handler != NULL)
  509.         {
  510.         if (message->id >= M_MIN_INPUT && message->id <= M_MAX_INPUT)
  511.             (*default_input_handler)(message);
  512.         default_input_handler = NULL;
  513.         }
  514.     return 1;
  515.     }
  516.  
  517.  
  518. /* waits for a keypress and returns the ASCII value */
  519. /* non-ASCII keys return 256+scan code */
  520. static short getkey(void)
  521.     {
  522.     short c;
  523.  
  524.     while ((c = checkey()) == -1)
  525.         ;
  526.     return c;
  527.     }
  528.  
  529.  
  530. /* checks if key is ready and returns it if so, else returns -1 */
  531. /* non-ASCII keys return 256+scan code */
  532. /* MSDOS dependent */
  533. static short checkey(void)
  534.     {
  535.     unsigned short c;
  536.  
  537.     if (_bios_keybrd(_KEYBRD_READY) == 0)
  538.         return -1;
  539.     c = _bios_keybrd(_KEYBRD_READ);
  540.     if ((c & 0xff) == 0)
  541.         {
  542.         c = ((c & 0xff00) >> 8);
  543.         if (c == 3)
  544.             c = 0;
  545.         else
  546.             c += 256;
  547.         }
  548.     else
  549.         c &= 0xff;
  550.     return (short) c;
  551.     }
  552.  
  553.  
  554. /* wait until the user presses a key or clicks the mouse, returns keypress or -1 if mouse click, mouse values in x,y */
  555. static short wait_key_or_mouse_click(short *x,short *y)
  556.     {
  557.     short c,tx,ty;
  558.  
  559.     while (fg_msm_getstatus((fg_coord_t *)&tx,(fg_coord_t *)&ty))
  560.         ;
  561.     while ((c = checkey()) == -1)
  562.         {
  563.         if (fg_msm_getstatus((fg_coord_t *)x,(fg_coord_t *)y))
  564.             {
  565.             while (fg_msm_getstatus((fg_coord_t *)&tx,(fg_coord_t *)&ty))
  566.                 ;
  567.             return -1;
  568.             }
  569.         }
  570.     return c;
  571.     }
  572.  
  573.  
  574. /* handler for message box */
  575. static void message_box_handler(MESSAGE *message,void *data)
  576.     {
  577.     message->id = M_NONE;
  578.     }
  579.  
  580.  
  581. /* display message and wait for keystroke or mouse click, if yn=1 then waits for yes/no response */
  582. static short message_box_core(char *str,short yn)
  583.     {
  584.     unsigned short dx,dy;
  585.     fg_box_t message_area,box,box2;
  586.     void *temp_data;
  587.     short ret,c,x,y;
  588.  
  589.     temp_data = &temp_data;     /* nothing is assigned to *temp_data, just a handle */
  590.     dx = (strlen(str) + 2) * gui_char_width;
  591.     dy = 2 * gui_char_height;
  592.     if (dx > gui_screen_width || dy > gui_screen_height)
  593.         return 0;
  594.     message_area[FG_X1] = (fg.displaybox[FG_X2] - fg.displaybox[FG_X1])/2 - dx/2 + fg.displaybox[FG_X1];
  595.     message_area[FG_X2] = message_area[FG_X1] + dx;
  596.     message_area[FG_Y1] = (fg.displaybox[FG_Y2] - fg.displaybox[FG_Y1])/2 - dy/2 + fg.displaybox[FG_Y1];
  597.     message_area[FG_Y2] = message_area[FG_Y1] + dy;
  598.     fg_boxclip(fg.displaybox,message_area,message_area);
  599.     fg_msm_hidecursor();
  600.     if (!object_add(OBJECT_MESSAGEBOX,message_box_handler,temp_data,message_area))
  601.         {
  602.         fg_msm_showcursor();
  603.         fg_flush();
  604.         return 0;
  605.         }
  606.     fg_fillbox(COLOR_MESSAGE_BACKGROUND,FG_MODE_SET,~0,message_area);
  607.     fg_drawbox(COLOR_MESSAGE_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,message_area,fg.displaybox);
  608.     box[FG_X1] = message_area[FG_X1] + 2;
  609.     box[FG_X2] = message_area[FG_X2] - 2;
  610.     box[FG_Y1] = message_area[FG_Y1] + 2;
  611.     box[FG_Y2] = message_area[FG_Y2] - 2;
  612.     fg_drawbox(COLOR_MESSAGE_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,box,fg.displaybox);
  613.     fg_puts(COLOR_MESSAGE_FOREGROUND,FG_MODE_SET,~0,FG_ROT0,message_area[FG_X1]+gui_char_width,message_area[FG_Y1]+gui_char_height/2,str,fg.displaybox);
  614.     if (yn)
  615.         {
  616.         fg_box_cpy(box2,box);
  617.         dx = gui_char_width * strlen(MESSAGE_YN);
  618.         box[FG_X1] = message_area[FG_X2] - dx - gui_char_width;
  619.         box[FG_X2] = box[FG_X1] + dx/2;
  620.         box2[FG_X1] = box[FG_X2] + 1;
  621.         box2[FG_X2] = box2[FG_X1] + dx/2;
  622.         exclusive_focus_set(temp_data);
  623.         fg_msm_setcurpos(box[FG_X1]+gui_char_width+gui_char_width/2,box[FG_Y1]+gui_char_height/2);
  624.         }
  625.     fg_msm_showcursor();
  626.     fg_flush();
  627.     ret = 1;
  628.     if (yn)
  629.         {
  630.         for (;;)
  631.             {
  632.             c = wait_key_or_mouse_click(&x,&y);
  633.             if (tolower(c) == MESSAGE_YES || c == RETURN)
  634.                 break;
  635.             if (tolower(c) == MESSAGE_NO)
  636.                 {
  637.                 ret = 0;
  638.                 break;
  639.                 }
  640.             if (c == -1)
  641.                 {
  642.                 if (fg_pt_inbox(box,x,y))
  643.                     break;
  644.                 if (fg_pt_inbox(box2,x,y))
  645.                     {
  646.                     ret = 0;
  647.                     break;
  648.                     }
  649.                 }
  650.             }
  651.         exclusive_focus_clear(temp_data);
  652.         }
  653.     else
  654.         wait_key_or_mouse_click(&x,&y);
  655.     fg_msm_hidecursor();
  656.     object_remove(temp_data);
  657.     fg_msm_showcursor();
  658.     fg_flush();
  659.     return ret;
  660.     }
  661.  
  662.  
  663. #ifndef DEBUG
  664. /* intercept critical errors, this is specific to Zortech */
  665. /* make sure stack checking is turned off before entering this function */
  666. static int _far _cdecl critical_error_handler(int *ax,int *di)
  667.     {
  668. #define CRITICAL_ERROR_RETRY 1
  669. #define CRITICAL_ERROR_FAIL 3
  670. #define DISK_LETTER_OFFSET 15
  671.     static char disk_error[] = "Error on Drive X, Retry";
  672.     static char device_error[] = "Device Error, Retry";
  673.     char *error;
  674.  
  675.     if (*ax & 0x8000)
  676.         error = device_error;
  677.     else
  678.         {
  679.         error = disk_error;
  680.         disk_error[DISK_LETTER_OFFSET] = (*ax & 0xff) + 'A';
  681.         }
  682.     if (yn_box(error))
  683.         *ax = CRITICAL_ERROR_RETRY;
  684.     else
  685.         *ax = CRITICAL_ERROR_FAIL;
  686.     return *ax;
  687.     }
  688. #endif
  689.