/*
* xipmsg.c - IP Messenger 1.20 for X11
* Copyright (C) 1995, 1996 by candy
*/
char rcsid_xipmsg[] = "$Id: xipmsg.c,v 3.7 1997/05/02 05:27:46 candy Exp candy $";
#include <ctype.h>
#include <math.h> /* floor() */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xatom.h>
#include <X11/Xlocale.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/List.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/Paned.h>
#include <X11/Xaw/Scrollbar.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/Toggle.h>
#include <X11/Xaw/Viewport.h>
#include <unistd.h> /* select() */
#include <sys/time.h> /* setitimer() */
/* includes below are order dependent */
#include <sys/param.h> /* htons() */
#include <sys/types.h> /* socket() */
#include <sys/socket.h> /* socket() */
#include <netinet/in.h> /* inet_addr() INADDR_ANY */
#include <arpa/inet.h> /* inet_addr() */
#include "xipmsg.h"
#include "brocas.h"
#include "db.h"
#ifdef NO_STRTOUL
#define strtoul(s,p,b) ((unsigned long)strtol(s,p,b))
#endif
#ifdef NO_MEMMOVE
#define memmove(d,s,l) bcopy(s,d,l)
#endif
#ifdef SUNOS41X /* [ */
static int
atexit(void (*proc)(void))
{
return on_exit((void (*)(int, caddr_t))proc, NULL);
}/* atexit */
#endif /* ] */
#ifdef NO_ATEXIT /* [ */
static void (*at_exit_proc)(void);
static void
inttrap(void)
{
if (at_exit_proc != NULL) {
at_exit_proc();
}
exit(1);
}/* inttrap */
static int
atexit(void (*proc)(void))
{
at_exit_proc = proc;
signal(SIGINT, inttrap);
signal(SIGHUP, inttrap);
signal(SIGTERM, inttrap);
return 0;
}/* atexit */
#endif /* ] */
#define NUMBER_OF_MENU 16 /* Don't forget change fallback_resources[] too.*/
extern char *myname;
static XtAppContext app_con;
static Widget toplevel;
static char last_msg[MESSAGE_MAX];
static Pixmap last_icon;
static Cursor csr_clock;
static int iconified;
static int pause_time = 500; /* milli-seconds */
static int bogus_fix;
static void send_dialog(Widget parent, const struct maddr_t *dstaddr, const char *to);
/*
* リストの個数を返す。
*/
static int
count_list(const void * const *ls)
{
const void * const *mv = ls;
while (*mv++ != NULL)
;
return mv - 1 - ls;
}/* count_list */
/*
* カンマで区切られた文字列をリストに変換する。
*/
static char **
cvs_list(const char *s)
{
char **ls = NULL;
int n = 0;
const char *p = s;
while (strchr(p, ',') != NULL) {
p = strchr(p, ',') + 1;
n++;
}/* while */
n++;
ls = malloc(sizeof(*ls) * (n + 1));
if (ls != NULL) {
char *buf = str_dup(s);
if (buf != NULL) {
char *mv = buf;
int i;
ls[0] = mv;
for (i = 1; i < n; i++) {
mv = strchr(mv, ',');
*mv++ = '\0';
while (isascii(*mv) && isspace(*mv))
mv++;
ls[i] = mv;
}/* for */
ls[n] = NULL;
}
}
return ls;
}/* cvs_list */
#define FROM_DB_MAX 64
struct from_t {
int fr_so;
char fr_name[USERNAME_MAX + HOSTNAME_MAX];
struct sockaddr_in fr_addr;
struct packet_t fr_pk;
char fr_last_msg[MESSAGE_MAX]; /* 最後に送ったメッセージ */
unsigned int fr_x0, fr_y0; /* 最初にダイアログを出す位置 */
unsigned int fr_x, fr_y; /* 次にダイアログを出す位置 */
int fr_count; /* 開いているダイアログの数 */
};
static struct db_t *from_db;
static int
from_comp(const void *d_, const void *s_)
{
const struct from_t *d = d_, *s = s_;
int cmp = strcmp(d->fr_name, s->fr_name);
return cmp;
}/* from_comp */
/*
* window 幅は最大 466 くらい
*/
static struct from_t *
from_install(const char *from)
{
static int lastx = 4, lasty = 4;
struct from_t *fr = malloc(sizeof(*fr));
int wx = DisplayWidth(XtDisplay(toplevel), 0);
int wy = DisplayHeight(XtDisplay(toplevel), 0);
if (lastx >= wx - 300)
lastx = lastx % (wx - 300);
if (lasty >= wy - 160)
lasty = lasty % (wy - 160);
if (fr != NULL) {
memset(fr, '\0', sizeof(*fr));
strncpyz(fr->fr_name, from, sizeof(fr->fr_name));
strcpy(fr->fr_last_msg, last_msg);
fr->fr_x0 = lastx;
fr->fr_y0 = lasty;
fr->fr_x = lastx;
fr->fr_y = lasty;
fr->fr_count = 0;
lastx += 256;
db_install(from_db, fr);
}
return fr;
}/* from_install */
static struct from_t *
from_lookup(const char *from)
{
struct from_t key, *fr;
strncpyz(key.fr_name, from, sizeof(key.fr_name));
fr = db_lookup(from_db, &key);
return fr;
}/* from_lookup */
static void
next_pos(Dimension *nx, Dimension *ny)
{
static int lastx = 20, lasty = 20;
int wx = DisplayWidth(XtDisplay(toplevel), 0);
int wy = DisplayHeight(XtDisplay(toplevel), 0);
if (lastx >= wx - 300)
lastx = 20;
if (lasty >= wy - 200)
lasty = 20;
*nx = lastx;
*ny = lasty;
lastx += 10;
lasty += 100;
}/* next_pos */
/*
* 以下の from_*()では fr == NULL でもよい。
*/
static struct from_t *
from_next_pos(struct from_t *fr, Dimension *nx, Dimension *ny)
{
int wy = DisplayHeight(XtDisplay(toplevel), 0);
if (fr != NULL) {
*nx = fr->fr_x;
*ny = fr->fr_y;
fr->fr_y += 100;
if (fr->fr_y >= wy - 200)
fr->fr_y = fr->fr_y0;
}
else {
next_pos(nx, ny);
}
return fr;
}/* from_next_pos */
static char *
from_last_msg(struct from_t *fr)
{
char *ret = NULL;
if (fr != NULL)
ret = fr->fr_last_msg;
else
ret = last_msg;
return ret;
}/* from_last_msg */
static struct from_t *
from_count_up(struct from_t *fr)
{
if (fr != NULL)
fr->fr_count++;
return fr;
}/* from_count_up */
static struct from_t *
from_count_down(struct from_t *fr)
{
if (fr != NULL) {
if (--fr->fr_count == 0) {
fr->fr_x = fr->fr_x0;
fr->fr_y = fr->fr_y0;
}
}
return fr;
}/* from_count_down */
/*
*
*/
static void
iconify_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
Display *d = XtDisplay(toplevel);
Window win = XtWindow(toplevel);
int scno = XScreenNumberOfScreen(XtScreen(w));
if (iconified)
XMapWindow(d, win);
else
XIconifyWindow(d, win, scno);
iconified = !iconified;
}/* iconify_action */
/*
* 返信ボタン
*/
static void
answer_proc(Widget w, XtPointer closure, XtPointer call_data)
{
struct maddr_t *replyto = closure;
Widget from = XtNameToWidget(XtParent(w), "from");
if (from != NULL) {
String to;
XtVaGetValues(from, XtNlabel, &to, NULL);
#if 1 /* answer時に引用する機能の追加 sakane@NES [ */
{
struct from_t *fr = from_lookup(to);
if (fr != NULL) {
char answer_string[MESSAGE_MAX];
char *p, *ap = answer_string;
String from_msg;
Widget dialog = XtParent(w);
XtVaGetValues(dialog, XtNlabel, &from_msg, NULL);
memset(answer_string, '\0', sizeof(answer_string));
*ap = '>';
for (p = (char *)from_msg;*p != '\0' && ap + 1 < &answer_string[COUNTOF(answer_string)];++p) {
*++ap = *p;
if (*p == '\n') {
if (*(p + 1) != '\0')
*++ap = '>';
}
}
ap[1] = '\0';
strncpyz(fr->fr_last_msg, answer_string, sizeof(fr->fr_last_msg));
}
}
#endif /* ] */
send_dialog(w, replyto, to);
}
else {
fprintf(stderr, "%s: [answer] cannot get `from' widget.\n", myname);
}
}/* answer_proc */
/*
* Done または Cancel ボタン
*/
static void
done_proc(Widget w, XtPointer closure, XtPointer call_data)
{
if (bogus_fix)
XtPopdown(XtParent(XtParent(w)));
else
XtDestroyWidget(XtParent(XtParent(w)));
}/* done_proc */
/*
* destroyCallback
*/
static void
destroy_proc(Widget w, XtPointer closure, XtPointer call_data)
{
XtFree(closure);
}/* destroy_proc */
/*
* destroyCallback
*/
static void
from_free_proc(Widget w, XtPointer closure, XtPointer call_data)
{
struct from_t *fr = closure;
from_count_down(fr);
}/* from_free_proc */
/*
* セレクション [
*/
#undef SELECT_TOGGLE
static char selected_string[MESSAGE_MAX];
/*
* ペースト(?)の要求があった。
*/
static Boolean
convert_selection(Widget w, Atom *selection, Atom *target, Atom *type_return, XtPointer *value_return, unsigned long *length_return, int *format_return)
{
XTextProperty ct;
Display *d = XtDisplay(w);
char *v = selected_string;
XmbTextListToTextProperty(d, &v, 1, XCompoundTextStyle, &ct);
*type_return = ct.encoding;
*length_return = ct.nitems;
*value_return = ct.value;
*format_return = ct.format;
return True;
}/* convert_selection */
static void
lose_selection(Widget w, Atom *selection)
{
return;
}/* lose_selection */
static void
own_selection(Widget w, XtPointer closure, XtPointer call_data)
{
Time time = XtLastTimestampProcessed(XtDisplay(w));
XtOwnSelection(w, XA_PRIMARY, time, convert_selection, lose_selection, NULL);
}/* own_selection */
/* ] セレクション */
/*
* コピーボタン
*/
static void
select_proc(Widget w, XtPointer closure, XtPointer call_data)
{
Widget dialog = XtParent(w);
String str;
char *p;
XtVaGetValues(dialog, XtNlabel, &str, NULL);
strncpyz(selected_string, str, sizeof(selected_string));
p = strrchr(selected_string, '\n');
if (p != NULL) {
*p = '\0';
if (*--p == '\n')
*p = '\0';
}
own_selection(toplevel, NULL, NULL);
}/* select_proc */
/*
*
*/
static char *
cryption(char *str, int decode_flag)
{
unsigned char *p = (unsigned char *)str;
while (*p != '\0') {
if (*p != '\n' && *p != 0xff - '\n')
*p ^= 0xff;
p++;
}/* while */
return str;
}/* cryption */
/*
* 開封ボタン
*/
static void
open_proc(Widget w, XtPointer closure, XtPointer call_data)
{
struct maddr_t *replyto = closure;
Widget dialog = XtParent(w);
Widget w_pkno = XtNameToWidget(dialog, "pkno");
String str, str_pkno;
XtVaGetValues(dialog, XtNlabel, &str, NULL);
cryption(str, 1);
XtVaSetValues(dialog, XtNlabel, str, NULL);
XtSetSensitive(w, False);
XtVaGetValues(w_pkno, XtNlabel, &str_pkno, NULL);
send_IPMSG_READMSG(replyto, strtoul(str_pkno, NULL, 0));
}/* open_proc */
/*
*
*/
static char *
mkmsg(const char *msg, const char *from)
{
char *ret, lbuf[64];
time_t now = time(NULL);
struct tm lc = *localtime(&now);
sprintf(lbuf, "\n\n%02d:%02d ", lc.tm_hour, lc.tm_min);
ret = XtMalloc(strlen(msg) + strlen(lbuf) + strlen(from) + 1);
strcat(strcat(strcpy(ret, msg), lbuf), from);
return ret;
}/* mkmsg */
/*
* メッセージが届きました。
*/
static void
recv_dialog(const char *msg, const char *from, const unsigned char *icon, struct maddr_t *replyto_, unsigned long opt, unsigned long pkno)
{
Widget popup, dialog;
Pixmap pix;
Dimension nx, ny;
char title[256], *label = NULL, *str_pkno = NULL;
struct from_t *fr = from_lookup(from);
struct maddr_t *replyto = (void *)XtMalloc(sizeof(*replyto));
*replyto = *replyto_;
if (fr == NULL)
fr = from_install(from);
popup = XtVaCreatePopupShell("recv_popup", transientShellWidgetClass, toplevel, NULL);
label = mkmsg(msg, from);
str_pkno = XtMalloc(20);
if (opt & IPMSG_SECRETOPT) {
cryption(label, 0);
sprintf(str_pkno, "%lu", pkno);
}
pix = XCreateBitmapFromData(XtDisplay(toplevel), XtWindow(toplevel), (char *)icon, 32, 32);
dialog = XtVaCreateManagedWidget("recv_from", dialogWidgetClass, popup,
XtNlabel, label,
XtNicon, pix,
NULL);
XtAddCallback(dialog, XtNdestroyCallback, destroy_proc, replyto);
XtAddCallback(dialog, XtNdestroyCallback, destroy_proc, label);
XtAddCallback(dialog, XtNdestroyCallback, destroy_proc, str_pkno);
XtAddCallback(dialog, XtNdestroyCallback, from_free_proc, fr);
XawDialogAddButton(dialog, "answer", answer_proc, replyto);
XawDialogAddButton(dialog, "done", done_proc, NULL);
XawDialogAddButton(dialog, "select", select_proc, NULL);
if (opt & IPMSG_SECRETOPT) {
XawDialogAddButton(dialog, "open", open_proc, replyto);
}
XtVaCreateManagedWidget("from", labelWidgetClass, dialog,
XtNlabel, from,
XtNmappedWhenManaged, False,
NULL);
XtVaCreateManagedWidget("pkno", labelWidgetClass, dialog,
XtNlabel, str_pkno,
XtNmappedWhenManaged, False,
NULL);
from_next_pos(fr, &nx, &ny);
from_count_up(fr);
XtVaSetValues(popup, XtNx, nx, XtNy, ny, NULL);
XtPopup(popup, XtGrabNone);
sprintf(title, "%s %s.%d", from, inet_ntoa(replyto->m_saddr.sin.sin_addr), (unsigned short)htons(replyto->m_saddr.sin.sin_port));
#ifdef NOTDEF
XStoreName(XtDisplay(popup), XtWindow(popup), title);
#else
{
char *v = title;
XTextProperty ct;
Display *d = XtDisplay(popup);
XmbTextListToTextProperty(d, &v, 1, XCompoundTextStyle, &ct);
XSetWMName(d, XtWindow(popup), &ct);
}
#endif
XBell(XtDisplay(toplevel), 20);
XFlush(XtDisplay(toplevel));
}/* recv_dialog */
/*
*
*/
static void
error_dialog(Widget w, const char *msg)
{
Position nx, ny;
Widget err_popup = XtVaCreatePopupShell("err_popup", transientShellWidgetClass, toplevel, NULL);
Widget err_dialog = XtVaCreateManagedWidget(msg, dialogWidgetClass, err_popup, NULL);
XawDialogAddButton(err_dialog, "ok", done_proc, NULL);
XtTranslateCoords(w, 0, 0, &nx, &ny);
XtVaSetValues(err_popup, XtNx, nx + 20, XtNy, ny + 20, NULL);
XtPopup(err_popup, XtGrabExclusive);
XBell(XtDisplay(toplevel), 20);
XFlush(XtDisplay(toplevel));
}/* error_dialog */
/*
*
*/
static unsigned char *
get_icon_data(XImage *img, unsigned char *buf)
{
int y;
unsigned char *d = buf;
for (y = 0; y < 32; y++) {
int x, z = 0;
for (x = 0; x < 32; x++) {
z >>= 1;
if (XGetPixel(img, x, y) != 0)
z |= 0x80;
if ((x & 7) == 7) {
*d++ = z;
z = 0;
}
}/* for */
}/* for */
return buf;
}/* get_icon_data */
/*
* 送信ボタン
*/
static void
send_proc(Widget w, XtPointer closure, XtPointer call_data)
{
struct maddr_t *replyto = closure;
Widget dialog = XtParent(w);
String msg, label;
Pixmap icon;
XImage *img;
Widget button = XtNameToWidget(dialog, "*icon_button");
int err = -1;
struct from_t *fr;
XtVaGetValues(button, XtNbitmap, &icon, NULL);
XtVaGetValues(dialog, XtNvalue, &msg, XtNlabel, &label, NULL);
img = XGetImage(XtDisplay(w), icon, 0, 0, 32, 32, 1L, XYPixmap);
if (msg[0] != '\0' && img != NULL) {
if (strlen(msg) < MESSAGE_MAX - 1 - 128) {
unsigned char pat[128];
get_icon_data(img, pat);
XDestroyImage(img);
last_icon = icon;
strcpy(last_msg, msg);
fr = from_lookup(label);
if (fr != NULL)
strncpyz(fr->fr_last_msg, msg, sizeof(fr->fr_last_msg));
XDefineCursor(XtDisplay(dialog), XtWindow(dialog), csr_clock);
XDefineCursor(XtDisplay(toplevel), XtWindow(toplevel), csr_clock);
XFlush(XtDisplay(dialog));
err = bro_send(msg, pat, replyto);
XUndefineCursor(XtDisplay(toplevel), XtWindow(toplevel));
XUndefineCursor(XtDisplay(dialog), XtWindow(dialog));
if (err == 0) {
if (bogus_fix)
XtPopdown(XtParent(dialog));
else
XtDestroyWidget(XtParent(dialog));
}
else {
error_dialog(dialog, "not_sent");
}
}
else {
error_dialog(dialog, "too_long");
}
}
}/* send_proc */
/*
* クリアボタン
*/
static void
clear_proc(Widget w, XtPointer closure, XtPointer call_data)
{
Widget dialog = XtParent(w);
Widget value = XtNameToWidget(dialog, "value");
XawTextBlock tb;
tb.firstPos = 0;
tb.length = 0;
tb.ptr = "";
tb.format = FMT8BIT;
XawTextReplace(value, 0, 9999, &tb);
XawTextSetInsertionPoint(value, 0);
}/* clear_proc */
/*
*
*/
static Widget
make_menu(const char *rsc, const char *inst, Widget parent, const char * const *list, int n, void (*callback)())
{
Widget menu = XtVaCreatePopupShell(rsc, simpleMenuWidgetClass, parent, NULL);
int i;
for (i = 0; i < n; i++) {
char iname[256];
Widget entry;
sprintf(iname, "%s%02d", inst, i);
entry = XtVaCreateManagedWidget(iname, smeBSBObjectClass, menu, NULL);
XtAddCallback(entry, XtNcallback, callback, (XtPointer)i);
}/* for */
return menu;
}/* make_menu */
/*
* アイコンポップアップメニュー
*/
static void
icon_select(Widget w, XtPointer closure, XtPointer call_data)
{
Pixmap pix;
Widget menu = XtParent(w);
Widget button = XtParent(menu);
XtVaGetValues(w, XtNleftBitmap, &pix, NULL);
XtVaSetValues(button, XtNbitmap, pix, NULL);
}/* icon_select */
/*
* 送信ダイアログ
* parent のウィンドウの付近にだします。
*/
static void
send_dialog(Widget parent, const struct maddr_t *dstaddr, const char *to)
{
struct from_t *fr = from_lookup(to);
String label = XtMalloc(strlen(to) + 1);
struct maddr_t *daddr = (void *)XtMalloc(sizeof(*daddr));
Widget send_popup = XtVaCreatePopupShell("send_popup", transientShellWidgetClass, toplevel, NULL);
Widget send_to = XtVaCreateManagedWidget("send_to", dialogWidgetClass, send_popup,
XtNlabel, (strcpy(label, to)),
XtNvalue, from_last_msg(fr),
NULL);
Widget icon_button = XtVaCreateManagedWidget("icon_button", menuButtonWidgetClass, send_to,
XtNfromVert, NULL,
XtNfromHoriz, NULL,
NULL);
Position nx, ny;
*daddr = *dstaddr;
XtAddCallback(send_to, XtNdestroyCallback, destroy_proc, label);
XtAddCallback(send_to, XtNdestroyCallback, destroy_proc, daddr);
make_menu("icon_menu", "icon", icon_button, NULL, NUMBER_OF_MENU, icon_select);
if (last_msg[0] == '\0') {
Widget entry = XtNameToWidget(icon_button, "*icon00");
XtVaGetValues(entry, XtNleftBitmap, &last_icon, NULL);
}
XtVaSetValues(icon_button, XtNbitmap, last_icon, NULL);
XawDialogAddButton(send_to, "clear", clear_proc, NULL);
XawDialogAddButton(send_to, "cancel", done_proc, NULL);
XawDialogAddButton(send_to, "send", send_proc, (XtPointer)daddr);
XtTranslateCoords(parent, 0, 0, &nx, &ny);
XtVaSetValues(send_popup, XtNx, nx, XtNy, ny, NULL);
XtPopup(send_popup, XtGrabNone);
}/* send_dialog */
/*
* 送信ダイアログを出すボタン
*/
static void
compose_proc(Widget w, XtPointer closure, XtPointer call_data)
{
Widget name_list = closure;
XawListReturnStruct *np = XawListShowCurrent(name_list);
if (np->list_index != XAW_LIST_NONE) {
struct ns_t *ns = ns_get(np->list_index);
if (ns != NULL)
send_dialog(XtParent(w), &ns->ns_maddr, np->string);
}
}/* compose_proc */
/*
* List Widget の操作 [
*/
/*
* List を空っぽにする。
*/
static int
list_clear(Widget list)
{
static String empty[1] = {NULL};
XawListChange(list, empty, 0, 0, True);
return 0;
}/* list_clear */
/*
* list の親が viewport だったら、
* 1 ページの行数を返す。つもり。
*/
static int
list_lines_per_page(Widget list)
{
int n, lines;
Widget view = XtParent(list);
XtVaGetValues(list, XtNnumberStrings, &n, NULL);
lines = n;
if (XtClass(view) == viewportWidgetClass) {
Widget vbar = XtNameToWidget(view, "vertical");
if (vbar != NULL) {
float shown, top;
XtVaGetValues(vbar, XtNshown, &shown, XtNtop, &top, NULL);
lines = floor(n * shown / 1.0);
}
}
return lines;
}/* list_lines_per_page */
/*
* List の親が Viewport だったら、
* vertical スクロールバーの位置を調整する。
*/
static void
list_manage_viewport(Widget list)
{
XawListReturnStruct *elm = XawListShowCurrent(list);
int idx = elm->list_index;
Widget view = XtParent(list);
if (idx != XAW_LIST_NONE && XtClass(view) == viewportWidgetClass) {
Widget vbar = XtNameToWidget(view, "vertical");
if (vbar != NULL) {
int n;
float shown, top;
XtVaGetValues(list, XtNnumberStrings, &n, NULL);
XtVaGetValues(vbar, XtNshown, &shown, NULL);
top = (double)idx / (n + 1);
XawScrollbarSetThumb(vbar, top, shown);
XtCallCallbacks(vbar, XtNjumpProc, &top);
}
}
}/* list_manage_viewport */
/*
* List のセレクトの位置を delta ずらす。
*/
static void
list_move_select(Widget list, int delta)
{
String *ls;
XawListReturnStruct *elm = XawListShowCurrent(list);
int n, idx = elm->list_index;
XtVaGetValues(list, XtNlist, &ls, XtNnumberStrings, &n, NULL);
if (elm->list_index == XAW_LIST_NONE)
idx = -1;
idx += delta;
if (idx >= n)
idx = n -1;
if (idx < 0)
idx = 0;
if (idx != elm->list_index && idx < n) {
XawListHighlight(list, idx);
list_manage_viewport(list);
}
}/* list_move_select */
/*
* List の要素のから、文字列 match とマッチした所を選択する。
*/
static void
list_select_match(Widget list, const char *match)
{
String *ls;
int n, i = 0, found = -1, len = strlen(match);
XtVaGetValues(list, XtNlist, &ls, XtNnumberStrings, &n, NULL);
while (i < n && found < 0) {
if (strncmpi(ls[i], match, len) == 0)
found = i;
else
i++;
}/* while */
if (found >= 0) {
XawListHighlight(list, found);
list_manage_viewport(list);
}
}/* list_select_match */
/*
* List にカーソルキーやページキーの処理をする。
*/
static void
do_control_key(Widget list, int ksym)
{
#ifndef XK_Page_Up
#define XK_Page_Up XK_Prior
#endif
#ifndef XK_Page_Down
#define XK_Page_Down XK_Next
#endif
int lpp;
switch (ksym) {
case XK_Up: list_move_select(list, -1); break;
case XK_Down: list_move_select(list, 1); break;
case XK_Home: list_move_select(list, -9999); break;
case XK_End: list_move_select(list, 9999); break;
case XK_Page_Up:
lpp = list_lines_per_page(list);
list_move_select(list, -lpp);
break;
case XK_Page_Down:
lpp = list_lines_per_page(list);
list_move_select(list, lpp);
break;
}/* switch */
}/* do_control_key */
/*
* List にカーソルキーやページキーの処理をさせるアクション。
* アクションのパラメータにキーの名前が入っている。
*/
static void
list_key_named_action(Widget list, XEvent *event, String *params, Cardinal *num_params)
{
int err = -1;
if (XtClass(list) == listWidgetClass && event->type == KeyPress) {
String arg = *params;
if (arg != NULL) {
struct kw_t {
const char *name;
int ksym;
};
static struct kw_t kw[] = {
{"Up", XK_Up},
{"Down", XK_Down},
{"Page_Up", XK_Page_Up},
{"Page_Down", XK_Page_Down},
{"Home", XK_Home},
{"End", XK_End},
{NULL, 0},
};
struct kw_t *p = kw;
while (p->name != NULL && strcmp(p->name, arg) != 0)
p++;
if (p->name != NULL) {
do_control_key(list, p->ksym);
err = 0;
}
if (err < 0)
fprintf(stderr, "%s: bad parameter for key_named_action.\n", arg);
}
}
}/* list_key_named_action */
/*
* リストでキーが押されたアクション
* ・カーソルキーでスクロール
* ・頭文字で、マッチした所に飛ぶ
*/
static void
list_key_action(Widget list, XEvent *event, String *params, Cardinal *num_params)
{
static Time last_time;
if (XtClass(list) == listWidgetClass && event->type == KeyPress) {
XKeyEvent *ev = &event->xkey;
int multi_event_time = XtGetMultiClickTime(XtDisplay(list)); /* milli-sec */
char kbuf[32];
KeySym ksym;
int len = XLookupString(ev, kbuf, sizeof(kbuf) - 1, &ksym, NULL);
multi_event_time *= 2; /* キータイプ速度はクリック速度より遅いので */
kbuf[len] = '\0';
switch (ksym) {
case XK_Up:
case XK_Down:
case XK_Home:
case XK_End:
case XK_Page_Up:
case XK_Page_Down:
do_control_key(list, ksym);
break;
default:
if (len != 0) {
static char match_str[32];
static int match_len;
if (ev->time - last_time >= multi_event_time) {
match_len = 0;
match_str[0] = '\0';
}
if (strlen(match_str) + len < sizeof(match_str) - 1) {
strcat(match_str, kbuf);
list_select_match(list, match_str);
}
}
break;
}/* switch */
last_time = ev->time;
}
}/* list_key_action */
/* ] List Widget の操作 */
/*
* 名前リストにキーイベントが届いたアクション
*/
static void
call_name_list_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
w = XtNameToWidget(toplevel, "*name_list");
if (w != NULL && XtClass(w) == listWidgetClass) {
list_key_action(w, event, params, num_params);
}
}/* call_name_list_action */
/*
* 名前リストを更新する。
*/
static int
refresh_name_list(Widget list)
{
ns_clear();
list_clear(list);
send_IPMSG_BR_ENTRY();
return 0;
}/* refresh_name_list */
/*
* ゾーンリストをクリックされた処理。
*/
static void
get_zone_proc(Widget w, XtPointer closure, XtPointer call_data)
{
Widget name_list = (Widget)closure;
refresh_name_list(name_list);
}/* get_zone_proc */
/*
* 自分の名前を見えなくするボタン。
*/
static void
disable_proc(Widget w, XtPointer closure, XtPointer call_data)
{
Boolean state;
XtVaGetValues(w, XtNstate, &state, NULL);
XDefineCursor(XtDisplay(w), XtWindow(w), csr_clock);
XDefineCursor(XtDisplay(toplevel), XtWindow(toplevel), csr_clock);
XFlush(XtDisplay(w));
bro_set_disable(state);
XUndefineCursor(XtDisplay(toplevel), XtWindow(toplevel));
XUndefineCursor(XtDisplay(w), XtWindow(w));
}/* disable_proc */
/*
* 終了
*/
static void
quit_proc(Widget w, XtPointer closure, XtPointer call_data)
{
exit(0);
}/* quit_proc */
/*
* ファイル *source に入力があった時呼び出される。
*/
static void
input_proc(XtPointer closure, int *source, XtInputId *id)
{
bro_recv_packet(*source);
}/* input_proc */
/*
* タイムアウト処理。
*/
static void
timeout_proc(XtPointer p1, XtIntervalId* id)
{
XtAppContext app_con = p1;
#if 0
static Widget icon_label;
static int done, count, status = -1;
static Pixmap icons[2];
if (!done) {
done = 1;
icon_label = XtNameToWidget(toplevel, "*icon_label");
if (icon_label != NULL) {
Widget icon_label2;
XtVaGetValues(icon_label, XtNbitmap, &icons[0], NULL);
icon_label2 = XtNameToWidget(toplevel, "*icon_label2");
if (icon_label2 != NULL) {
XtVaGetValues(icon_label2, XtNbitmap, &icons[1], NULL);
status = 1;
}
}
}
if (status >= 0) {
if (++count == 10) {
count = 0;
XtVaSetValues(icon_label, XtNbitmap, icons[status], NULL);
status = (status + 1) % COUNTOF(icons);
}
}
#endif
bro_job();
XtAppAddTimeOut(app_con, pause_time, timeout_proc, app_con);
}/* timeout_proc */
/*
* 何もイベントが無い時呼ばれる。
*/
static Boolean
work_proc(XtPointer closure)
{
bro_work();
return True; /* True -> remove proc */
}/* work_proc */
/*
* アクション
*/
/*
* 他の Command widget のコールバックを呼び出す、アクション処理ルーチン。
* 1. イベントのあったウィジェットから、親をたどって Dialog widget を探す。
* 2. その Dialog widget を起点に params の名前の widget を探す。
* 3. その widget の set() notify() unset() アクションを呼び出す。
*/
static void
direct_call_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
Widget dialog = w;
while (dialog != NULL && XtClass(dialog) != dialogWidgetClass)
dialog = XtParent(dialog);
if (dialog == NULL)
dialog = toplevel;
if (params != NULL) {
String name = *params;
Widget command = XtNameToWidget(dialog, name);
if (command != NULL) {
if (XtClass(command) == commandWidgetClass) {
XtCallActionProc(command, "set", event, NULL, ZERO);
XtCallActionProc(command, "notify", event, NULL, ZERO);
XtCallActionProc(command, "unset", event, NULL, ZERO);
}
else {
fprintf(stderr, "%s:direct_call_action: %s: not Command widget.\n", myname, name);
}
}
else {
fprintf(stderr, "%s:direct_call_action: %s: unknown widget.\n", myname, name);
}
}
else {
fprintf(stderr, "%s:direct_call_action: no arg.\n", myname);
}
}/* direct_call_action */
/*
* disable は Command じゃないので、direct_call_action が使えない。
*/
static void
disable_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
Widget disable = XtNameToWidget(toplevel, "*disable");
if (disable != NULL) {
Boolean state;
String action;
XtVaGetValues(disable, XtNstate, &state, NULL);
action = state ? "unset" : "set";
XtCallActionProc(disable, action, event, params, *num_params);
XtCallActionProc(disable, "notify", event, params, *num_params);
}
else
fprintf(stderr, "%s: disable toggle not found.\n", myname);
}/* disable_action */
/*
* IP Messenger のイベントの処理
*/
static void
ipmsg_notify(enum bro_event_t evt, void *closure, void *call_data)
{
switch (evt) {
case BRO_EV_START_WORK_PROC:
{
XtAppContext app_con = closure;
XtAppAddWorkProc(app_con, work_proc, app_con);
}
break;
case BRO_EV_LIST_CHANGED:
{
Widget name_list = closure;
char **ls, *name = NULL;
int n;
XawListReturnStruct *np = XawListShowCurrent(name_list);
if (np->list_index != XAW_LIST_NONE) {
name = str_dup(np->string);
}
ls = ns_list();
n = count_list((void *)ls);
XawListChange(name_list, ls, n, 0, True);
if (name != NULL) {
list_select_match(name_list, name);
free(name);
}
}
break;
case BRO_EV_RECV_MESSAGE:
{
struct msg_data_t *md = call_data;
struct maddr_t *rp = md->md_replyto;
recv_dialog(md->md_msg, md->md_from, md->md_icon, rp, md->md_opt, md->md_pkno);
}
break;
case BRO_EV_RECV_ACK:
if (debug_flag & 1)
fprintf(stderr, "%s に出したメッセージは届いたようです。\n", (char *)call_data);
break;
case BRO_EV_NO_ACK:
error_dialog(toplevel, "not_sent");
if (debug_flag & 1)
fprintf(stderr, "%s :メッセージは届かなかったようです。\n", (char *)call_data);
break;
case BRO_EV_MAX:
break;
}/* switch */
}/* ipmsg_notify */
/*
*
*/
static void
exit_proc(void)
{
send_IPMSG_BR_EXIT();
}/* exit_proc */
#define DEFSTR(name, class, default) {#name, class, XtRString, sizeof(String), XtOffsetOf(struct appr, name), XtRString, (default)}
#define DEFINT(name, class, default) {#name, class, XtRInt, sizeof(int), XtOffsetOf(struct appr, name), XtRImmediate, (XtPointer)(default)}
#define DEFBOOL(name, class, default) {#name, class, XtRBoolean, sizeof(Boolean), XtOffsetOf(struct appr, name), XtRImmediate, (XtPointer)(default)}
#define TITLE "XIP Messenger V0.8086"
/*
*
*/
int
main(int argc, char *argv[])
{
int ex = 1;
static String fallback_resources[] = {
#include "xipmsg.ad.h"
NULL,
};
static XtActionsRec actions[] = {
{"direct_call_action", direct_call_action},
{"call_name_list_action", call_name_list_action},
{"iconify_action", iconify_action},
{"disable_action", disable_action},
{"list_key_named_action", list_key_named_action},
};
static XrmOptionDescRec options[] = {
/* {option, specifier, argKind, value} */
{"-bogus_fix", ".bogusfix", XrmoptionNoArg, "True"},
{"-broadcast", ".broadcast", XrmoptionSepArg, NULL},
{"-disable", ".disable", XrmoptionNoArg, "True"},
{"-debug", ".debug", XrmoptionSepArg, NULL},
{"-name", ".name", XrmoptionSepArg, NULL},
{"-port", ".port", XrmoptionSepArg, NULL},
};
struct appr {
Boolean bogusfix;
String broadcast;
String debug;
Boolean disable;
String name;
String port;
} app_resources;
static XtResource resources[] = {
/* resource_{name, class, type, size}, */
/* resource_offset, default_type, default_addr */
DEFBOOL(bogusfix, "Bogusfix", False),
DEFSTR(broadcast, "Broadcast", "255.255.255.255"),
DEFSTR(debug, "Debug", NULL),
DEFBOOL(disable, "Disable", False),
DEFSTR(name, "Name", NULL),
DEFSTR(port, "Port", NULL),
};
static char usage_msg[] =
"usage: %s "
"[-bogus_fix][-disable]"
"[-broadcast xx.xx.xx.xx][-debug n]"
"[-port n][-name str]"
"[Xtoolkit options]"
"\n";
myname = argv[0];
XtSetLanguageProc(NULL, NULL, NULL);
toplevel = XtVaAppInitialize(&app_con, "XIpmsg", options, XtNumber(options), &argc, argv, fallback_resources, NULL);
if (argc > 1) {
fprintf(stderr, usage_msg, myname, argv[1]);
}
else {
int port = IPMSG_DEFAULT_PORT, bro_so;
char **bros;
char *name, entity_name[USERNAME_MAX], hostname[HOSTNAME_MAX];
XtVaGetApplicationResources(toplevel, &app_resources, resources, XtNumber(resources), NULL);
XtAppAddActions(app_con, actions, XtNumber(actions));
bogus_fix = app_resources.bogusfix;
if (app_resources.debug)
debug_flag = strtol(app_resources.debug, NULL, 0);
if (app_resources.disable)
bro_set_disable(True);
if (app_resources.port != NULL)
port = strtol(app_resources.port, NULL, 0);
if (app_resources.name != NULL)
name = app_resources.name;
else
name = getenv("USER");
if (name == NULL)
name = "anonymous";
bros = cvs_list(app_resources.broadcast);
strncpyz(entity_name, name, sizeof(entity_name));
gethostname(hostname, sizeof(hostname));
hostname[sizeof(hostname) - 1] = '\0';
if (strchr(hostname, '.') != NULL)
*strchr(hostname, '.') = '\0';
bro_so = bro_init(port, entity_name, hostname, (void *)bros);
if (bro_so < 0) {
fprintf(stderr, "%s: failed to initialize.\n", myname);
perror("bro_init");
}
else if ((from_db = db_new(FROM_DB_MAX, from_comp)) == NULL)
fprintf(stderr, "%s: malloc failed.\n", myname);
else {
Widget level0, main1, main2, commands, disable;
Widget name_view, name_list;
ex = 0;
csr_clock = XCreateFontCursor(XtDisplay(toplevel), XC_watch);
level0 = XtVaCreateManagedWidget("level0", panedWidgetClass, toplevel, NULL);
main1 = XtVaCreateManagedWidget("main1", formWidgetClass, level0, NULL);
main2 = XtVaCreateManagedWidget("main2", formWidgetClass, level0, NULL);
commands = XtVaCreateManagedWidget("commands", panedWidgetClass, main1, NULL);
XtVaCreateManagedWidget("icon_label", labelWidgetClass, main1, NULL);
XtVaCreateManagedWidget("icon_label2", labelWidgetClass, main1, NULL);
name_view = XtVaCreateManagedWidget("name_view", viewportWidgetClass, main2, NULL);
name_list = XtVaCreateManagedWidget("name_list", listWidgetClass, name_view, NULL);
XawDialogAddButton(commands, "quit", quit_proc, NULL);
XawDialogAddButton(commands, "get_zone", get_zone_proc, name_list);
XawDialogAddButton(commands, "compose", compose_proc, name_list);
disable = XtVaCreateManagedWidget("disable", toggleWidgetClass, commands,
XtNstate, app_resources.disable,
NULL);
XtAddCallback(disable, XtNcallback, disable_proc, NULL);
XtRealizeWidget(toplevel);
XStoreName(XtDisplay(toplevel), XtWindow(toplevel), TITLE);
XSetIconName(XtDisplay(toplevel), XtWindow(toplevel), "xipmsg");
XtAppAddTimeOut(app_con, pause_time, timeout_proc, app_con);
XtAppAddInput(app_con, bro_so, (XtPointer)XtInputReadMask, input_proc, NULL);
bro_add_callback(BRO_EV_LIST_CHANGED, ipmsg_notify, name_list);
bro_add_callback(BRO_EV_RECV_MESSAGE, ipmsg_notify, NULL);
bro_add_callback(BRO_EV_RECV_ACK, ipmsg_notify, NULL);
bro_add_callback(BRO_EV_NO_ACK, ipmsg_notify, NULL);
bro_add_callback(BRO_EV_START_WORK_PROC, ipmsg_notify, app_con);
refresh_name_list(name_list);
atexit(exit_proc);
XtAppMainLoop(app_con);
}
}
return ex;
}/* main */
syntax highlighted by Code2HTML, v. 0.9.1