Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2 
      3 Copyright (c) 1988, 1991, 1994  X Consortium
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining
      6 a copy of this software and associated documentation files (the
      7 "Software"), to deal in the Software without restriction, including
      8 without limitation the rights to use, copy, modify, merge, publish,
      9 distribute, sublicense, and/or sell copies of the Software, and to
     10 permit persons to whom the Software is furnished to do so, subject to
     11 the following conditions:
     12 
     13 The above copyright notice and this permission notice shall be included
     14 in all copies or substantial portions of the Software.
     15 
     16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     17 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     19 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22 OTHER DEALINGS IN THE SOFTWARE.
     23 
     24 Except as contained in this notice, the name of the X Consortium shall
     25 not be used in advertising or otherwise to promote the sale, use or
     26 other dealings in this Software without prior written authorization
     27 from the X Consortium.
     28 
     29 */
     30 /* $XFree86: xc/programs/xmessage/xmessage.c,v 1.4 2000/02/17 16:53:03 dawes Exp $ */
     31 
     32 #ifdef HAVE_CONFIG_H
     33 # include "config.h"
     34 #endif
     35 
     36 #include <assert.h>
     37 #include <X11/Intrinsic.h>
     38 #include <X11/StringDefs.h>
     39 #include <X11/Shell.h>
     40 #include <stdio.h>
     41 #include <stdlib.h>
     42 
     43 #include "xmessage.h"
     44 #include "readfile.h"
     45 
     46 /*
     47  * data used by xmessage
     48  */
     49 
     50 const char *ProgramName;
     51 
     52 static struct _QueryResources {
     53     char *file;
     54     char *button_list;
     55     char *default_button;
     56     Boolean print_value;
     57     Boolean center;
     58     Boolean nearmouse;
     59     int timeout_secs;
     60     Dimension maxHeight;
     61     Dimension maxWidth;
     62 } qres;				/* initialized by resources below */
     63 
     64 #define offset(field) XtOffsetOf(struct _QueryResources, field)
     65 static XtResource resources[] = {
     66     { "file", "File", XtRString, sizeof (char *),
     67       offset(file), XtRString, (XtPointer) NULL },
     68     { "buttons", "Buttons", XtRString, sizeof (char *),
     69       offset(button_list), XtRString, (XtPointer) "okay:0" },
     70     { "defaultButton", "DefaultButton", XtRString, sizeof (char *),
     71       offset(default_button), XtRString, (XtPointer) NULL },
     72     { "printValue", "PrintValue", XtRBoolean, sizeof (Boolean),
     73       offset(print_value), XtRString, "false" },
     74     { "center", "Center", XtRBoolean, sizeof (Boolean),
     75       offset(center), XtRString, "false" },
     76     { "nearMouse", "NearMouse", XtRBoolean, sizeof (Boolean),
     77       offset(nearmouse), XtRString, "false" },
     78     { "timeout", "Timeout", XtRInt, sizeof (int),
     79       offset(timeout_secs), XtRInt, NULL },
     80     { "maxHeight", "Maximum", XtRDimension, sizeof (Dimension),
     81       offset(maxHeight), XtRDimension, NULL },
     82     { "maxWidth", "Maximum", XtRDimension, sizeof (Dimension),
     83       offset(maxWidth), XtRDimension, NULL },
     84 };
     85 #undef offset
     86 
     87 static XrmOptionDescRec optionList[] =  {
     88     { "-file",    ".file", XrmoptionSepArg, (XPointer) NULL },
     89     { "-buttons", ".buttons", XrmoptionSepArg, (XPointer) NULL },
     90     { "-default", ".defaultButton", XrmoptionSepArg, (XPointer) NULL },
     91     { "-print",   ".printValue", XrmoptionNoArg, (XPointer) "on" },
     92     { "-center",  ".center", XrmoptionNoArg, (XPointer) "on" },
     93     { "-nearmouse", ".nearMouse", XrmoptionNoArg, (XPointer) "on" },
     94     { "-timeout", ".timeout", XrmoptionSepArg, (XPointer) NULL },
     95 };
     96 
     97 static String fallback_resources[] = {
     98     "*baseTranslations: #override :<Key>Return: default-exit()",
     99     "*message.Scroll: whenNeeded",
    100     NULL};
    101 
    102 
    103 /*
    104  * usage
    105  */
    106 
    107 static void
    108 usage (FILE *outf)
    109 {
    110     static const char *options[] = {
    111 "    -file filename              file to read message from, \"-\" for stdin",
    112 "    -buttons string             comma-separated list of label:exitcode",
    113 "    -default button             button to activate if Return is pressed",
    114 "    -print                      print the button label when selected",
    115 "    -center                     pop up at center of screen",
    116 "    -nearmouse                  pop up near the mouse cursor",
    117 "    -timeout secs               exit with status 0 after \"secs\" seconds",
    118 "",
    119 NULL};
    120     const char **cpp;
    121 
    122     fprintf (outf, "usage: %s [-options] [message ...]\n\n",
    123 	     ProgramName);
    124     fprintf (outf, "where options include:\n");
    125     for (cpp = options; *cpp; cpp++)
    126 	fprintf (outf, "%s\n", *cpp);
    127 }
    128 
    129 /*
    130  * Action to implement ICCCM delete_window and other translations.
    131  * Takes one argument, the exit status.
    132  */
    133 static Atom wm_delete_window;
    134 /* ARGSUSED */
    135 static void
    136 exit_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
    137 {
    138     int exit_status = 0;
    139 
    140     if(event->type == ClientMessage
    141        && event->xclient.data.l[0] != wm_delete_window)
    142 	return;
    143 
    144     if (*num_params == 1)
    145 	exit_status = atoi(params[0]);
    146     exit(exit_status);
    147 }
    148 
    149 int default_exitstatus = -1;		/* value of button named by -default */
    150 
    151 /* ARGSUSED */
    152 static void
    153 default_exit_action(Widget w, XEvent *event, String *params,
    154     Cardinal *num_params)
    155 {
    156     if (default_exitstatus >= 0)
    157 	exit(default_exitstatus);
    158 }
    159 
    160 /* Convert tabs to spaces in *messagep,*lengthp, copying to a new block of
    161    memory.  */
    162 static void
    163 detab (char **messagep, int *lengthp)
    164 {
    165   int   i, n, col, psize;
    166   char  *p;
    167 
    168   /* count how many tabs there are */
    169   n = 0;
    170   for (i = 0; i < *lengthp; i++)
    171     if ((*messagep)[i] == '\t')
    172       n++;
    173 
    174   /* length increases by at most seven extra spaces for each tab */
    175   psize = *lengthp + n*7 + 1;
    176   p = XtMalloc (psize);
    177 
    178   /* convert tabs to spaces, copying into p */
    179   n = 0;
    180   col = 0;
    181   for (i = 0; i < *lengthp; i++)
    182     {
    183       switch ((*messagep)[i]) {
    184       case '\n':
    185         p[n++] = '\n';
    186         col = 0;
    187         break;
    188       case '\t':
    189         do
    190           {
    191             p[n++] = ' ';
    192             col++;
    193           }
    194         while ((col % 8) != 0);
    195         break;
    196       default:
    197         p[n++] = (*messagep)[i];
    198         col++;
    199         break;
    200       }
    201     }
    202 
    203   assert (n < psize);
    204 
    205   /* null-terminator needed by Label widget */
    206   p[n] = '\0';
    207 
    208   free (*messagep);
    209 
    210   *messagep = p;
    211   *lengthp = n;
    212 }
    213 
    214 static XtActionsRec actions_list[] = {
    215     {"exit", exit_action},
    216     {"default-exit", default_exit_action},
    217 };
    218 
    219 static String top_trans =
    220     "<ClientMessage>WM_PROTOCOLS: exit(1)\n";
    221 
    222 /* assumes shell widget has already been realized */
    223 
    224 static void
    225 position_near(Widget shell, int x, int y)
    226 {
    227     int max_x, max_y;
    228     Dimension width, height, border;
    229     int gravity;
    230 
    231     /* some of this is copied from CenterWidgetOnPoint in Xaw/TextPop.c */
    232 
    233     XtVaGetValues(shell,
    234 		  XtNwidth, &width,
    235 		  XtNheight, &height,
    236 		  XtNborderWidth, &border,
    237 		  NULL);
    238 
    239     width += 2 * border;
    240     height += 2 * border;
    241 
    242     max_x = WidthOfScreen(XtScreen(shell));
    243     max_y = HeightOfScreen(XtScreen(shell));
    244 
    245     /* set gravity hint based on position on screen */
    246     gravity = 1;
    247     if (x > max_x/3) gravity += 1;
    248     if (x > max_x*2/3) gravity += 1;
    249     if (y > max_y/3) gravity += 3;
    250     if (y > max_y*2/3) gravity += 3;
    251 
    252     max_x -= width;
    253     max_y -= height;
    254 
    255     x -= ( (Position) width/2 );
    256     if (x < 0) x = 0;
    257     if (x > max_x) x = max_x;
    258 
    259     y -= ( (Position) height/2 );
    260     if (y < 0) y = 0;
    261     if ( y > max_y ) y = max_y;
    262 
    263     XtVaSetValues(shell,
    264 		  XtNx, (Position)x,
    265 		  XtNy, (Position)y,
    266 		  XtNwinGravity, gravity,
    267 		  NULL);
    268 }
    269 
    270 static void
    271 position_near_mouse(Widget shell)
    272 {
    273     int x, y;
    274     Window root, child;
    275     int winx, winy;
    276     unsigned int mask;
    277 
    278     XQueryPointer(XtDisplay(shell), XtWindow(shell),
    279 		  &root, &child, &x, &y, &winx, &winy, &mask);
    280     position_near(shell, x, y);
    281 }
    282 
    283 static void
    284 position_near_center(Widget shell)
    285 {
    286     position_near(shell,
    287 		  WidthOfScreen(XtScreen(shell))/2,
    288 		  HeightOfScreen(XtScreen(shell))/2);
    289 }
    290 
    291 /* ARGSUSED */
    292 static void
    293 time_out(XtPointer client_data, XtIntervalId *iid)
    294 {
    295     exit(0);
    296 }
    297 
    298 /*
    299  * xmessage main program - make sure that there is a message,
    300  * then create the query box and go.  Callbacks take care of exiting.
    301  */
    302 int
    303 main (int argc, char *argv[])
    304 {
    305     Widget top, queryform;
    306     XtAppContext app_con;
    307     char *message_str;
    308     int message_len;
    309 
    310     ProgramName = argv[0];
    311 
    312     XtSetLanguageProc(NULL, (XtLanguageProc) NULL, NULL);
    313 
    314     /* Handle args that don't require opening a display */
    315     for (int n = 1; n < argc; n++) {
    316 	const char *argn = argv[n];
    317 	/* accept single or double dash for -help & -version */
    318 	if (argn[0] == '-' && argn[1] == '-') {
    319 	    argn++;
    320 	}
    321 	if (strcmp (argn, "-help") == 0) {
    322 	    usage(stdout);
    323 	    exit(0);
    324 	}
    325 	if (strcmp (argn, "-version") == 0) {
    326 	    puts (PACKAGE_STRING);
    327 	    exit (0);
    328 	}
    329     }
    330 
    331     top = XtAppInitialize (&app_con, "Xmessage",
    332 			   optionList, XtNumber(optionList), &argc, argv,
    333 			   fallback_resources, NULL, 0);
    334 
    335     XtGetApplicationResources (top, (XtPointer) &qres, resources,
    336 			       XtNumber(resources), NULL, 0);
    337 
    338     if (argc == 1 && qres.file != NULL) {
    339 	message_str = read_file (qres.file, &message_len);
    340 	if (message_str == NULL) {
    341 	    fprintf (stderr, "%s: problems reading message file\n",
    342 		     ProgramName);
    343 	    exit (1);
    344 	}
    345     } else if (argc > 1 && qres.file == NULL) {
    346 	int i, len;
    347 	char *cp;
    348 
    349 	len = argc - 1;		/* spaces between words and final NULL */
    350 	for (i=1; i<argc; i++)
    351 	    len += strlen(argv[i]);
    352 	message_str = malloc(len);
    353 	if (!message_str) {
    354 	    fprintf (stderr, "%s: cannot get memory for message string\n",
    355 		     ProgramName);
    356 	    exit (1);
    357 	}
    358 	cp = message_str;
    359 	for (i=1; i<argc; i++) {
    360 	    strcpy(cp, argv[i]);
    361 	    cp += strlen(argv[i]);
    362 	    if (i != argc-1)
    363 		*cp++ = ' ';
    364 	    else
    365 		*cp = '\0';
    366 	}
    367 	message_len = len;
    368     } else {
    369 	fputs("Unknown argument(s):", stderr);
    370 	for (int n = 1; n < argc; n++) {
    371 	    fprintf(stderr, " %s", argv[n]);
    372 	}
    373 	fputs("\n\n", stderr);
    374 	usage(stderr);
    375 	exit(1);
    376     }
    377 
    378     wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False);
    379     XtAppAddActions(app_con, actions_list, XtNumber(actions_list));
    380     XtOverrideTranslations(top, XtParseTranslationTable(top_trans));
    381 
    382     detab (&message_str, &message_len);
    383 
    384     /*
    385      * create the query form; this is where most of the real work is done
    386      */
    387     queryform = make_queryform (top, message_str, message_len,
    388 				qres.button_list,
    389 				qres.print_value, qres.default_button,
    390 				qres.maxWidth, qres.maxHeight);
    391     if (!queryform) {
    392 	fprintf (stderr,
    393 		 "%s: unable to create query form with buttons: %s\n",
    394 		 ProgramName, qres.button_list);
    395 	exit (1);
    396     }
    397 
    398     XtSetMappedWhenManaged(top, FALSE);
    399     XtRealizeWidget(top);
    400 
    401     /* do WM_DELETE_WINDOW before map */
    402     XSetWMProtocols(XtDisplay(top), XtWindow(top), &wm_delete_window, 1);
    403 
    404     if (qres.center)
    405 	position_near_center(top);
    406     else if (qres.nearmouse)
    407 	position_near_mouse(top);
    408 
    409     XtMapWidget(top);
    410 
    411     if (qres.timeout_secs)
    412 	XtAppAddTimeOut(app_con, 1000*qres.timeout_secs, time_out, NULL);
    413 
    414     XtAppMainLoop(app_con);
    415 
    416     exit (0);
    417 }
    418