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