chooser.c revision 4901b09e
1/* 2 * 3Copyright 1990, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21Except as contained in this notice, the name of The Open Group shall not be 22used in advertising or otherwise to promote the sale, use or other dealings 23in this Software without prior written authorization from The Open Group. 24 * 25 * Author: Keith Packard, MIT X Consortium 26 */ 27 28 29/* 30 * Chooser - display a menu of names and let the user select one 31 */ 32 33/* 34 * Layout: 35 * 36 * +--------------------------------------------------+ 37 * | +------------------+ | 38 * | | Label | | 39 * | +------------------+ | 40 * | +-+--------------+ | 41 * | |^| name-1 | | 42 * | ||| name-2 | | 43 * | |v| name-3 | | 44 * | | | name-4 | | 45 * | | | name-5 | | 46 * | | | name-6 | | 47 * | +----------------+ | 48 * | cancel accept ping | 49 * +--------------------------------------------------+ 50 */ 51 52#include <X11/Intrinsic.h> 53#include <X11/StringDefs.h> 54#include <X11/Xatom.h> 55 56#include <X11/Xaw/Paned.h> 57#include <X11/Xaw/Label.h> 58#include <X11/Xaw/Viewport.h> 59#include <X11/Xaw/List.h> 60#include <X11/Xaw/Box.h> 61#include <X11/Xaw/Command.h> 62 63#include "dm.h" 64 65#include <X11/Xdmcp.h> 66 67#include <sys/types.h> 68#include <stdio.h> 69#include <ctype.h> 70 71#ifdef USE_XINERAMA 72# include <X11/extensions/Xinerama.h> 73#endif 74 75#if defined(SVR4) 76# include <sys/sockio.h> 77#endif 78 79#include "dm_socket.h" 80 81#include <arpa/inet.h> 82 83#include <sys/ioctl.h> 84 85#ifdef HAVE_SYS_PARAM_H 86# include <sys/param.h> 87# ifdef BSD 88# if (BSD >= 199103) 89# define VARIABLE_IFREQ 90# endif 91# endif 92#endif 93 94#ifdef XKB 95# include <X11/extensions/XKBbells.h> 96#endif 97 98#define BROADCAST_HOSTNAME "BROADCAST" 99 100#ifndef ishexdigit 101# define ishexdigit(c) (isdigit(c) || ('a' <= (c) && (c) <= 'f')) 102#endif 103 104#include <net/if.h> 105#include <netdb.h> 106#include <X11/keysym.h> 107 108static int FromHex (char *s, char *d, int len); 109static int oldline; 110 111static Widget toplevel, label, viewport, paned, list, box, cancel, acceptit, ping; 112 113static void CvtStringToARRAY8( 114 XrmValuePtr args, 115 Cardinal *num_args, 116 XrmValuePtr fromVal, 117 XrmValuePtr toVal); 118 119static struct _app_resources { 120 ARRAY8Ptr xdmAddress; 121 ARRAY8Ptr clientAddress; 122 int connectionType; 123} app_resources; 124 125#define offset(field) XtOffsetOf(struct _app_resources, field) 126 127#define XtRARRAY8 "ARRAY8" 128 129static XtResource resources[] = { 130 {"xdmAddress", "XdmAddress", XtRARRAY8, sizeof (ARRAY8Ptr), 131 offset (xdmAddress), XtRString, NULL }, 132 {"clientAddress", "ClientAddress", XtRARRAY8, sizeof (ARRAY8Ptr), 133 offset (clientAddress), XtRString, NULL }, 134 {"connectionType", "ConnectionType", XtRInt, sizeof (int), 135 offset (connectionType), XtRImmediate, (XtPointer) 0 } 136}; 137#undef offset 138 139static XrmOptionDescRec options[] = { 140 { "-xdmaddress", "*xdmAddress", XrmoptionSepArg, NULL }, 141 { "-clientaddress", "*clientAddress", XrmoptionSepArg, NULL }, 142 { "-connectionType","*connectionType", XrmoptionSepArg, NULL }, 143}; 144 145typedef struct _hostAddr { 146 struct _hostAddr *next; 147 struct sockaddr *addr; 148 int addrlen; 149 xdmOpCode type; 150} HostAddr; 151 152static HostAddr *hostAddrdb; 153 154typedef struct _hostName { 155 struct _hostName *next; 156 char *fullname; 157 int willing; 158 ARRAY8 hostname, status; 159 CARD16 connectionType; 160 ARRAY8 hostaddr; 161} HostName; 162 163static HostName *hostNamedb; 164 165static int socketFD; 166#if defined(IPv6) && defined(AF_INET6) 167static int socket6FD; 168#endif 169 170static int pingTry; 171 172#define PING_INTERVAL 2000 173#define TRIES 3 174 175static XdmcpBuffer directBuffer, broadcastBuffer; 176static XdmcpBuffer buffer; 177 178/* ARGSUSED */ 179static void 180PingHosts (XtPointer closure, XtIntervalId *id) 181{ 182 HostAddr *hosts; 183 int sfd = socketFD; 184 185 for (hosts = hostAddrdb; hosts; hosts = hosts->next) 186 { 187#if defined(IPv6) && defined(AF_INET6) 188 if ( ((struct sockaddr *) hosts->addr)->sa_family == AF_INET6 ) 189 sfd = socket6FD; 190 else 191 sfd = socketFD; 192#endif 193 if (hosts->type == QUERY) 194 XdmcpFlush (sfd, &directBuffer, 195 (XdmcpNetaddr) hosts->addr, hosts->addrlen); 196 else 197 XdmcpFlush (sfd, &broadcastBuffer, 198 (XdmcpNetaddr) hosts->addr, hosts->addrlen); 199 } 200 if (++pingTry < TRIES) 201 XtAddTimeOut (PING_INTERVAL, PingHosts, (XtPointer) 0); 202} 203 204char **NameTable; 205int NameTableSize; 206 207static int 208HostnameCompare (const void *a, const void *b) 209{ 210 return strcmp (*(char **)a, *(char **)b); 211} 212 213static void 214RebuildTable (int size) 215{ 216 char **newTable = NULL; 217 HostName *names; 218 int i; 219 220 if (size) 221 { 222 newTable = malloc (size * sizeof (char *)); 223 if (!newTable) 224 return; 225 for (names = hostNamedb, i = 0; names; names = names->next, i++) 226 newTable[i] = names->fullname; 227 qsort (newTable, size, sizeof (char *), HostnameCompare); 228 } 229 XawListChange (list, (_Xconst char **) newTable, size, 0, TRUE); 230 free (NameTable); 231 NameTable = newTable; 232 NameTableSize = size; 233} 234 235static int 236AddHostname (ARRAY8Ptr hostname, ARRAY8Ptr status, struct sockaddr *addr, int willing) 237{ 238 HostName *new, **names, *name; 239 ARRAY8 hostAddr = {0, NULL}; 240 CARD16 connectionType; 241 int fulllen; 242 243 switch (addr->sa_family) 244 { 245 case AF_INET: 246 hostAddr.data = (CARD8 *) &((struct sockaddr_in *) addr)->sin_addr; 247 hostAddr.length = 4; 248 connectionType = FamilyInternet; 249 break; 250#if defined(IPv6) && defined(AF_INET6) 251 case AF_INET6: 252 hostAddr.data = (CARD8 *) &((struct sockaddr_in6 *) addr)->sin6_addr; 253 hostAddr.length = 16; 254 connectionType = FamilyInternet6; 255 break; 256#endif 257 default: 258 hostAddr.data = (CARD8 *) ""; 259 hostAddr.length = 0; 260 connectionType = FamilyLocal; 261 break; 262 } 263 for (names = &hostNamedb; *names; names = & (*names)->next) 264 { 265 name = *names; 266 if (connectionType == name->connectionType && 267 XdmcpARRAY8Equal (&hostAddr, &name->hostaddr)) 268 { 269 if (XdmcpARRAY8Equal (status, &name->status)) 270 { 271 return 0; 272 } 273 break; 274 } 275 } 276 if (!*names) 277 { 278 new = calloc (1, sizeof (HostName)); 279 if (!new) 280 return 0; 281 if (hostname->length) 282 { 283 switch (addr->sa_family) 284 { 285 case AF_INET: 286#if defined(IPv6) && defined(AF_INET6) 287 case AF_INET6: 288#endif 289 { 290 struct hostent *hostent; 291 char *host; 292 293 hostent = gethostbyaddr ((char *)hostAddr.data, hostAddr.length, addr->sa_family); 294 if (hostent) 295 { 296 XdmcpDisposeARRAY8 (hostname); 297 host = (char *)hostent->h_name; 298 XdmcpAllocARRAY8 (hostname, strlen (host)); 299 memcpy(hostname->data, host, hostname->length); 300 } 301 } 302 } 303 } 304 if (!XdmcpAllocARRAY8 (&new->hostaddr, hostAddr.length)) 305 { 306 free (new); 307 return 0; 308 } 309 memcpy(new->hostaddr.data, hostAddr.data, hostAddr.length); 310 new->connectionType = connectionType; 311 new->hostname = *hostname; 312 313 *names = new; 314 new->next = NULL; 315 NameTableSize++; 316 } 317 else 318 { 319 new = *names; 320 free (new->fullname); 321 XdmcpDisposeARRAY8 (&new->status); 322 XdmcpDisposeARRAY8 (hostname); 323 } 324 new->willing = willing; 325 new->status = *status; 326 327 hostname = &new->hostname; 328 fulllen = hostname->length; 329 if (fulllen < 30) 330 fulllen = 30; 331 fulllen += status->length + 10; 332 new->fullname = malloc (fulllen); 333 if (!new->fullname) 334 { 335 new->fullname = "Unknown"; 336 } 337 else 338 { 339 snprintf(new->fullname, fulllen, "%-30.*s %*.*s", 340 hostname->length, hostname->data, 341 status->length, status->length, status->data); 342 } 343 RebuildTable (NameTableSize); 344 return 1; 345} 346 347static void 348DisposeHostname (HostName *host) 349{ 350 XdmcpDisposeARRAY8 (&host->hostname); 351 XdmcpDisposeARRAY8 (&host->hostaddr); 352 XdmcpDisposeARRAY8 (&host->status); 353 free (host->fullname); 354 free (host); 355} 356 357static void 358EmptyHostnames (void) 359{ 360 HostName *hosts, *next; 361 362 for (hosts = hostNamedb; hosts; hosts = next) 363 { 364 next = hosts->next; 365 DisposeHostname (hosts); 366 } 367 NameTableSize = 0; 368 hostNamedb = NULL; 369 RebuildTable (NameTableSize); 370} 371 372/* ARGSUSED */ 373static void 374ReceivePacket (XtPointer closure, int *source, XtInputId *id) 375{ 376 XdmcpHeader header; 377 ARRAY8 authenticationName = {0, NULL}; 378 ARRAY8 hostname = {0, NULL}; 379 ARRAY8 status = {0, NULL}; 380 int saveHostname = 0; 381#if defined(IPv6) && defined(AF_INET6) 382 struct sockaddr_storage addr; 383#else 384 struct sockaddr addr; 385#endif 386 int addrlen; 387 int sfd = * (int *) closure; 388 389 addrlen = sizeof (addr); 390 if (!XdmcpFill (sfd, &buffer, (XdmcpNetaddr) &addr, &addrlen)) 391 return; 392 if (!XdmcpReadHeader (&buffer, &header)) 393 return; 394 if (header.version != XDM_PROTOCOL_VERSION) 395 return; 396 switch (header.opcode) { 397 case WILLING: 398 if (XdmcpReadARRAY8 (&buffer, &authenticationName) && 399 XdmcpReadARRAY8 (&buffer, &hostname) && 400 XdmcpReadARRAY8 (&buffer, &status)) 401 { 402 if (header.length == 6 + authenticationName.length + 403 hostname.length + status.length) 404 { 405 if (AddHostname (&hostname, &status, (struct sockaddr *) &addr, 406 header.opcode == (int) WILLING)) 407 saveHostname = 1; 408 } 409 } 410 XdmcpDisposeARRAY8 (&authenticationName); 411 break; 412 case UNWILLING: 413 if (XdmcpReadARRAY8 (&buffer, &hostname) && 414 XdmcpReadARRAY8 (&buffer, &status)) 415 { 416 if (header.length == 4 + hostname.length + status.length) 417 { 418 if (AddHostname (&hostname, &status, (struct sockaddr *) &addr, 419 header.opcode == (int) WILLING)) 420 saveHostname = 1; 421 422 } 423 } 424 break; 425 default: 426 break; 427 } 428 if (!saveHostname) 429 { 430 XdmcpDisposeARRAY8 (&hostname); 431 XdmcpDisposeARRAY8 (&status); 432 } 433} 434 435static void 436RegisterHostaddr (struct sockaddr *addr, int len, xdmOpCode type) 437{ 438 HostAddr *host, **prev; 439 440 host = malloc (sizeof (HostAddr)); 441 if (!host) 442 return; 443 host->addr = malloc (len); 444 if (!host->addr) 445 { 446 free (host); 447 return; 448 } 449 memcpy(host->addr, addr, len); 450 host->addrlen = len; 451 host->type = type; 452 for (prev = &hostAddrdb; *prev; prev = &(*prev)->next) 453 ; 454 *prev = host; 455 host->next = NULL; 456} 457 458/* 459 * Register the address for this host. 460 * Called with each of the names on the command line. 461 * The special name "BROADCAST" looks up all the broadcast 462 * addresses on the local host. 463 */ 464 465/* Handle variable length ifreq in BNR2 and later */ 466#ifdef VARIABLE_IFREQ 467# define ifr_size(p) (sizeof (struct ifreq) + \ 468 (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \ 469 p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0)) 470#else 471# define ifr_size(p) (sizeof (struct ifreq)) 472#endif 473 474static void 475RegisterHostname (char *name) 476{ 477#if !defined(IPv6) || !defined(AF_INET6) 478 struct hostent *hostent; 479#endif 480 struct sockaddr_in in_addr; 481 struct ifconf ifc; 482 register struct ifreq *ifr; 483 struct sockaddr broad_addr; 484 char buf[2048], *cp, *cplim; 485 486 if (!strcmp (name, BROADCAST_HOSTNAME)) 487 { 488 ifc.ifc_len = sizeof (buf); 489 ifc.ifc_buf = buf; 490 if (ioctl (socketFD, (int) SIOCGIFCONF, (char *) &ifc) < 0) 491 return; 492 493 cplim = (char *) ifc.ifc_req + ifc.ifc_len; 494 495 for (cp = (char *) ifc.ifc_req; cp < cplim; cp += ifr_size (ifr)) 496 { 497 ifr = (struct ifreq *) cp; 498 if (ifr->ifr_addr.sa_family != AF_INET) 499 continue; 500 501 broad_addr = ifr->ifr_addr; 502 ((struct sockaddr_in *) &broad_addr)->sin_addr.s_addr = 503 htonl (INADDR_BROADCAST); 504#ifdef SIOCGIFBRDADDR 505 { 506 struct ifreq broad_req; 507 508 broad_req = *ifr; 509 if (ioctl (socketFD, SIOCGIFFLAGS, (char *) &broad_req) != -1 && 510 (broad_req.ifr_flags & IFF_BROADCAST) && 511 (broad_req.ifr_flags & IFF_UP) 512 ) 513 { 514 broad_req = *ifr; 515 if (ioctl (socketFD, SIOCGIFBRDADDR, &broad_req) != -1) 516 broad_addr = broad_req.ifr_addr; 517 else 518 continue; 519 } 520 else 521 continue; 522 } 523#endif 524 in_addr = *((struct sockaddr_in *) &broad_addr); 525 in_addr.sin_port = htons (XDM_UDP_PORT); 526#ifdef BSD44SOCKETS 527 in_addr.sin_len = sizeof(in_addr); 528#endif 529 RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr), 530 BROADCAST_QUERY); 531 } 532 } 533 else 534 { 535 /* address as hex string, e.g., "12180022" (deprecated) */ 536 if (strlen(name) == 8 && 537 FromHex(name, (char *)&in_addr.sin_addr, strlen(name)) == 0) 538 { 539 in_addr.sin_family = AF_INET; 540 in_addr.sin_port = htons (XDM_UDP_PORT); 541#ifdef BSD44SOCKETS 542 in_addr.sin_len = sizeof(in_addr); 543#endif 544 RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr), 545 QUERY); 546 } 547#if defined(IPv6) && defined(AF_INET6) 548 else { 549 char sport[8]; 550 struct addrinfo *ai, *nai, hints; 551 bzero(&hints,sizeof(hints)); 552 hints.ai_socktype = SOCK_DGRAM; 553 snprintf(sport, sizeof(sport), "%d", XDM_UDP_PORT); 554 if (getaddrinfo(name, sport, &hints, &ai) == 0) { 555 for (nai = ai ; nai != NULL ; nai = nai->ai_next) { 556 if ((nai->ai_family == AF_INET) || 557 (nai->ai_family == AF_INET6)) { 558 if (((nai->ai_family == AF_INET) && 559 IN_MULTICAST(((struct sockaddr_in *) nai->ai_addr) 560 ->sin_addr.s_addr)) 561 || ((nai->ai_family == AF_INET6) && 562 IN6_IS_ADDR_MULTICAST( 563 &((struct sockaddr_in6 *) nai->ai_addr) 564 ->sin6_addr))) 565 { 566 RegisterHostaddr(nai->ai_addr, nai->ai_addrlen, 567 BROADCAST_QUERY); 568 } else { 569 RegisterHostaddr(nai->ai_addr, nai->ai_addrlen, 570 QUERY); 571 } 572 } 573 } 574 freeaddrinfo(ai); 575 } 576 } 577#else 578 /* Per RFC 1123, check first for IP address in dotted-decimal form */ 579 else if ((in_addr.sin_addr.s_addr = inet_addr(name)) != -1) 580 in_addr.sin_family = AF_INET; 581 else 582 { 583 hostent = gethostbyname (name); 584 if (!hostent) 585 return; 586 if (hostent->h_addrtype != AF_INET || hostent->h_length != 4) 587 return; 588 in_addr.sin_family = hostent->h_addrtype; 589 memcpy(&in_addr.sin_addr, hostent->h_addr, 4); 590 } 591 in_addr.sin_port = htons (XDM_UDP_PORT); 592# ifdef BSD44SOCKETS 593 in_addr.sin_len = sizeof(in_addr); 594# endif 595 RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr), 596 QUERY); 597#endif /* IPv6 */ 598 } 599} 600 601static ARRAYofARRAY8 AuthenticationNames; 602 603static int 604InitXDMCP (char **argv) 605{ 606 int soopts = 1; 607 XdmcpHeader header; 608 int i; 609 610 header.version = XDM_PROTOCOL_VERSION; 611 header.opcode = (CARD16) BROADCAST_QUERY; 612 header.length = 1; 613 for (i = 0; i < (int)AuthenticationNames.length; i++) 614 header.length += 2 + AuthenticationNames.data[i].length; 615 XdmcpWriteHeader (&broadcastBuffer, &header); 616 XdmcpWriteARRAYofARRAY8 (&broadcastBuffer, &AuthenticationNames); 617 618 header.version = XDM_PROTOCOL_VERSION; 619 header.opcode = (CARD16) QUERY; 620 header.length = 1; 621 for (i = 0; i < (int)AuthenticationNames.length; i++) 622 header.length += 2 + AuthenticationNames.data[i].length; 623 XdmcpWriteHeader (&directBuffer, &header); 624 XdmcpWriteARRAYofARRAY8 (&directBuffer, &AuthenticationNames); 625 if ((socketFD = socket (AF_INET, SOCK_DGRAM, 0)) < 0) 626 return 0; 627#if defined(IPv6) && defined(AF_INET6) 628 socket6FD = socket (AF_INET6, SOCK_DGRAM, 0); 629#endif 630#ifdef SO_BROADCAST 631 soopts = 1; 632 if (setsockopt (socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts, sizeof (soopts)) < 0) 633 perror ("setsockopt"); 634#endif 635 636 XtAddInput (socketFD, (XtPointer) XtInputReadMask, ReceivePacket, 637 (XtPointer) &socketFD); 638#if defined(IPv6) && defined(AF_INET6) 639 if (socket6FD != -1) 640 XtAddInput (socket6FD, (XtPointer) XtInputReadMask, ReceivePacket, 641 (XtPointer) &socket6FD); 642#endif 643 while (*argv) 644 { 645 RegisterHostname (*argv); 646 ++argv; 647 } 648 pingTry = 0; 649 PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL); 650 return 1; 651} 652 653static void 654Choose (HostName *h) 655{ 656 if (app_resources.xdmAddress) 657 { 658 struct sockaddr_in in_addr; 659#if defined(IPv6) && defined(AF_INET6) 660 struct sockaddr_in6 in6_addr; 661#endif 662 struct sockaddr *addr = NULL; 663 int family; 664 int len = 0; 665 int fd; 666 char buf[1024]; 667 XdmcpBuffer buffer; 668 char *xdm; 669 670 xdm = (char *) app_resources.xdmAddress->data; 671 family = (xdm[0] << 8) + xdm[1]; 672 switch (family) { 673 case AF_INET: 674#ifdef BSD44SOCKETS 675 in_addr.sin_len = sizeof(in_addr); 676#endif 677 in_addr.sin_family = family; 678 memcpy(&in_addr.sin_port, xdm + 2, 2); 679 memcpy(&in_addr.sin_addr, xdm + 4, 4); 680 addr = (struct sockaddr *) &in_addr; 681 len = sizeof (in_addr); 682 break; 683#if defined(IPv6) && defined(AF_INET6) 684 case AF_INET6: 685 bzero(&in6_addr, sizeof(in6_addr)); 686# ifdef SIN6_LEN 687 in6_addr.sin6_len = sizeof(in6_addr); 688# endif 689 in6_addr.sin6_family = family; 690 memcpy(&in6_addr.sin6_port, xdm + 2, 2); 691 memcpy(&in6_addr.sin6_addr, xdm + 4, 16); 692 addr = (struct sockaddr *) &in6_addr; 693 len = sizeof (in6_addr); 694 break; 695#endif 696 } 697 if ((fd = socket (family, SOCK_STREAM, 0)) == -1) 698 { 699 fprintf (stderr, "Cannot create response socket\n"); 700 exit (REMANAGE_DISPLAY); 701 } 702 if (connect (fd, addr, len) == -1) 703 { 704 fprintf (stderr, "Cannot connect to xdm\n"); 705 exit (REMANAGE_DISPLAY); 706 } 707 buffer.data = (BYTE *) buf; 708 buffer.size = sizeof (buf); 709 buffer.pointer = 0; 710 buffer.count = 0; 711 XdmcpWriteARRAY8 (&buffer, app_resources.clientAddress); 712 XdmcpWriteCARD16 (&buffer, (CARD16) app_resources.connectionType); 713 XdmcpWriteARRAY8 (&buffer, &h->hostaddr); 714 write (fd, (char *)buffer.data, buffer.pointer); 715 close (fd); 716 } 717 else 718 { 719 int i; 720 721 printf ("%u\n", h->connectionType); 722 for (i = 0; i < (int)h->hostaddr.length; i++) 723 printf ("%u%s", h->hostaddr.data[i], 724 i == h->hostaddr.length - 1 ? "\n" : " "); 725 } 726} 727 728/* 729 next_line returns the next line in a list 730 across the list end. 731 (0, 1, 2, 3, 0, 1, 2, 3 ....) 732*/ 733 734static int 735next_line(unsigned int current, unsigned int size, int event) 736{ 737 switch(event) { 738 case Button5: 739 return (current + 1) % size; 740 case Button4: 741 return (current + size - 1) % size; 742 case XK_Down: 743 return (current + 1) % size; 744 case XK_Up: 745 return (current + size - 1) % size; 746 } 747 return -1; 748} 749 750/* 751 Hostselect selects a given chooser line. 752 Returns 1 when host is willing and 0 if not 753*/ 754 755static int 756Hostselect (int line) 757{ 758 XawListReturnStruct *r; 759 HostName *h; 760 761 /* Assume the next host is willing */ 762 XawListHighlight (list,line); 763 r = XawListShowCurrent (list); 764 /* copied from DoCheckWilling */ 765 for (h = hostNamedb; h; h = h->next) 766 { 767 if (!strcmp (r->string, h->fullname)) 768 { 769 if (!h->willing) 770 XawListUnhighlight (list); 771 else 772 { 773 /* Scroll viewport to make sure new selection is visible */ 774 Arg size[2]; 775 Dimension height, portheight; 776 Position y; 777 int lineheight, liney; 778 779 XtSetArg (size[0], XtNheight, &height); 780 XtSetArg (size[1], XtNy, &y); 781 XtGetValues (list, size, (Cardinal) 2); 782 783 XtSetArg (size[0], XtNheight, &portheight); 784 XtGetValues (viewport, size, (Cardinal) 1); 785 786 lineheight = height / NameTableSize; 787 liney = lineheight * line; 788 789 if ((y + liney) < 0) { 790 XawViewportSetCoordinates(viewport, 0, liney); 791 } else if ((y + liney + lineheight) > portheight) { 792 XawViewportSetCoordinates(viewport, 0, 793 (liney + lineheight) - portheight); 794 } 795 796 XtFree((char *) r); 797 return 1; 798 } 799 } 800 } 801 XtFree((char *) r); 802 return 0; 803} 804 805/* 806 Selectfirst selects the first willing host 807 in the chooser list (so you can select a host without 808 presence of mouse, but with pressing space or return, 809 or XK_Down / XK_Up 810*/ 811 812static void 813Selectfirst (void) 814{ 815 int line; 816 817 for (line = 0; line < NameTableSize; line++) 818 { 819 if (Hostselect(line)) 820 return; 821 } 822 return; 823} 824 825/* 826 Storeold stores the selected line into global int oldentry 827*/ 828 829static void 830Storeold (Widget w, XEvent *event, String *params, Cardinal *num_params) 831{ 832 XawListReturnStruct *r = XawListShowCurrent(list); 833 834 oldline = r->list_index; 835 XtFree((char *) r); 836} 837 838/* 839 Setold restores the global int oldentry 840 when you try to select a host with your mouse 841 who is unwilling as follows: 842 <Btn1Down>: Store() Set() CheckWilling() Setold() \n\ 843*/ 844 845static void 846Setold (Widget w, XEvent *event, String *params, Cardinal *num_params) 847{ 848 849 if ( (XawListShowCurrent(list))->list_index == XAW_LIST_NONE && oldline != XAW_LIST_NONE) 850 XawListHighlight (list, oldline); 851} 852 853/* 854 HostCycle tries to select the next willing host across 855 the list end and will stop at the first found willing host 856 or after trying all entries. 857*/ 858 859static void 860HostCycle(unsigned int line, unsigned int size, KeySym keysym) 861{ 862 unsigned int newline = line; 863 /* Do it only once around the chooser list, either direction */ 864 while ( (newline = next_line(newline,size,keysym)) != line && newline != -1) 865 { 866 if (Hostselect(newline)) 867 return; 868 } 869 /* No other willing host could be found, stay at the old one*/ 870 XawListHighlight (list, line); 871 return; 872} 873 874/* 875 Switch_Key handles XK_Up and XK_Down 876 and highlights an appropriate line in the chooser list. 877*/ 878 879static void 880Switch_Key (Widget w, XEvent *event, String *params, Cardinal *num_params) 881{ 882 char strbuf[128]; 883 KeySym keysym = 0; 884 static XComposeStatus compose_status = {NULL, 0}; 885 XawListReturnStruct *r; 886 887 XLookupString (&event->xkey, strbuf, sizeof (strbuf), 888 &keysym, &compose_status); 889 890 if (keysym != XK_Up && keysym != XK_Down) 891 return; 892 893 r = XawListShowCurrent (list); 894 895 if (r->list_index == XAW_LIST_NONE) 896 Selectfirst(); 897 else 898 HostCycle(r->list_index,NameTableSize,keysym); 899 900 XtFree((char *) r); 901 return; 902} 903 904 905 906/* 907 Switch_Btn handles ScrollWheel Forward (Button5) and Backward 908 (Button4) and highlights an appropriate line in the chooser list. 909*/ 910 911static void 912Switch_Btn (Widget w, XEvent *event, String *params, Cardinal *num_params) 913{ 914 XawListReturnStruct *r; 915 r = XawListShowCurrent (list); 916 917 if (r->list_index == XAW_LIST_NONE) 918 Selectfirst(); 919 else 920 HostCycle(r->list_index,NameTableSize,event->xbutton.button); 921 922 XtFree((char *) r); 923 return; 924} 925 926 927/* ARGSUSED */ 928static void 929DoAccept (Widget w, XEvent *event, String *params, Cardinal *num_params) 930{ 931 XawListReturnStruct *r; 932 HostName *h; 933 934 r = XawListShowCurrent (list); 935 if (r->list_index == XAW_LIST_NONE) 936#ifdef XKB 937 XkbStdBell(XtDisplay(toplevel),XtWindow(w),0,XkbBI_MinorError); 938#else 939 XBell (XtDisplay (toplevel), 0); 940#endif 941 else 942 { 943 for (h = hostNamedb; h; h = h->next) 944 if (!strcmp (r->string, h->fullname)) 945 { 946 Choose (h); 947 } 948 exit (OBEYSESS_DISPLAY); 949 } 950 XtFree((char *) r); 951} 952 953/* ARGSUSED */ 954static void 955DoCheckWilling (Widget w, XEvent *event, String *params, Cardinal *num_params) 956{ 957 XawListReturnStruct *r; 958 HostName *h; 959 960 r = XawListShowCurrent (list); 961 if (r->list_index != XAW_LIST_NONE) { 962 for (h = hostNamedb; h; h = h->next) 963 if (!strcmp (r->string, h->fullname)) 964 if (!h->willing) 965 XawListUnhighlight (list); 966 } 967 XtFree((char *) r); 968} 969 970/* ARGSUSED */ 971_X_NORETURN 972static void 973DoCancel (Widget w, XEvent *event, String *params, Cardinal *num_params) 974{ 975 exit (OBEYSESS_DISPLAY); 976} 977 978/* ARGSUSED */ 979static void 980DoPing (Widget w, XEvent *event, String *params, Cardinal *num_params) 981{ 982 EmptyHostnames (); 983 pingTry = 0; 984 PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL); 985} 986 987static XtActionsRec app_actions[] = { 988 { "Accept", DoAccept }, 989 { "Cancel", DoCancel }, 990 { "CheckWilling", DoCheckWilling }, 991 { "Ping", DoPing }, 992 { "KeySwitch", Switch_Key }, 993 { "BtnSwitch", Switch_Btn }, 994 { "Store", Storeold }, 995 { "Setold", Setold }, 996}; 997 998int 999main (int argc, char **argv) 1000{ 1001 Arg position[3]; 1002 Dimension width, height; 1003 Position x, y; 1004#ifdef USE_XINERAMA 1005 XineramaScreenInfo *screens; 1006 int s_num; 1007#endif 1008 1009 1010 toplevel = XtInitialize (argv[0], "Chooser", options, XtNumber(options), &argc, argv); 1011 1012 XtAddConverter(XtRString, XtRARRAY8, CvtStringToARRAY8, NULL, 0); 1013 1014 XtGetApplicationResources (toplevel, (XtPointer) &app_resources, resources, 1015 XtNumber (resources), NULL, (Cardinal) 0); 1016 1017 XtAddActions (app_actions, XtNumber (app_actions)); 1018 paned = XtCreateManagedWidget ("paned", panedWidgetClass, toplevel, NULL, 0); 1019 label = XtCreateManagedWidget ("label", labelWidgetClass, paned, NULL, 0); 1020 viewport = XtCreateManagedWidget ("viewport", viewportWidgetClass, paned, NULL, 0); 1021 list = XtCreateManagedWidget ("list", listWidgetClass, viewport, NULL, 0); 1022 box = XtCreateManagedWidget ("box", boxWidgetClass, paned, NULL, 0); 1023 cancel = XtCreateManagedWidget ("cancel", commandWidgetClass, box, NULL, 0); 1024 acceptit = XtCreateManagedWidget ("accept", commandWidgetClass, box, NULL, 0); 1025 ping = XtCreateManagedWidget ("ping", commandWidgetClass, box, NULL, 0); 1026 1027 /* 1028 * center ourselves on the screen 1029 */ 1030 XtSetMappedWhenManaged(toplevel, FALSE); 1031 XtRealizeWidget (toplevel); 1032 1033 XtSetArg (position[0], XtNwidth, &width); 1034 XtSetArg (position[1], XtNheight, &height); 1035 XtGetValues (toplevel, position, (Cardinal) 2); 1036#ifdef USE_XINERAMA 1037 if ( 1038 XineramaIsActive(XtDisplay(toplevel)) && 1039 (screens = XineramaQueryScreens(XtDisplay(toplevel), &s_num)) != NULL 1040 ) 1041 { 1042 x = (Position)(screens[0].x_org + (screens[0].width - width) / 2); 1043 y = (Position)(screens[0].y_org + (screens[0].height - height) / 3); 1044 1045 XFree(screens); 1046 } 1047 else 1048#endif 1049 { 1050 x = (Position)(WidthOfScreen (XtScreen (toplevel)) - width) / 2; 1051 y = (Position)(HeightOfScreen (XtScreen (toplevel)) - height) / 3; 1052 } 1053 XtSetArg (position[0], XtNx, x); 1054 XtSetArg (position[1], XtNy, y); 1055 XtSetValues (toplevel, position, (Cardinal) 2); 1056 1057 /* 1058 * Run 1059 */ 1060 XtMapWidget(toplevel); 1061 InitXDMCP (argv + 1); 1062 XtMainLoop (); 1063 exit(0); 1064 /*NOTREACHED*/ 1065} 1066 1067/* Converts the hex string s of length len into the byte array d. 1068 Returns 0 if s was a legal hex string, 1 otherwise. 1069 */ 1070static int 1071FromHex (char *s, char *d, int len) 1072{ 1073 int t; 1074 int ret = len&1; /* odd-length hex strings are illegal */ 1075 while (len >= 2) 1076 { 1077#define HexChar(c) ('0' <= (c) && (c) <= '9' ? (c) - '0' : (c) - 'a' + 10) 1078 1079 if (!ishexdigit(*s)) 1080 ret = 1; 1081 t = HexChar (*s) << 4; 1082 s++; 1083 if (!ishexdigit(*s)) 1084 ret = 1; 1085 t += HexChar (*s); 1086 s++; 1087 *d++ = t; 1088 len -= 2; 1089 } 1090 return ret; 1091} 1092 1093/*ARGSUSED*/ 1094static void 1095CvtStringToARRAY8 (XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal) 1096{ 1097 static ARRAY8Ptr dest; 1098 char *s; 1099 int len; 1100 1101 dest = (ARRAY8Ptr) XtMalloc (sizeof (ARRAY8)); 1102 len = fromVal->size; 1103 s = (char *) fromVal->addr; 1104 if (!XdmcpAllocARRAY8 (dest, len >> 1)) 1105 XtStringConversionWarning ((char *) fromVal->addr, XtRARRAY8); 1106 else 1107 { 1108 FromHex (s, (char *) dest->data, len); 1109 } 1110 toVal->addr = (caddr_t) &dest; 1111 toVal->size = sizeof (ARRAY8Ptr); 1112} 1113