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