/* * 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 #include /* floor() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* select() */ #include /* setitimer() */ /* includes below are order dependent */ #include /* htons() */ #include /* socket() */ #include /* socket() */ #include /* inet_addr() INADDR_ANY */ #include /* 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 */