1 /*************************************************
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2014 */
6 /* See the file NOTICE for conditions of use and distribution. */
11 /* This module contains the main program of the Exim monitor, which
12 sets up the world and then lets the XtAppMainLoop function
13 run things off X events. */
16 /*************************************************
18 *************************************************/
20 /* Fallback resources */
22 static String fallback_resources[] = {"eximon.geometry: +150+0", NULL};
24 /* X11 fixed argument lists */
26 static Arg quit_args[] = {
27 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
28 {XtNlabel, (XtArgVal) " Quit "},
29 {"left", XawChainLeft},
30 {"right", XawChainLeft},
32 {"bottom", XawChainTop}
35 static Arg resize_args[] = {
36 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
37 {XtNfromHoriz,(XtArgVal) NULL}, /* must be second */
38 {XtNlabel, (XtArgVal) " Size "},
39 {"left", XawChainLeft},
40 {"right", XawChainLeft},
42 {"bottom", XawChainTop}
45 static Arg update_args[] = {
46 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
47 {XtNlabel, (XtArgVal) " Update "},
48 {"left", XawChainLeft},
49 {"right", XawChainLeft},
51 {"bottom", XawChainTop}
54 static Arg hide_args[] = {
55 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
56 {XtNfromHoriz,(XtArgVal) NULL}, /* must be second */
57 {XtNlabel, (XtArgVal) " Hide "},
58 {"left", XawChainLeft},
59 {"right", XawChainLeft},
61 {"bottom", XawChainTop}
64 static Arg unhide_args[] = {
65 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
66 {XtNfromHoriz,(XtArgVal) NULL}, /* must be second */
67 {XtNlabel, (XtArgVal) " Unhide "},
68 {"left", XawChainLeft},
69 {"right", XawChainLeft},
71 {"bottom", XawChainTop}
74 static Arg log_args[] = {
75 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
76 {"editType", XawtextEdit},
77 {"useStringInPlace", (XtArgVal)TRUE},
78 {"string", (XtArgVal)""}, /* dummy to get it going */
79 {"scrollVertical", XawtextScrollAlways},
80 {"scrollHorizontal", XawtextScrollAlways},
81 {"right", XawChainRight},
83 {"bottom", XawChainTop}
86 static Arg queue_args[] = {
87 {XtNfromVert, (XtArgVal) NULL}, /* must be first */
88 {"editType", XawtextEdit},
89 {"string", (XtArgVal)""}, /* dummy to get it going */
90 {"scrollVertical", XawtextScrollAlways},
91 {"right", XawChainRight},
93 {"bottom", XawChainBottom}
96 static Arg sizepos_args[] = {
97 {"width", (XtArgVal)NULL},
98 {"height", (XtArgVal)NULL},
99 {"x", (XtArgVal)NULL},
100 {"y", (XtArgVal)NULL}
103 XtActionsRec menu_action_table[] = {
104 { "menu-create", menu_create } };
106 /* Types of non-message dialog action */
110 /* Miscellaneous local variables */
112 static int dialog_action;
113 static int tick_stripchart_accumulator = 999999;
114 static int tick_interval = 2;
115 static int maxposset = 0;
116 static int minposset = 0;
117 static int x_adjustment = -1;
118 static int y_adjustment = -1;
119 static Dimension screenwidth, screenheight;
120 static Dimension original_x, original_y;
121 static Dimension maxposx, maxposy;
122 static Dimension minposx, minposy;
123 static Dimension maxwidth, maxheight;
124 static Widget outer_form_widget;
125 static Widget hide_widget;
126 static Widget above_queue_widget;
131 #ifdef STRERROR_FROM_ERRLIST
132 /*************************************************
133 * Provide strerror() for non-ANSI libraries *
134 *************************************************/
136 /* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
137 in their libraries, but can provide the same facility by this simple
138 alternative function. */
143 if (n < 0 || n >= sys_nerr) return "unknown error number";
144 return sys_errlist[n];
146 #endif /* STRERROR_FROM_ERRLIST */
150 /*************************************************
151 * Handle attempts to write the log *
152 *************************************************/
154 /* The message gets written to stderr when log_write() is called from a
155 utility. The message always gets '\n' added on the end of it. These calls come
156 from modules such as store.c when things go drastically wrong (e.g. malloc()
157 failing). In normal use they won't get obeyed.
160 selector not relevant when running a utility
161 flags not relevant when running a utility
162 format a printf() format
163 ... arguments for format
169 log_write(unsigned int selector, int flags, const char *format, ...)
172 va_start(ap, format);
173 vfprintf(stderr, format, ap);
174 fprintf(stderr, "\n");
176 selector = selector; /* Keep picky compilers happy */
183 /*************************************************
184 * Extract port from address string *
185 *************************************************/
187 /* In the spool file, a host plus port is given as an IP address followed by a
188 dot and a port number. This function decodes this. It is needed by the
189 spool-reading function, and copied here to avoid having to include the whole
190 host.c module. One day the interaction between exim and eximon with regard to
191 included code MUST be tidied up!
194 address points to the string; if there is a port, the '.' in the string
195 is overwritten with zero to terminate the address
197 Returns: 0 if there is no port, else the port number.
201 host_address_extract_port(uschar *address)
203 int skip = -3; /* Skip 3 dots in IPv4 addresses */
205 while (*(++address) != 0)
208 if (ch == ':') skip = 0; /* Skip 0 dots in IPv6 addresses */
209 else if (ch == '.' && skip++ >= 0) break;
211 if (*address == 0) return 0;
213 return Uatoi(address);
219 /*************************************************
221 *************************************************/
223 /* Operations on messages are done in subprocesses; this handler
224 just catches them when they finish. It causes a queue display update
225 unless configured not to. */
227 static void sigchld_handler(int sig)
229 while (waitpid(-1, NULL, WNOHANG) > 0);
230 signal(sig, sigchld_handler);
231 if (action_queue_update) tick_queue_accumulator = 999999;
236 /*************************************************
237 * Callback routines *
238 *************************************************/
241 void updateAction(Widget w, XtPointer client_data, XtPointer call_data)
243 w = w; /* Keep picky compilers happy */
244 client_data = client_data;
245 call_data = call_data;
246 scan_spool_input(TRUE);
248 tick_queue_accumulator = 0;
251 void hideAction(Widget w, XtPointer client_data, XtPointer call_data)
253 w = w; /* Keep picky compilers happy */
254 client_data = client_data;
255 call_data = call_data;
256 actioned_message[0] = 0;
257 dialog_ref_widget = w;
258 dialog_action = da_hide;
259 create_dialog(US"Hide addresses ending with", US"");
262 void unhideAction(Widget w, XtPointer client_data, XtPointer call_data)
264 skip_item *sk = queue_skip;
266 w = w; /* Keep picky compilers happy */
267 client_data = client_data;
268 call_data = call_data;
272 skip_item *next = sk->next;
278 XtDestroyWidget(unhide_widget);
279 unhide_widget = NULL;
281 scan_spool_input(TRUE);
283 tick_queue_accumulator = 0;
286 void quitAction(Widget w, XtPointer client_data, XtPointer call_data)
288 w = w; /* Keep picky compilers happy */
289 client_data = client_data;
290 call_data = call_data;
295 /* Action when the "Size" button is pressed. This is a kludged up mess
296 that I made work after much messing around. Reading the position of the
297 toplevel widget gets the absolute position of the data portion of the window,
298 excluding the window manager's furniture. However, positioning the toplevel
299 widget's window seems to position the top corner of the furniture under the twm
300 window manager, but not under fwvm and others. The two cases are distinguished
301 by the values of x_adjustment and y_adjustment.
303 For twm (adjustment >= 0), one has to fudge the miminizing function to ensure
304 that we go back to exactly the same position as before.
306 For fwvm (adjustment < 0), one has to fudge the "top left hand corner"
307 positioning to ensure that the window manager's furniture gets displayed on the
308 screen. I haven't found a way of discovering the thickness of the furniture, so
309 some screwed-in values are used.
311 This is all ad hoc, developed by floundering around as I haven't found any
312 documentation that tells me what I really should do. */
314 void resizeAction(Widget button, XtPointer client_data, XtPointer call_data)
317 Dimension width, height;
319 Window w = XtWindow(toplevel_widget);
321 button = button; /* Keep picky compilers happy */
322 client_data = client_data;
323 call_data = call_data;
325 /* Get the position and size of the top level widget. */
327 sizepos_args[0].value = (XtArgVal)(&width);
328 sizepos_args[1].value = (XtArgVal)(&height);
329 sizepos_args[2].value = (XtArgVal)(&x);
330 sizepos_args[3].value = (XtArgVal)(&y);
331 XtGetValues(toplevel_widget, sizepos_args, 4);
333 /* Get the position of the widget's window relative to its parent; this
334 gives the thickness of the window manager's furniture. At least it does
335 in twm. For fwvm it gives zero. The size/movement function uses this data.
336 I tried doing this before entering the main loop, but it didn't always
337 work properly with twm. Running it every time seems to be OK. */
339 XGetWindowAttributes(X_display, XtWindow(toplevel_widget), &a);
340 if (a.x != 0) x_adjustment = a.x;
341 if (a.y != 0) y_adjustment = a.y;
343 /* If at maximum size, reduce to minimum and move back to where it was
344 when maximized, if that value is set, allowing for the furniture in cases
345 where the positioning includes the furniture. */
347 if (width == maxwidth && height == maxheight)
354 xs_SetValues(toplevel_widget, 4,
356 "height", min_height,
357 "x", minposx - ((x_adjustment >= 0)? x_adjustment : 0),
358 "y", minposy - ((y_adjustment >= 0)? y_adjustment : 0));
360 xs_SetValues(toplevel_widget, 2,
362 "height", min_height);
365 /* Else always expand to maximum. If currently at minimum size, remember where
366 it was for coming back. If we don't have a value for the thickness of the
367 furniture, the implication is that the coordinates position the application
368 window, so we can't use (0,0) because that loses the furniture. Use screwed in
369 values that seem to work with fvwm. */
376 if (width == min_width && height == min_height)
383 if ((int)(x + maxwidth) > (int)screenwidth ||
384 (int)(y + maxheight + 10) > (int)screenheight)
388 xx = maxposx - ((x_adjustment >= 0)? x_adjustment : 0);
389 yy = maxposy - ((y_adjustment >= 0)? y_adjustment : 0);
393 if ((int)(x + maxwidth) > (int)screenwidth)
394 xx = (x_adjustment >= 0)? 0 : 4;
395 if ((int)(y + maxheight + 10) > (int)screenheight)
396 yy = (y_adjustment >= 0)? 0 : 21;
399 xs_SetValues(toplevel_widget, 4,
406 else xs_SetValues(toplevel_widget, 2,
408 "height", maxheight);
411 /* Ensure the window is at the top */
413 XRaiseWindow(X_display, w);
419 /*************************************************
420 * Handle input from non-msg dialogue *
421 *************************************************/
423 /* The various cases here are: hide domain, (no more yet) */
425 void NonMessageDialogue(uschar *s)
429 switch(dialog_action)
433 /* Create the unhide button if not present */
435 if (unhide_widget == NULL)
437 unhide_args[0].value = (XtArgVal) above_queue_widget;
438 unhide_args[1].value = (XtArgVal) hide_widget;
439 unhide_widget = XtCreateManagedWidget("unhide", commandWidgetClass,
440 outer_form_widget, unhide_args, XtNumber(unhide_args));
441 XtAddCallback(unhide_widget, "callback", unhideAction, NULL);
444 /* Add item to skip queue */
446 sk = (skip_item *)store_malloc(sizeof(skip_item) + Ustrlen(s));
447 sk->next = queue_skip;
449 Ustrcpy(sk->text, s);
450 sk->reveal = time(NULL) + 60 * 60;
451 scan_spool_input(TRUE);
453 tick_queue_accumulator = 0;
460 /*************************************************
462 *************************************************/
464 /* This function is called initially to set up the starting data
465 values; it then sets a timeout so that it continues to be called
468 static void ticker(XtPointer pt, XtIntervalId *i)
470 pipe_item **pp = &pipe_chain;
471 pipe_item *p = pipe_chain;
472 tick_queue_accumulator += tick_interval;
473 tick_stripchart_accumulator += tick_interval;
476 pt = pt; /* Keep picky compilers happy */
479 /* If we have passed the queue update time, we must do a full
480 scan of the queue, checking for new arrivals, etc. This will
481 as a by-product set the count of items for use by the stripchart
482 display. On some systems, SIGCHLD signals can get lost at busy times,
483 so just in case, clean up any completed children here. */
485 if (tick_queue_accumulator >= queue_update)
487 scan_spool_input(TRUE);
489 tick_queue_accumulator = 0;
490 if (tick_stripchart_accumulator >= stripchart_update)
491 tick_stripchart_accumulator = 0;
492 while (waitpid(-1, NULL, WNOHANG) > 0);
495 /* Otherwise, if we have exceeded the stripchart interval,
496 do a reduced queue scan that simply provides the count for
499 else if (tick_stripchart_accumulator >= stripchart_update)
501 scan_spool_input(FALSE);
502 tick_stripchart_accumulator = 0;
505 /* Scan any pipes that are set up for listening to delivery processes,
506 and display their output if their windows are still open. */
513 while ((count = read(p->fd, buffer, 254)) > 0)
516 if (p->widget != NULL) text_show(p->widget, buffer);
524 /* If configured, cause display update */
525 if (action_queue_update) tick_queue_accumulator = 999999;
528 else pp = &(p->next);
533 /* Reset the timer for next time */
535 XtAppAddTimeOut(X_appcon, tick_interval * 1000, ticker, 0);
540 /*************************************************
541 * Find Num Lock modifiers *
542 *************************************************/
544 /* Return a string with the modifiers generated by XK_Num_Lock, or return
545 NULL if XK_Num_Lock doesn't generate any modifiers. This is needed because Num
546 Lock isn't always the same modifier on all servers.
550 buf a buffer in which to put the answers (long enough to hold 5)
552 Returns: points to the buffer, or NULL
556 numlock_modifiers(Display *display, uschar *buf)
562 m = XGetModifierMapping(display);
565 printf("Not enough memory\n");
569 /* Look at Mod1 through Mod5, and fill in the buffer as necessary. */
572 for (i = 3; i < 8; i++)
574 for (j = 0; j < m->max_keypermod; j++)
576 if (XKeycodeToKeysym(display, m->modifiermap [i*m->max_keypermod + j], 0)
579 sprintf(CS(buf+Ustrlen(buf)), " Mod%d", i-2);
591 /*************************************************
593 *************************************************/
595 int main(int argc, char **argv)
598 struct stat statdata;
599 uschar modbuf[] = " Mod1 Mod2 Mod3 Mod4 Mod5";
601 Widget stripchart_form_widget,
606 /* The exim global message_id needs to get set */
608 message_id_external = message_id_option + 1;
609 message_id = message_id_external + 1;
610 message_subdir[1] = 0;
612 /* Some store needs getting for big_buffer, which is used for
613 constructing file names and things. This call will initialize
614 the store_get() function. */
616 big_buffer = store_get(big_buffer_size);
618 /* Set up the version string and date and output them */
621 printf("\nExim Monitor version %s (compiled %s) initializing\n",
622 version_string, version_date);
624 /* Initialize various things from the environment and arguments. */
626 init(argc, USS argv);
628 /* Set up the SIGCHLD handler */
630 signal(SIGCHLD, sigchld_handler);
632 /* Get the buffer for storing the string for the log display. */
634 log_display_buffer = (uschar *)store_malloc(log_buffer_size);
635 log_display_buffer[0] = 0;
637 /* Initialize the data structures for the stripcharts */
641 /* If log_file contains the empty string, then Exim is running using syslog
642 only, and we can't tail the log. If not, open the log file and position to the
643 end of it. Before doing so, we have to detect whether the log files are
644 datestamped, and if so, sort out the name. The string in log_file already has
645 %s replaced by "main"; if datestamping is occurring, %D or %M will be present.
646 In fact, we don't need to test explicitly - just process the string with
649 Once opened, save the file's inode so that we can detect when the file is
650 switched to another one for non-datestamped files. However, allow the monitor
651 to start up without a log file (can happen if no messages have been sent
654 if (log_file[0] != 0)
656 /* Do *not* use "%s" here, we need the %D datestamp in the log_file to
658 (void)string_format(log_file_open, sizeof(log_file_open), CS log_file);
659 log_datestamping = string_datestamp_offset >= 0;
661 LOG = fopen(CS log_file_open, "r");
665 printf("*** eximon warning: can't open log file %s - will try "
666 "periodically\n", log_file_open);
670 fseek(LOG, 0, SEEK_END);
671 log_position = ftell(LOG);
672 fstat(fileno(LOG), &statdata);
673 log_inode = statdata.st_ino;
678 printf("*** eximon warning: no log file available to tail\n");
681 /* Now initialize the X world and create the top-level widget */
683 toplevel_widget = XtAppInitialize(&X_appcon, "Eximon", NULL, 0, &argc, argv,
684 fallback_resources, NULL, 0);
685 X_display = XtDisplay(toplevel_widget);
686 xs_SetValues(toplevel_widget, 4,
687 "title", window_title,
688 "iconName", window_title,
689 "minWidth", min_width,
690 "minHeight", min_height);
693 /* Create the action for setting up the menu in the queue display
694 window, and register the action for positioning the menu. */
696 XtAppAddActions(X_appcon, menu_action_table, 1);
697 XawSimpleMenuAddGlobalActions(X_appcon);
699 /* Set up translation tables for the text widgets we use. We don't
700 want all the generality of editing, etc. that the defaults provide.
701 This cannot be done before initializing X - the parser complains
702 about unknown events, modifiers, etc. in an unhelpful way... The
703 queue text widget has a different table which includes the button
704 for popping up the menu. Note that the order of things in these
705 tables is significant. Shift<thing> must come before <thing> as
706 otherwise it isn't noticed. */
709 <FocusIn>: display-caret(on)\n\
710 <FocusOut>: display-caret(off)\n\
713 /* The translation manager sets up passive grabs for the menu popups as a
714 result of MenuPopup(), but the grabs match only the exact modifiers listed,
715 hence combinations with and without caps-lock and num-lock must be given,
716 rather than just one "Shift<Btn1Down>" (or whatever menu_event is set to),
717 despite the fact that that notation (without a leading !) should ignore the
718 state of other modifiers. Thanks to Kevin Ryde for this information, and for
719 the function above that discovers which modifier is Num Lock, because it turns
720 out that it varies from server to server. */
722 sprintf(CS big_buffer,
723 "!%s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
724 !Lock %s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
725 ", menu_event, menu_event);
727 numlock = numlock_modifiers(X_display, modbuf); /* Get Num Lock modifier(s) */
729 if (numlock != NULL) sprintf(CS big_buffer + Ustrlen(big_buffer),
730 "!%s %s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
731 !Lock %s %s: menu-create() XawPositionSimpleMenu(menu) MenuPopup(menu)\n\
732 ", numlock, menu_event, numlock, menu_event);
734 sprintf(CS big_buffer + Ustrlen(big_buffer),
735 "<Btn1Down>: select-start()\n\
736 <Btn1Motion>: extend-adjust()\n\
737 <Btn1Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
738 <Btn3Down>: extend-start()\n\
739 <Btn3Motion>: extend-adjust()\n\
740 <Btn3Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
741 <Key>Up: scroll-one-line-down()\n\
742 <Key>Down: scroll-one-line-up()\n\
743 Ctrl<Key>R: search(backward)\n\
744 Ctrl<Key>S: search(forward)\n\
747 queue_trans = XtParseTranslationTable(CS big_buffer);
749 text_trans = XtParseTranslationTable(
750 "<Btn1Down>: select-start()\n\
751 <Btn1Motion>: extend-adjust()\n\
752 <Btn1Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
753 <Btn3Down>: extend-start()\n\
754 <Btn3Motion>: extend-adjust()\n\
755 <Btn3Up>: extend-end(PRIMARY,CUT_BUFFER0)\n\
756 <Key>Up: scroll-one-line-down()\n\
757 <Key>Down: scroll-one-line-up()\n\
758 Ctrl<Key>R: search(backward)\n\
759 Ctrl<Key>S: search(forward)\n\
763 /* Create a toplevel form widget to hold all the other things */
765 outer_form_widget = XtCreateManagedWidget("form", formWidgetClass,
766 toplevel_widget, NULL, 0);
768 /* Now create an inner form to hold the stripcharts */
770 stripchart_form_widget = XtCreateManagedWidget("form", formWidgetClass,
771 outer_form_widget, NULL, 0);
772 xs_SetValues(stripchart_form_widget, 5,
773 "defaultDistance", 8,
774 "left", XawChainLeft,
775 "right", XawChainLeft,
777 "bottom", XawChainTop);
779 /* Create the queue count stripchart and its label. */
781 create_stripchart(stripchart_form_widget, queue_stripchart_name);
783 /* If configured, create the size monitoring stripchart, but
784 only if the OS supports statfs(). */
786 if (size_stripchart != NULL)
789 if (size_stripchart_name == NULL)
791 size_stripchart_name = size_stripchart + Ustrlen(size_stripchart) - 1;
792 while (size_stripchart_name > size_stripchart &&
793 *size_stripchart_name == '/') size_stripchart_name--;
794 while (size_stripchart_name > size_stripchart &&
795 *size_stripchart_name != '/') size_stripchart_name--;
797 create_stripchart(stripchart_form_widget, size_stripchart_name);
799 printf("Can't create size stripchart: statfs() function not available\n");
803 /* Now create the configured input/output stripcharts; note
804 the total number includes the queue stripchart. */
806 for (i = stripchart_varstart; i < stripchart_number; i++)
807 create_stripchart(stripchart_form_widget, stripchart_title[i]);
809 /* Next in vertical order come the Resize & Quit buttons */
811 quit_args[0].value = (XtArgVal) stripchart_form_widget;
812 quit_widget = XtCreateManagedWidget("quit", commandWidgetClass,
813 outer_form_widget, quit_args, XtNumber(quit_args));
814 XtAddCallback(quit_widget, "callback", quitAction, NULL);
816 resize_args[0].value = (XtArgVal) stripchart_form_widget;
817 resize_args[1].value = (XtArgVal) quit_widget;
818 resize_widget = XtCreateManagedWidget("resize", commandWidgetClass,
819 outer_form_widget, resize_args, XtNumber(resize_args));
820 XtAddCallback(resize_widget, "callback", resizeAction, NULL);
822 /* In the absence of log tailing, the quit widget is the one above the
825 above_queue_widget = quit_widget;
827 /* Create an Ascii text widget for the log tail display if we are tailing a
828 log. Skip it if not. */
830 if (log_file[0] != 0)
832 log_args[0].value = (XtArgVal) quit_widget;
833 log_widget = XtCreateManagedWidget("log", asciiTextWidgetClass,
834 outer_form_widget, log_args, XtNumber(log_args));
835 XawTextDisplayCaret(log_widget, TRUE);
836 xs_SetValues(log_widget, 6,
837 "editType", XawtextEdit,
838 "translations", text_trans,
839 "string", log_display_buffer,
840 "length", log_buffer_size,
844 if (log_font != NULL)
846 XFontStruct *f = XLoadQueryFont(X_display, CS log_font);
847 if (f != NULL) xs_SetValues(log_widget, 1, "font", f);
850 above_queue_widget = log_widget;
853 /* The update button */
855 update_args[0].value = (XtArgVal) above_queue_widget;
856 update_widget = XtCreateManagedWidget("update", commandWidgetClass,
857 outer_form_widget, update_args, XtNumber(update_args));
858 XtAddCallback(update_widget, "callback", updateAction, NULL);
860 /* The hide button */
862 hide_args[0].value = (XtArgVal) above_queue_widget;
863 hide_args[1].value = (XtArgVal) update_widget;
864 hide_widget = XtCreateManagedWidget("hide", commandWidgetClass,
865 outer_form_widget, hide_args, XtNumber(hide_args));
866 XtAddCallback(hide_widget, "callback", hideAction, NULL);
868 /* Create an Ascii text widget for the queue display. */
870 queue_args[0].value = (XtArgVal) update_widget;
871 queue_widget = XtCreateManagedWidget("queue", asciiTextWidgetClass,
872 outer_form_widget, queue_args, XtNumber(queue_args));
873 XawTextDisplayCaret(queue_widget, TRUE);
875 xs_SetValues(queue_widget, 4,
876 "editType", XawtextEdit,
877 "height", queue_depth,
878 "width", queue_width,
879 "translations", queue_trans);
881 if (queue_font != NULL)
883 XFontStruct *f = XLoadQueryFont(X_display, CS queue_font);
884 if (f != NULL) xs_SetValues(queue_widget, 1, "font", f);
887 /* Call the ticker function to get the initial data set up. It
888 arranges to have itself recalled every 2 seconds. */
892 /* Everything is now set up; this flag is used by the regerror
893 function and also by the queue reader. */
895 eximon_initialized = TRUE;
896 printf("\nExim Monitor running\n");
898 /* Realize the toplevel and thereby get things displayed */
900 XtRealizeWidget(toplevel_widget);
902 /* Find out the size of the initial window, and set that as its
903 maximum. While we are at it, get the initial position. */
905 sizepos_args[0].value = (XtArgVal)(&maxwidth);
906 sizepos_args[1].value = (XtArgVal)(&maxheight);
907 sizepos_args[2].value = (XtArgVal)(&original_x);
908 sizepos_args[3].value = (XtArgVal)(&original_y);
909 XtGetValues(toplevel_widget, sizepos_args, 4);
911 xs_SetValues(toplevel_widget, 2,
912 "maxWidth", maxwidth,
913 "maxHeight", maxheight);
915 /* Set up the size of the screen */
917 screenwidth = XDisplayWidth(X_display, 0);
918 screenheight= XDisplayHeight(X_display,0);
920 /* Register the action table */
922 XtAppAddActions(X_appcon, actionTable, actionTableSize);
924 /* Reduce the window to the small size if this is wanted */
926 if (start_small) resizeAction(NULL, NULL, NULL);
928 /* Enter the application loop which handles things from here
929 onwards. The return statement is never obeyed, but is needed to
930 keep pedantic ANSI compilers happy. */
932 XtAppMainLoop(X_appcon);
937 /* End of em_main.c */