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