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