xmessage.c revision 165cb819
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
32100ae103Smrg#include <assert.h>
33100ae103Smrg#include <X11/Intrinsic.h>
34100ae103Smrg#include <X11/StringDefs.h>
35100ae103Smrg#include <X11/Shell.h>
36100ae103Smrg#include <stdio.h>
37100ae103Smrg#include <stdlib.h>
38100ae103Smrg
39100ae103Smrg#include "xmessage.h"
40100ae103Smrg#include "readfile.h"
41100ae103Smrg
42100ae103Smrg/*
43100ae103Smrg * data used by xmessage
44100ae103Smrg */
45100ae103Smrg
46100ae103Smrgconst char *ProgramName;
47100ae103Smrg
48100ae103Smrgstatic struct _QueryResources {
49100ae103Smrg    char *file;
50100ae103Smrg    char *button_list;
51100ae103Smrg    char *default_button;
52100ae103Smrg    Boolean print_value;
53100ae103Smrg    Boolean center;
54100ae103Smrg    Boolean nearmouse;
55100ae103Smrg    int timeout_secs;
56100ae103Smrg    Dimension maxHeight;
57100ae103Smrg    Dimension maxWidth;
58100ae103Smrg} qres;				/* initialized by resources below */
59100ae103Smrg
60100ae103Smrg#define offset(field) XtOffsetOf(struct _QueryResources, field)
61100ae103Smrgstatic XtResource resources[] = {
62100ae103Smrg    { "file", "File", XtRString, sizeof (char *),
63100ae103Smrg      offset(file), XtRString, (XtPointer) NULL },
64100ae103Smrg    { "buttons", "Buttons", XtRString, sizeof (char *),
65100ae103Smrg      offset(button_list), XtRString, (XtPointer) "okay:0" },
66100ae103Smrg    { "defaultButton", "DefaultButton", XtRString, sizeof (char *),
67100ae103Smrg      offset(default_button), XtRString, (XtPointer) NULL },
68100ae103Smrg    { "printValue", "PrintValue", XtRBoolean, sizeof (Boolean),
69100ae103Smrg      offset(print_value), XtRString, "false" },
70100ae103Smrg    { "center", "Center", XtRBoolean, sizeof (Boolean),
71100ae103Smrg      offset(center), XtRString, "false" },
72100ae103Smrg    { "nearMouse", "NearMouse", XtRBoolean, sizeof (Boolean),
73100ae103Smrg      offset(nearmouse), XtRString, "false" },
74100ae103Smrg    { "timeout", "Timeout", XtRInt, sizeof (int),
75165cb819Smrg      offset(timeout_secs), XtRInt, NULL },
76100ae103Smrg    { "maxHeight", "Maximum", XtRDimension, sizeof (Dimension),
77165cb819Smrg      offset(maxHeight), XtRDimension, NULL },
78100ae103Smrg    { "maxWidth", "Maximum", XtRDimension, sizeof (Dimension),
79165cb819Smrg      offset(maxWidth), XtRDimension, NULL },
80100ae103Smrg};
81100ae103Smrg#undef offset
82100ae103Smrg
83100ae103Smrgstatic XrmOptionDescRec optionList[] =  {
84100ae103Smrg    { "-file",    ".file", XrmoptionSepArg, (XPointer) NULL },
85100ae103Smrg    { "-buttons", ".buttons", XrmoptionSepArg, (XPointer) NULL },
86100ae103Smrg    { "-default", ".defaultButton", XrmoptionSepArg, (XPointer) NULL },
87100ae103Smrg    { "-print",   ".printValue", XrmoptionNoArg, (XPointer) "on" },
88100ae103Smrg    { "-center",  ".center", XrmoptionNoArg, (XPointer) "on" },
89100ae103Smrg    { "-nearmouse", ".nearMouse", XrmoptionNoArg, (XPointer) "on" },
90100ae103Smrg    { "-timeout", ".timeout", XrmoptionSepArg, (XPointer) NULL },
91100ae103Smrg};
92100ae103Smrg
93100ae103Smrgstatic String fallback_resources[] = {
94100ae103Smrg    "*baseTranslations: #override :<Key>Return: default-exit()",
95100ae103Smrg    "*message.Scroll: whenNeeded",
96100ae103Smrg    NULL};
97100ae103Smrg
98100ae103Smrg
99100ae103Smrg/*
100100ae103Smrg * usage
101100ae103Smrg */
102100ae103Smrg
103100ae103Smrgstatic void
104100ae103Smrgusage (FILE *outf)
105100ae103Smrg{
106100ae103Smrg    static const char *options[] = {
107100ae103Smrg"    -file filename              file to read message from, \"-\" for stdin",
108100ae103Smrg"    -buttons string             comma-separated list of label:exitcode",
109100ae103Smrg"    -default button             button to activate if Return is pressed",
110100ae103Smrg"    -print                      print the button label when selected",
111100ae103Smrg"    -center                     pop up at center of screen",
112100ae103Smrg"    -nearmouse                  pop up near the mouse cursor",
113100ae103Smrg"    -timeout secs               exit with status 0 after \"secs\" seconds",
114100ae103Smrg"",
115100ae103SmrgNULL};
116100ae103Smrg    const char **cpp;
117100ae103Smrg
118100ae103Smrg    fprintf (outf, "usage: %s [-options] [message ...]\n\n",
119100ae103Smrg	     ProgramName);
120100ae103Smrg    fprintf (outf, "where options include:\n");
121100ae103Smrg    for (cpp = options; *cpp; cpp++)
122100ae103Smrg	fprintf (outf, "%s\n", *cpp);
123100ae103Smrg}
124100ae103Smrg
125100ae103Smrg/*
126100ae103Smrg * Action to implement ICCCM delete_window and other translations.
127100ae103Smrg * Takes one argument, the exit status.
128100ae103Smrg */
129100ae103Smrgstatic Atom wm_delete_window;
130100ae103Smrg/* ARGSUSED */
131100ae103Smrgstatic void
132100ae103Smrgexit_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
133100ae103Smrg{
134100ae103Smrg    int exit_status = 0;
135100ae103Smrg
136100ae103Smrg    if(event->type == ClientMessage
137100ae103Smrg       && event->xclient.data.l[0] != wm_delete_window)
138100ae103Smrg	return;
139100ae103Smrg
140100ae103Smrg    if (*num_params == 1)
141100ae103Smrg	exit_status = atoi(params[0]);
142100ae103Smrg    exit(exit_status);
143100ae103Smrg}
144100ae103Smrg
145100ae103Smrgint default_exitstatus = -1;		/* value of button named by -default */
146100ae103Smrg
147100ae103Smrg/* ARGSUSED */
148100ae103Smrgstatic void
149100ae103Smrgdefault_exit_action(Widget w, XEvent *event, String *params,
150100ae103Smrg    Cardinal *num_params)
151100ae103Smrg{
152100ae103Smrg    if (default_exitstatus >= 0)
153100ae103Smrg	exit(default_exitstatus);
154100ae103Smrg}
155100ae103Smrg
156100ae103Smrg/* Convert tabs to spaces in *messagep,*lengthp, copying to a new block of
157100ae103Smrg   memory.  */
158165cb819Smrgstatic void
159100ae103Smrgdetab (char **messagep, int *lengthp)
160100ae103Smrg{
161100ae103Smrg  int   i, n, col, psize;
162100ae103Smrg  char  *p;
163100ae103Smrg
164100ae103Smrg  /* count how many tabs there are */
165100ae103Smrg  n = 0;
166100ae103Smrg  for (i = 0; i < *lengthp; i++)
167100ae103Smrg    if ((*messagep)[i] == '\t')
168100ae103Smrg      n++;
169100ae103Smrg
170100ae103Smrg  /* length increases by at most seven extra spaces for each tab */
171100ae103Smrg  psize = *lengthp + n*7 + 1;
172100ae103Smrg  p = XtMalloc (psize);
173100ae103Smrg
174100ae103Smrg  /* convert tabs to spaces, copying into p */
175100ae103Smrg  n = 0;
176100ae103Smrg  col = 0;
177100ae103Smrg  for (i = 0; i < *lengthp; i++)
178100ae103Smrg    {
179100ae103Smrg      switch ((*messagep)[i]) {
180100ae103Smrg      case '\n':
181100ae103Smrg        p[n++] = '\n';
182100ae103Smrg        col = 0;
183100ae103Smrg        break;
184100ae103Smrg      case '\t':
185100ae103Smrg        do
186100ae103Smrg          {
187100ae103Smrg            p[n++] = ' ';
188100ae103Smrg            col++;
189100ae103Smrg          }
190100ae103Smrg        while ((col % 8) != 0);
191100ae103Smrg        break;
192100ae103Smrg      default:
193100ae103Smrg        p[n++] = (*messagep)[i];
194100ae103Smrg        col++;
195100ae103Smrg        break;
196100ae103Smrg      }
197100ae103Smrg    }
198100ae103Smrg
199100ae103Smrg  assert (n < psize);
200100ae103Smrg
201100ae103Smrg  /* null-terminator needed by Label widget */
202100ae103Smrg  p[n] = '\0';
203100ae103Smrg
204100ae103Smrg  free (*messagep);
205100ae103Smrg
206100ae103Smrg  *messagep = p;
207100ae103Smrg  *lengthp = n;
208100ae103Smrg}
209100ae103Smrg
210100ae103Smrgstatic XtActionsRec actions_list[] = {
211100ae103Smrg    {"exit", exit_action},
212100ae103Smrg    {"default-exit", default_exit_action},
213100ae103Smrg};
214100ae103Smrg
215100ae103Smrgstatic String top_trans =
216100ae103Smrg    "<ClientMessage>WM_PROTOCOLS: exit(1)\n";
217100ae103Smrg
218100ae103Smrg/* assumes shell widget has already been realized */
219100ae103Smrg
220100ae103Smrgstatic void
221100ae103Smrgposition_near(Widget shell, int x, int y)
222100ae103Smrg{
223100ae103Smrg    int max_x, max_y;
224100ae103Smrg    Dimension width, height, border;
225100ae103Smrg    int gravity;
226100ae103Smrg
227100ae103Smrg    /* some of this is copied from CenterWidgetOnPoint in Xaw/TextPop.c */
228100ae103Smrg
229100ae103Smrg    XtVaGetValues(shell,
230100ae103Smrg		  XtNwidth, &width,
231100ae103Smrg		  XtNheight, &height,
232100ae103Smrg		  XtNborderWidth, &border,
233100ae103Smrg		  NULL);
234100ae103Smrg
235100ae103Smrg    width += 2 * border;
236100ae103Smrg    height += 2 * border;
237100ae103Smrg
238100ae103Smrg    max_x = WidthOfScreen(XtScreen(shell));
239100ae103Smrg    max_y = HeightOfScreen(XtScreen(shell));
240100ae103Smrg
241100ae103Smrg    /* set gravity hint based on position on screen */
242100ae103Smrg    gravity = 1;
243100ae103Smrg    if (x > max_x/3) gravity += 1;
244100ae103Smrg    if (x > max_x*2/3) gravity += 1;
245100ae103Smrg    if (y > max_y/3) gravity += 3;
246100ae103Smrg    if (y > max_y*2/3) gravity += 3;
247100ae103Smrg
248100ae103Smrg    max_x -= width;
249100ae103Smrg    max_y -= height;
250100ae103Smrg
251100ae103Smrg    x -= ( (Position) width/2 );
252100ae103Smrg    if (x < 0) x = 0;
253100ae103Smrg    if (x > max_x) x = max_x;
254100ae103Smrg
255100ae103Smrg    y -= ( (Position) height/2 );
256100ae103Smrg    if (y < 0) y = 0;
257100ae103Smrg    if ( y > max_y ) y = max_y;
258100ae103Smrg
259100ae103Smrg    XtVaSetValues(shell,
260100ae103Smrg		  XtNx, (Position)x,
261100ae103Smrg		  XtNy, (Position)y,
262100ae103Smrg		  XtNwinGravity, gravity,
263100ae103Smrg		  NULL);
264100ae103Smrg}
265100ae103Smrg
266100ae103Smrgstatic void
267100ae103Smrgposition_near_mouse(Widget shell)
268100ae103Smrg{
269100ae103Smrg    int x, y;
270100ae103Smrg    Window root, child;
271100ae103Smrg    int winx, winy;
272100ae103Smrg    unsigned int mask;
273100ae103Smrg
274100ae103Smrg    XQueryPointer(XtDisplay(shell), XtWindow(shell),
275100ae103Smrg		  &root, &child, &x, &y, &winx, &winy, &mask);
276100ae103Smrg    position_near(shell, x, y);
277100ae103Smrg}
278100ae103Smrg
279100ae103Smrgstatic void
280100ae103Smrgposition_near_center(Widget shell)
281100ae103Smrg{
282100ae103Smrg    position_near(shell,
283100ae103Smrg		  WidthOfScreen(XtScreen(shell))/2,
284100ae103Smrg		  HeightOfScreen(XtScreen(shell))/2);
285100ae103Smrg}
286100ae103Smrg
287100ae103Smrg/* ARGSUSED */
288100ae103Smrgstatic void
289100ae103Smrgtime_out(XtPointer client_data, XtIntervalId *iid)
290100ae103Smrg{
291100ae103Smrg    exit(0);
292100ae103Smrg}
293100ae103Smrg
294100ae103Smrg/*
295100ae103Smrg * xmessage main program - make sure that there is a message,
296100ae103Smrg * then create the query box and go.  Callbacks take care of exiting.
297100ae103Smrg */
298100ae103Smrgint
299100ae103Smrgmain (int argc, char *argv[])
300100ae103Smrg{
301100ae103Smrg    Widget top, queryform;
302100ae103Smrg    XtAppContext app_con;
303100ae103Smrg    char *message_str;
304100ae103Smrg    int message_len;
305100ae103Smrg
306100ae103Smrg    ProgramName = argv[0];
307100ae103Smrg
308100ae103Smrg    XtSetLanguageProc(NULL, (XtLanguageProc) NULL, NULL);
309100ae103Smrg
310100ae103Smrg    top = XtAppInitialize (&app_con, "Xmessage",
311100ae103Smrg			   optionList, XtNumber(optionList), &argc, argv,
312100ae103Smrg			   fallback_resources, NULL, 0);
313100ae103Smrg
314100ae103Smrg    XtGetApplicationResources (top, (XtPointer) &qres, resources,
315100ae103Smrg			       XtNumber(resources), NULL, 0);
316100ae103Smrg
317100ae103Smrg    if (argc > 1 && !strcmp(argv[1], "-help")) {
318100ae103Smrg	usage(stdout);
319100ae103Smrg	exit(0);
320100ae103Smrg    }
321100ae103Smrg    if (argc == 1 && qres.file != NULL) {
322100ae103Smrg	message_str = read_file (qres.file, &message_len);
323100ae103Smrg	if (message_str == NULL) {
324100ae103Smrg	    fprintf (stderr, "%s: problems reading message file\n",
325100ae103Smrg		     ProgramName);
326100ae103Smrg	    exit (1);
327100ae103Smrg	}
328100ae103Smrg    } else if (argc > 1 && qres.file == NULL) {
329100ae103Smrg	int i, len;
330100ae103Smrg	char *cp;
331100ae103Smrg
332100ae103Smrg	len = argc - 1;		/* spaces between words and final NULL */
333100ae103Smrg	for (i=1; i<argc; i++)
334100ae103Smrg	    len += strlen(argv[i]);
335100ae103Smrg	message_str = malloc(len);
336100ae103Smrg	if (!message_str) {
337100ae103Smrg	    fprintf (stderr, "%s: cannot get memory for message string\n",
338100ae103Smrg		     ProgramName);
339100ae103Smrg	    exit (1);
340100ae103Smrg	}
341100ae103Smrg	cp = message_str;
342100ae103Smrg	for (i=1; i<argc; i++) {
343100ae103Smrg	    strcpy(cp, argv[i]);
344100ae103Smrg	    cp += strlen(argv[i]);
345100ae103Smrg	    if (i != argc-1)
346100ae103Smrg		*cp++ = ' ';
347100ae103Smrg	    else
348100ae103Smrg		*cp = '\0';
349100ae103Smrg	}
350100ae103Smrg	message_len = len;
351100ae103Smrg    } else {
352100ae103Smrg	usage(stderr);
353100ae103Smrg	exit(1);
354100ae103Smrg    }
355100ae103Smrg
356100ae103Smrg    wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False);
357100ae103Smrg    XtAppAddActions(app_con, actions_list, XtNumber(actions_list));
358100ae103Smrg    XtOverrideTranslations(top, XtParseTranslationTable(top_trans));
359100ae103Smrg
360100ae103Smrg    detab (&message_str, &message_len);
361100ae103Smrg
362100ae103Smrg    /*
363100ae103Smrg     * create the query form; this is where most of the real work is done
364100ae103Smrg     */
365100ae103Smrg    queryform = make_queryform (top, message_str, message_len,
366100ae103Smrg				qres.button_list,
367100ae103Smrg				qres.print_value, qres.default_button,
368100ae103Smrg				qres.maxWidth, qres.maxHeight);
369100ae103Smrg    if (!queryform) {
370100ae103Smrg	fprintf (stderr,
371100ae103Smrg		 "%s: unable to create query form with buttons: %s\n",
372100ae103Smrg		 ProgramName, qres.button_list);
373100ae103Smrg	exit (1);
374100ae103Smrg    }
375100ae103Smrg
376100ae103Smrg    XtSetMappedWhenManaged(top, FALSE);
377100ae103Smrg    XtRealizeWidget(top);
378100ae103Smrg
379100ae103Smrg    /* do WM_DELETE_WINDOW before map */
380100ae103Smrg    XSetWMProtocols(XtDisplay(top), XtWindow(top), &wm_delete_window, 1);
381100ae103Smrg
382100ae103Smrg    if (qres.center)
383100ae103Smrg	position_near_center(top);
384100ae103Smrg    else if (qres.nearmouse)
385100ae103Smrg	position_near_mouse(top);
386100ae103Smrg
387100ae103Smrg    XtMapWidget(top);
388100ae103Smrg
389100ae103Smrg    if (qres.timeout_secs)
390100ae103Smrg	XtAppAddTimeOut(app_con, 1000*qres.timeout_secs, time_out, NULL);
391100ae103Smrg
392100ae103Smrg    XtAppMainLoop(app_con);
393100ae103Smrg
394100ae103Smrg    exit (0);
395100ae103Smrg}
396