1100ae103Smrg/*
2100ae103Smrg
3100ae103SmrgCopyright (c) 1988, 1991, 1994  X Consortium
4100ae103Smrg
5100ae103SmrgPermission is hereby granted, free of charge, to any person obtaining
6100ae103Smrga copy of this software and associated documentation files (the
7100ae103Smrg"Software"), to deal in the Software without restriction, including
8100ae103Smrgwithout limitation the rights to use, copy, modify, merge, publish,
9100ae103Smrgdistribute, sublicense, and/or sell copies of the Software, and to
10100ae103Smrgpermit persons to whom the Software is furnished to do so, subject to
11100ae103Smrgthe following conditions:
12100ae103Smrg
13100ae103SmrgThe above copyright notice and this permission notice shall be included
14100ae103Smrgin all copies or substantial portions of the Software.
15100ae103Smrg
16100ae103SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17100ae103SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18100ae103SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19100ae103SmrgIN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
20100ae103SmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21100ae103SmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22100ae103SmrgOTHER DEALINGS IN THE SOFTWARE.
23100ae103Smrg
24100ae103SmrgExcept as contained in this notice, the name of the X Consortium shall
25100ae103Smrgnot be used in advertising or otherwise to promote the sale, use or
26100ae103Smrgother dealings in this Software without prior written authorization
27100ae103Smrgfrom the X Consortium.
28100ae103Smrg
29100ae103Smrg*/
30100ae103Smrg/* $XFree86: xc/programs/xmessage/xmessage.c,v 1.4 2000/02/17 16:53:03 dawes Exp $ */
31100ae103Smrg
3214a67432Smrg#ifdef HAVE_CONFIG_H
3314a67432Smrg# include "config.h"
3414a67432Smrg#endif
3514a67432Smrg
36100ae103Smrg#include <assert.h>
37100ae103Smrg#include <X11/Intrinsic.h>
38100ae103Smrg#include <X11/StringDefs.h>
39100ae103Smrg#include <X11/Shell.h>
40100ae103Smrg#include <stdio.h>
41100ae103Smrg#include <stdlib.h>
42100ae103Smrg
43100ae103Smrg#include "xmessage.h"
44100ae103Smrg#include "readfile.h"
45100ae103Smrg
46100ae103Smrg/*
47100ae103Smrg * data used by xmessage
48100ae103Smrg */
49100ae103Smrg
50100ae103Smrgconst char *ProgramName;
51100ae103Smrg
52100ae103Smrgstatic struct _QueryResources {
53100ae103Smrg    char *file;
54100ae103Smrg    char *button_list;
55100ae103Smrg    char *default_button;
56100ae103Smrg    Boolean print_value;
57100ae103Smrg    Boolean center;
58100ae103Smrg    Boolean nearmouse;
59100ae103Smrg    int timeout_secs;
60100ae103Smrg    Dimension maxHeight;
61100ae103Smrg    Dimension maxWidth;
62100ae103Smrg} qres;				/* initialized by resources below */
63100ae103Smrg
64100ae103Smrg#define offset(field) XtOffsetOf(struct _QueryResources, field)
65100ae103Smrgstatic XtResource resources[] = {
66100ae103Smrg    { "file", "File", XtRString, sizeof (char *),
67100ae103Smrg      offset(file), XtRString, (XtPointer) NULL },
68100ae103Smrg    { "buttons", "Buttons", XtRString, sizeof (char *),
69100ae103Smrg      offset(button_list), XtRString, (XtPointer) "okay:0" },
70100ae103Smrg    { "defaultButton", "DefaultButton", XtRString, sizeof (char *),
71100ae103Smrg      offset(default_button), XtRString, (XtPointer) NULL },
72100ae103Smrg    { "printValue", "PrintValue", XtRBoolean, sizeof (Boolean),
73100ae103Smrg      offset(print_value), XtRString, "false" },
74100ae103Smrg    { "center", "Center", XtRBoolean, sizeof (Boolean),
75100ae103Smrg      offset(center), XtRString, "false" },
76100ae103Smrg    { "nearMouse", "NearMouse", XtRBoolean, sizeof (Boolean),
77100ae103Smrg      offset(nearmouse), XtRString, "false" },
78100ae103Smrg    { "timeout", "Timeout", XtRInt, sizeof (int),
79165cb819Smrg      offset(timeout_secs), XtRInt, NULL },
80100ae103Smrg    { "maxHeight", "Maximum", XtRDimension, sizeof (Dimension),
81165cb819Smrg      offset(maxHeight), XtRDimension, NULL },
82100ae103Smrg    { "maxWidth", "Maximum", XtRDimension, sizeof (Dimension),
83165cb819Smrg      offset(maxWidth), XtRDimension, NULL },
84100ae103Smrg};
85100ae103Smrg#undef offset
86100ae103Smrg
87100ae103Smrgstatic XrmOptionDescRec optionList[] =  {
88100ae103Smrg    { "-file",    ".file", XrmoptionSepArg, (XPointer) NULL },
89100ae103Smrg    { "-buttons", ".buttons", XrmoptionSepArg, (XPointer) NULL },
90100ae103Smrg    { "-default", ".defaultButton", XrmoptionSepArg, (XPointer) NULL },
91100ae103Smrg    { "-print",   ".printValue", XrmoptionNoArg, (XPointer) "on" },
92100ae103Smrg    { "-center",  ".center", XrmoptionNoArg, (XPointer) "on" },
93100ae103Smrg    { "-nearmouse", ".nearMouse", XrmoptionNoArg, (XPointer) "on" },
94100ae103Smrg    { "-timeout", ".timeout", XrmoptionSepArg, (XPointer) NULL },
95100ae103Smrg};
96100ae103Smrg
97100ae103Smrgstatic String fallback_resources[] = {
98100ae103Smrg    "*baseTranslations: #override :<Key>Return: default-exit()",
99100ae103Smrg    "*message.Scroll: whenNeeded",
100100ae103Smrg    NULL};
101100ae103Smrg
102100ae103Smrg
103100ae103Smrg/*
104100ae103Smrg * usage
105100ae103Smrg */
106100ae103Smrg
107100ae103Smrgstatic void
108100ae103Smrgusage (FILE *outf)
109100ae103Smrg{
110100ae103Smrg    static const char *options[] = {
111100ae103Smrg"    -file filename              file to read message from, \"-\" for stdin",
112100ae103Smrg"    -buttons string             comma-separated list of label:exitcode",
113100ae103Smrg"    -default button             button to activate if Return is pressed",
114100ae103Smrg"    -print                      print the button label when selected",
115100ae103Smrg"    -center                     pop up at center of screen",
116100ae103Smrg"    -nearmouse                  pop up near the mouse cursor",
117100ae103Smrg"    -timeout secs               exit with status 0 after \"secs\" seconds",
118100ae103Smrg"",
119100ae103SmrgNULL};
120100ae103Smrg    const char **cpp;
121100ae103Smrg
122100ae103Smrg    fprintf (outf, "usage: %s [-options] [message ...]\n\n",
123100ae103Smrg	     ProgramName);
124100ae103Smrg    fprintf (outf, "where options include:\n");
125100ae103Smrg    for (cpp = options; *cpp; cpp++)
126100ae103Smrg	fprintf (outf, "%s\n", *cpp);
127100ae103Smrg}
128100ae103Smrg
129100ae103Smrg/*
130100ae103Smrg * Action to implement ICCCM delete_window and other translations.
131100ae103Smrg * Takes one argument, the exit status.
132100ae103Smrg */
133100ae103Smrgstatic Atom wm_delete_window;
134100ae103Smrg/* ARGSUSED */
135100ae103Smrgstatic void
136100ae103Smrgexit_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
137100ae103Smrg{
138100ae103Smrg    int exit_status = 0;
139100ae103Smrg
140100ae103Smrg    if(event->type == ClientMessage
141100ae103Smrg       && event->xclient.data.l[0] != wm_delete_window)
142100ae103Smrg	return;
143100ae103Smrg
144100ae103Smrg    if (*num_params == 1)
145100ae103Smrg	exit_status = atoi(params[0]);
146100ae103Smrg    exit(exit_status);
147100ae103Smrg}
148100ae103Smrg
149100ae103Smrgint default_exitstatus = -1;		/* value of button named by -default */
150100ae103Smrg
151100ae103Smrg/* ARGSUSED */
152100ae103Smrgstatic void
153100ae103Smrgdefault_exit_action(Widget w, XEvent *event, String *params,
154100ae103Smrg    Cardinal *num_params)
155100ae103Smrg{
156100ae103Smrg    if (default_exitstatus >= 0)
157100ae103Smrg	exit(default_exitstatus);
158100ae103Smrg}
159100ae103Smrg
160100ae103Smrg/* Convert tabs to spaces in *messagep,*lengthp, copying to a new block of
161100ae103Smrg   memory.  */
162165cb819Smrgstatic void
163100ae103Smrgdetab (char **messagep, int *lengthp)
164100ae103Smrg{
165100ae103Smrg  int   i, n, col, psize;
166100ae103Smrg  char  *p;
167100ae103Smrg
168100ae103Smrg  /* count how many tabs there are */
169100ae103Smrg  n = 0;
170100ae103Smrg  for (i = 0; i < *lengthp; i++)
171100ae103Smrg    if ((*messagep)[i] == '\t')
172100ae103Smrg      n++;
173100ae103Smrg
174100ae103Smrg  /* length increases by at most seven extra spaces for each tab */
175100ae103Smrg  psize = *lengthp + n*7 + 1;
176100ae103Smrg  p = XtMalloc (psize);
177100ae103Smrg
178100ae103Smrg  /* convert tabs to spaces, copying into p */
179100ae103Smrg  n = 0;
180100ae103Smrg  col = 0;
181100ae103Smrg  for (i = 0; i < *lengthp; i++)
182100ae103Smrg    {
183100ae103Smrg      switch ((*messagep)[i]) {
184100ae103Smrg      case '\n':
185100ae103Smrg        p[n++] = '\n';
186100ae103Smrg        col = 0;
187100ae103Smrg        break;
188100ae103Smrg      case '\t':
189100ae103Smrg        do
190100ae103Smrg          {
191100ae103Smrg            p[n++] = ' ';
192100ae103Smrg            col++;
193100ae103Smrg          }
194100ae103Smrg        while ((col % 8) != 0);
195100ae103Smrg        break;
196100ae103Smrg      default:
197100ae103Smrg        p[n++] = (*messagep)[i];
198100ae103Smrg        col++;
199100ae103Smrg        break;
200100ae103Smrg      }
201100ae103Smrg    }
202100ae103Smrg
203100ae103Smrg  assert (n < psize);
204100ae103Smrg
205100ae103Smrg  /* null-terminator needed by Label widget */
206100ae103Smrg  p[n] = '\0';
207100ae103Smrg
208100ae103Smrg  free (*messagep);
209100ae103Smrg
210100ae103Smrg  *messagep = p;
211100ae103Smrg  *lengthp = n;
212100ae103Smrg}
213100ae103Smrg
214100ae103Smrgstatic XtActionsRec actions_list[] = {
215100ae103Smrg    {"exit", exit_action},
216100ae103Smrg    {"default-exit", default_exit_action},
217100ae103Smrg};
218100ae103Smrg
219100ae103Smrgstatic String top_trans =
220100ae103Smrg    "<ClientMessage>WM_PROTOCOLS: exit(1)\n";
221100ae103Smrg
222100ae103Smrg/* assumes shell widget has already been realized */
223100ae103Smrg
224100ae103Smrgstatic void
225100ae103Smrgposition_near(Widget shell, int x, int y)
226100ae103Smrg{
227100ae103Smrg    int max_x, max_y;
228100ae103Smrg    Dimension width, height, border;
229100ae103Smrg    int gravity;
230100ae103Smrg
231100ae103Smrg    /* some of this is copied from CenterWidgetOnPoint in Xaw/TextPop.c */
232100ae103Smrg
233100ae103Smrg    XtVaGetValues(shell,
234100ae103Smrg		  XtNwidth, &width,
235100ae103Smrg		  XtNheight, &height,
236100ae103Smrg		  XtNborderWidth, &border,
237100ae103Smrg		  NULL);
238100ae103Smrg
239100ae103Smrg    width += 2 * border;
240100ae103Smrg    height += 2 * border;
241100ae103Smrg
242100ae103Smrg    max_x = WidthOfScreen(XtScreen(shell));
243100ae103Smrg    max_y = HeightOfScreen(XtScreen(shell));
244100ae103Smrg
245100ae103Smrg    /* set gravity hint based on position on screen */
246100ae103Smrg    gravity = 1;
247100ae103Smrg    if (x > max_x/3) gravity += 1;
248100ae103Smrg    if (x > max_x*2/3) gravity += 1;
249100ae103Smrg    if (y > max_y/3) gravity += 3;
250100ae103Smrg    if (y > max_y*2/3) gravity += 3;
251100ae103Smrg
252100ae103Smrg    max_x -= width;
253100ae103Smrg    max_y -= height;
254100ae103Smrg
255100ae103Smrg    x -= ( (Position) width/2 );
256100ae103Smrg    if (x < 0) x = 0;
257100ae103Smrg    if (x > max_x) x = max_x;
258100ae103Smrg
259100ae103Smrg    y -= ( (Position) height/2 );
260100ae103Smrg    if (y < 0) y = 0;
261100ae103Smrg    if ( y > max_y ) y = max_y;
262100ae103Smrg
263100ae103Smrg    XtVaSetValues(shell,
264100ae103Smrg		  XtNx, (Position)x,
265100ae103Smrg		  XtNy, (Position)y,
266100ae103Smrg		  XtNwinGravity, gravity,
267100ae103Smrg		  NULL);
268100ae103Smrg}
269100ae103Smrg
270100ae103Smrgstatic void
271100ae103Smrgposition_near_mouse(Widget shell)
272100ae103Smrg{
273100ae103Smrg    int x, y;
274100ae103Smrg    Window root, child;
275100ae103Smrg    int winx, winy;
276100ae103Smrg    unsigned int mask;
277100ae103Smrg
278100ae103Smrg    XQueryPointer(XtDisplay(shell), XtWindow(shell),
279100ae103Smrg		  &root, &child, &x, &y, &winx, &winy, &mask);
280100ae103Smrg    position_near(shell, x, y);
281100ae103Smrg}
282100ae103Smrg
283100ae103Smrgstatic void
284100ae103Smrgposition_near_center(Widget shell)
285100ae103Smrg{
286100ae103Smrg    position_near(shell,
287100ae103Smrg		  WidthOfScreen(XtScreen(shell))/2,
288100ae103Smrg		  HeightOfScreen(XtScreen(shell))/2);
289100ae103Smrg}
290100ae103Smrg
291100ae103Smrg/* ARGSUSED */
292100ae103Smrgstatic void
293100ae103Smrgtime_out(XtPointer client_data, XtIntervalId *iid)
294100ae103Smrg{
295100ae103Smrg    exit(0);
296100ae103Smrg}
297100ae103Smrg
298100ae103Smrg/*
299100ae103Smrg * xmessage main program - make sure that there is a message,
300100ae103Smrg * then create the query box and go.  Callbacks take care of exiting.
301100ae103Smrg */
302100ae103Smrgint
303100ae103Smrgmain (int argc, char *argv[])
304100ae103Smrg{
305100ae103Smrg    Widget top, queryform;
306100ae103Smrg    XtAppContext app_con;
307100ae103Smrg    char *message_str;
308100ae103Smrg    int message_len;
309100ae103Smrg
310100ae103Smrg    ProgramName = argv[0];
311100ae103Smrg
312100ae103Smrg    XtSetLanguageProc(NULL, (XtLanguageProc) NULL, NULL);
313100ae103Smrg
31414a67432Smrg    /* Handle args that don't require opening a display */
31514a67432Smrg    for (int n = 1; n < argc; n++) {
31614a67432Smrg	const char *argn = argv[n];
31714a67432Smrg	/* accept single or double dash for -help & -version */
31814a67432Smrg	if (argn[0] == '-' && argn[1] == '-') {
31914a67432Smrg	    argn++;
32014a67432Smrg	}
32114a67432Smrg	if (strcmp (argn, "-help") == 0) {
32214a67432Smrg	    usage(stdout);
32314a67432Smrg	    exit(0);
32414a67432Smrg	}
32514a67432Smrg	if (strcmp (argn, "-version") == 0) {
32614a67432Smrg	    puts (PACKAGE_STRING);
32714a67432Smrg	    exit (0);
32814a67432Smrg	}
32914a67432Smrg    }
33014a67432Smrg
331100ae103Smrg    top = XtAppInitialize (&app_con, "Xmessage",
332100ae103Smrg			   optionList, XtNumber(optionList), &argc, argv,
333100ae103Smrg			   fallback_resources, NULL, 0);
334100ae103Smrg
335100ae103Smrg    XtGetApplicationResources (top, (XtPointer) &qres, resources,
336100ae103Smrg			       XtNumber(resources), NULL, 0);
337100ae103Smrg
338100ae103Smrg    if (argc == 1 && qres.file != NULL) {
339100ae103Smrg	message_str = read_file (qres.file, &message_len);
340100ae103Smrg	if (message_str == NULL) {
341100ae103Smrg	    fprintf (stderr, "%s: problems reading message file\n",
342100ae103Smrg		     ProgramName);
343100ae103Smrg	    exit (1);
344100ae103Smrg	}
345100ae103Smrg    } else if (argc > 1 && qres.file == NULL) {
346100ae103Smrg	int i, len;
347100ae103Smrg	char *cp;
348100ae103Smrg
349100ae103Smrg	len = argc - 1;		/* spaces between words and final NULL */
350100ae103Smrg	for (i=1; i<argc; i++)
351100ae103Smrg	    len += strlen(argv[i]);
352100ae103Smrg	message_str = malloc(len);
353100ae103Smrg	if (!message_str) {
354100ae103Smrg	    fprintf (stderr, "%s: cannot get memory for message string\n",
355100ae103Smrg		     ProgramName);
356100ae103Smrg	    exit (1);
357100ae103Smrg	}
358100ae103Smrg	cp = message_str;
359100ae103Smrg	for (i=1; i<argc; i++) {
360100ae103Smrg	    strcpy(cp, argv[i]);
361100ae103Smrg	    cp += strlen(argv[i]);
362100ae103Smrg	    if (i != argc-1)
363100ae103Smrg		*cp++ = ' ';
364100ae103Smrg	    else
365100ae103Smrg		*cp = '\0';
366100ae103Smrg	}
367100ae103Smrg	message_len = len;
368100ae103Smrg    } else {
36914a67432Smrg	fputs("Unknown argument(s):", stderr);
37014a67432Smrg	for (int n = 1; n < argc; n++) {
37114a67432Smrg	    fprintf(stderr, " %s", argv[n]);
37214a67432Smrg	}
37314a67432Smrg	fputs("\n\n", stderr);
374100ae103Smrg	usage(stderr);
375100ae103Smrg	exit(1);
376100ae103Smrg    }
377100ae103Smrg
378100ae103Smrg    wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False);
379100ae103Smrg    XtAppAddActions(app_con, actions_list, XtNumber(actions_list));
380100ae103Smrg    XtOverrideTranslations(top, XtParseTranslationTable(top_trans));
381100ae103Smrg
382100ae103Smrg    detab (&message_str, &message_len);
383100ae103Smrg
384100ae103Smrg    /*
385100ae103Smrg     * create the query form; this is where most of the real work is done
386100ae103Smrg     */
387100ae103Smrg    queryform = make_queryform (top, message_str, message_len,
388100ae103Smrg				qres.button_list,
389100ae103Smrg				qres.print_value, qres.default_button,
390100ae103Smrg				qres.maxWidth, qres.maxHeight);
391100ae103Smrg    if (!queryform) {
392100ae103Smrg	fprintf (stderr,
393100ae103Smrg		 "%s: unable to create query form with buttons: %s\n",
394100ae103Smrg		 ProgramName, qres.button_list);
395100ae103Smrg	exit (1);
396100ae103Smrg    }
397100ae103Smrg
398100ae103Smrg    XtSetMappedWhenManaged(top, FALSE);
399100ae103Smrg    XtRealizeWidget(top);
400100ae103Smrg
401100ae103Smrg    /* do WM_DELETE_WINDOW before map */
402100ae103Smrg    XSetWMProtocols(XtDisplay(top), XtWindow(top), &wm_delete_window, 1);
403100ae103Smrg
404100ae103Smrg    if (qres.center)
405100ae103Smrg	position_near_center(top);
406100ae103Smrg    else if (qres.nearmouse)
407100ae103Smrg	position_near_mouse(top);
408100ae103Smrg
409100ae103Smrg    XtMapWidget(top);
410100ae103Smrg
411100ae103Smrg    if (qres.timeout_secs)
412100ae103Smrg	XtAppAddTimeOut(app_con, 1000*qres.timeout_secs, time_out, NULL);
413100ae103Smrg
414100ae103Smrg    XtAppMainLoop(app_con);
415100ae103Smrg
416100ae103Smrg    exit (0);
417100ae103Smrg}
418