/* * xmover -- X11 frontend for scsi media changers * * (c) 2002 Gerd Knorr */ #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 #include "chio.h" #include "RegEdit.h" #include "list.h" #include "man.h" /* --------------------------------------------------------------------- */ XtAppContext app_context; static String fallback_ressources[] = { #include "xmover.h" NULL }; struct ARGS { char *device; int help; int debug; } args; XtResource args_desc[] = { /* name, class, type, size, offset, default_type, default_addr */ { /* Strings */ "device", XtCString, XtRString, sizeof(char*), XtOffset(struct ARGS*,device), XtRString, "/dev/sch0", },{ /* Integer */ "help", XtCValue, XtRInt, sizeof(int), XtOffset(struct ARGS*,help), XtRString, "0" },{ "debug", XtCValue, XtRInt, sizeof(int), XtOffset(struct ARGS*,debug), XtRString, "0" } }; const int args_count = XtNumber(args_desc); XrmOptionDescRec opt_desc[] = { { "-c", "device", XrmoptionSepArg, NULL }, { "-device", "device", XrmoptionSepArg, NULL }, { "-debug", "debug", XrmoptionNoArg, "1" }, { "-h", "help", XrmoptionNoArg, "1" }, { "-help", "help", XrmoptionNoArg, "1" }, { "--help", "help", XrmoptionNoArg, "1" }, }; const int opt_count = (sizeof(opt_desc)/sizeof(XrmOptionDescRec)); /* prototypes */ static void elem_convert(Widget, XtPointer, XtPointer); static void elem_destination(Widget, XtPointer, XtPointer); /* --------------------------------------------------------------------- */ /* atoms */ static Atom XA_WM_DELETE_WINDOW; static Atom XA_TARGETS; static Atom _MOTIF_EXPORT_TARGETS; static Atom _XMOVER_TYPEUNIT; static Atom _XMOVER_REFRESH; static Atom targets[2]; static Cardinal ntargets; static void init_atoms(Display *dpy) { XA_WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False); XA_TARGETS = XInternAtom(dpy, "TARGETS", False); _MOTIF_EXPORT_TARGETS = XInternAtom(dpy, "_MOTIF_EXPORT_TARGETS", False); _XMOVER_TYPEUNIT = XInternAtom(dpy, "_XMOVER_TYPEUNIT", False); _XMOVER_REFRESH = XInternAtom(dpy, "_XMOVER_REFRESH", False); targets[ntargets++] = _XMOVER_TYPEUNIT; targets[ntargets++] = _XMOVER_REFRESH; } /* ---------------------------------------------------------------------- */ /* redirect stderr to error dialog box */ struct stderr_handler { Widget box; XmString str; int pipe; XtInputId id; }; static void stderr_input(XtPointer clientdata, int *src, XtInputId *id) { struct stderr_handler *h = clientdata; XmString item; Widget label; char buf[1024]; int rc; rc = read(h->pipe,buf,sizeof(buf)-1); if (rc <= 0) { /* Oops */ XtRemoveInput(h->id); close(h->pipe); XtDestroyWidget(h->box); free(h); } buf[rc] = 0; item = XmStringGenerate(buf, NULL, XmMULTIBYTE_TEXT,NULL); h->str = XmStringConcatAndFree(h->str,item); label = XmMessageBoxGetChild(h->box,XmDIALOG_MESSAGE_LABEL); XtVaSetValues(label,XmNlabelString,h->str,NULL); XtManageChild(h->box); }; static void stderr_ok_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { struct stderr_handler *h = clientdata; XmStringFree(h->str); h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL); XtUnmanageChild(h->box); } static void stderr_close_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { struct stderr_handler *h = clientdata; XmStringFree(h->str); h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL); } static void stderr_init(Widget parent) { struct stderr_handler *h; int p[2]; h = malloc(sizeof(*h)); memset(h,0,sizeof(*h)); h->str = XmStringGenerate("", NULL, XmMULTIBYTE_TEXT,NULL); h->box = XmCreateErrorDialog(parent,"errbox",NULL,0); XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_HELP_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(h->box,XmDIALOG_CANCEL_BUTTON)); XtAddCallback(h->box,XmNokCallback,stderr_ok_cb,h); XtAddCallback(XtParent(h->box),XmNpopdownCallback,stderr_close_cb,h); pipe(p); dup2(p[1],2); close(p[1]); h->pipe = p[0]; h->id = XtAppAddInput(app_context,h->pipe,(XtPointer)XtInputReadMask, stderr_input,h); } /* ---------------------------------------------------------------------- */ /* pointer games */ struct ptr_list { struct list_head list; Widget widget; }; static struct list_head ptr_wins; static Cursor ptr_idle; static Cursor ptr_busy; static void ptr_register(Widget widget) { struct ptr_list *item; if (XtWindow(widget)) XDefineCursor(XtDisplay(widget), XtWindow(widget),ptr_idle); item = malloc(sizeof(*item)); memset(item,0,sizeof(*item)); item->widget = widget; list_add_tail(&item->list,&ptr_wins); } #if 0 static void ptr_unregister(Widget widget) { struct list_head *item; struct ptr_list *win,*del; del = NULL; list_for_each(item,&ptr_wins) { win = list_entry(item, struct ptr_list, list); if (win->widget == widget) del = win; } if (del) list_del(&del->list); } #endif static void ptr_set(Cursor ptr) { struct list_head *item; struct ptr_list *win; Display *dpy = NULL; list_for_each(item,&ptr_wins) { win = list_entry(item, struct ptr_list, list); if (!XtWindow(win->widget)) continue; dpy = XtDisplay(win->widget); XDefineCursor(dpy, XtWindow(win->widget),ptr); } if (dpy) XSync(dpy,False); } static void ptr_init(Widget shell) { XColor white,red,dummy; Colormap cmap = DefaultColormapOfScreen(XtScreen(shell)); INIT_LIST_HEAD(&ptr_wins); ptr_idle = XCreateFontCursor(XtDisplay(shell),XC_left_ptr); ptr_busy = XCreateFontCursor(XtDisplay(shell),XC_watch); if (XAllocNamedColor(XtDisplay(shell),cmap,"white",&white,&dummy) && XAllocNamedColor(XtDisplay(shell),cmap,"red",&red,&dummy)) { XRecolorCursor(XtDisplay(shell),ptr_idle,&red,&white); XRecolorCursor(XtDisplay(shell),ptr_busy,&red,&white); } } /* --------------------------------------------------------------------- */ /* handle changer elements */ struct changer_elem { struct list_head list; int type; int unit; Widget draw,label; Widget menu,mref,mtag,msrc; struct changer_get_element info; }; static int fd; static struct changer_params params; static struct list_head elems; static struct changer_elem* elem_by_typeunit(int type, int unit) { struct list_head *item; struct changer_elem *elem; list_for_each(item,&elems) { elem = list_entry(item, struct changer_elem, list); if (elem->type == type && elem->unit == unit) return elem; } return NULL; } static void update_elem(struct changer_elem *elem) { XmString item,str = NULL; char buf[64]; int err; /* query */ elem->info.cge_type = elem->type; elem->info.cge_unit = elem->unit; err = ioctl(fd,CHIOGELEM,&elem->info); if (-1 == err) { fprintf(stderr,"CHIOGELEM(%d/%d): %s\n", elem->type,elem->unit,strerror(errno)); } /* build XmString */ sprintf(buf,"#%03d",elem->unit); item = XmStringGenerate(buf,NULL,XmMULTIBYTE_TEXT,NULL); str = XmStringConcatAndFree(str,item); if (elem->info.cge_flags & CGE_PVOLTAG) { sprintf(buf," [%-32.32s]",elem->info.cge_pvoltag); item = XmStringGenerate(buf,NULL,XmMULTIBYTE_TEXT,"voltag"); str = XmStringConcatAndFree(str,item); } if (elem->info.cge_status & CESTATUS_FULL) { item = XmStringGenerate(" full",NULL,XmMULTIBYTE_TEXT,"full"); str = XmStringConcatAndFree(str,item); } if (elem->info.cge_status & CESTATUS_EXCEPT) { item = XmStringGenerate(" error",NULL,XmMULTIBYTE_TEXT,"error"); str = XmStringConcatAndFree(str,item); } /* commit */ XtVaSetValues(elem->label,XmNlabelString,str,NULL); XmStringFree(str); } static void elem_tag_done_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { XmSelectionBoxCallbackStruct *cd = call_data; struct changer_elem *elem = clientdata; Widget text; char *str; if (args.debug) fprintf(stderr,"elem_tag_done_cb\n"); if (cd->reason == XmCR_OK) { struct changer_set_voltag tag; text = XmSelectionBoxGetChild(widget,XmDIALOG_TEXT); str = XmTextGetString(text); memset(&tag,0,sizeof(tag)); tag.csv_type = elem->type; tag.csv_unit = elem->unit; tag.csv_flags = CSV_PVOLTAG; if (strlen(str) > 0) strncpy(tag.csv_voltag,str,32); else tag.csv_flags |= CSV_CLEARTAG; ptr_set(ptr_busy); if (-1 == ioctl(fd,CHIOSVOLTAG,&tag)) perror("CHIOSVOLTAG"); update_elem(elem); ptr_set(ptr_idle); } XtDestroyWidget(XtParent(widget)); } static void elem_tag_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { struct changer_elem *elem = clientdata; Widget box,text; if (args.debug) fprintf(stderr,"elem_tag_cb\n"); box = XmCreatePromptDialog(elem->draw,"tag",NULL,0); text = XmSelectionBoxGetChild(box,XmDIALOG_TEXT); if (elem->info.cge_flags & CGE_PVOLTAG) XmTextSetString(text,elem->info.cge_pvoltag); XtAddCallback(box,XmNokCallback,elem_tag_done_cb,elem); XtAddCallback(box,XmNcancelCallback,elem_tag_done_cb,elem); XmdRegisterEditres(XtParent(box)); XtManageChild(box); } static void elem_src_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { struct changer_elem *elem = clientdata; struct changer_elem *src; struct changer_move move; if (args.debug) fprintf(stderr,"elem_src_cb\n"); src = elem_by_typeunit(elem->info.cge_srctype,elem->info.cge_srcunit); memset(&move,0,sizeof(move)); move.cm_fromtype = elem->type; move.cm_fromunit = elem->unit; move.cm_totype = elem->info.cge_srctype; move.cm_tounit = elem->info.cge_srcunit; ptr_set(ptr_busy); if (-1 == ioctl(fd,CHIOMOVE,&move)) perror("CHIOMOVE"); update_elem(elem); update_elem(src); ptr_set(ptr_idle); } static void elem_refresh_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { struct changer_elem *elem = clientdata; ptr_set(ptr_busy); update_elem(elem); ptr_set(ptr_idle); } static void elem_menu_eh(Widget widget, XtPointer clientdata, XEvent *event, Boolean *cont) { struct changer_elem *elem = clientdata; Boolean tag,src; if (args.debug) fprintf(stderr,"elem_menu_eh\n"); tag = (elem->info.cge_status & CESTATUS_FULL) ? True : False; XtVaSetValues(elem->mtag,XmNsensitive,tag,NULL); if (elem->msrc) { src = (elem->info.cge_status & CESTATUS_FULL) && (elem->info.cge_flags & CGE_SRC); XtVaSetValues(elem->msrc,XmNsensitive,src,NULL); } XmMenuPosition(elem->menu,(XButtonPressedEvent*)event); XtManageChild(elem->menu); } static struct changer_elem* add_elem(Widget parent, int type, int unit) { struct changer_elem *elem; Cardinal n = 0; Arg args[4]; char name[32]; elem = malloc(sizeof(*elem)); memset(elem,0,sizeof(*elem)); sprintf(name,"%d/%d",type,unit); elem->type = type; elem->unit = unit; elem->draw = XtVaCreateManagedWidget("d",xmDrawingAreaWidgetClass,parent, NULL); elem->label = XtVaCreateManagedWidget(name,xmLabelWidgetClass,elem->draw, NULL); update_elem(elem); /* dnd */ XtAddCallback(elem->label,XmNconvertCallback,elem_convert,elem); XtAddCallback(elem->draw,XmNdestinationCallback,elem_destination,elem); XtSetArg(args[n], XmNimportTargets, targets); n++; XtSetArg(args[n], XmNnumImportTargets, ntargets); n++; XmeDropSink(elem->draw,args,n); /* context menu */ elem->menu = XmCreatePopupMenu(elem->draw,"menu",NULL,0); XtAddEventHandler(elem->draw,ButtonPressMask,False,elem_menu_eh,elem); elem->mref = XtVaCreateManagedWidget("ref",xmPushButtonWidgetClass, elem->menu,NULL); XtAddCallback(elem->mref,XmNactivateCallback,elem_refresh_cb,elem); elem->mtag = XtVaCreateManagedWidget("tag",xmPushButtonWidgetClass, elem->menu,NULL); XtAddCallback(elem->mtag,XmNactivateCallback,elem_tag_cb,elem); if (elem->type == CHET_DT) { elem->msrc = XtVaCreateManagedWidget("src",xmPushButtonWidgetClass, elem->menu,NULL); XtAddCallback(elem->msrc,XmNactivateCallback,elem_src_cb,elem); } list_add_tail(&elem->list,&elems); return elem; } /* --------------------------------------------------------------------- */ /* drag'n'drop stuff */ static void elem_convert(Widget widget, XtPointer clientdata, XtPointer call_data) { XmConvertCallbackStruct *ccs = call_data; struct changer_elem *elem = clientdata; unsigned long *largs; char *name; Atom *targs; int n; if (args.debug) { name = XGetAtomName(XtDisplay(widget),ccs->target); fprintf(stderr,"elem_convert %s\n",name); XFree(name); } if ((ccs->target == XA_TARGETS) || (ccs->target == _MOTIF_EXPORT_TARGETS)) { targs = (Atom*)XtMalloc(sizeof(Atom)*8); n = 0; targs[n++] = _XMOVER_TYPEUNIT; targs[n++] = _XMOVER_REFRESH; ccs->value = targs; ccs->length = n; ccs->type = XA_ATOM; ccs->format = 32; ccs->status = XmCONVERT_MERGE; } if (ccs->target == _XMOVER_TYPEUNIT) { largs = (unsigned long*)XtMalloc(sizeof(*largs)); largs[0] = (elem->type << 16) | elem->unit; ccs->value = largs; ccs->length = 1; ccs->type = XA_INTEGER; ccs->format = 32; ccs->status = XmCONVERT_DONE; } if (ccs->target == _XMOVER_REFRESH) { ptr_set(ptr_busy); update_elem(elem); ptr_set(ptr_idle); ccs->value = NULL; ccs->length = 0; ccs->type = XA_INTEGER; ccs->format = 32; ccs->status = XmCONVERT_DONE; } } static void elem_transfer(Widget widget, XtPointer clientdata, XtPointer call_data) { XmSelectionCallbackStruct *scs = call_data; struct changer_elem *elem = clientdata; unsigned long *ldata = scs->value; XmTransferStatus status = XmTRANSFER_DONE_SUCCEED; Time time = XtLastTimestampProcessed(XtDisplay(widget)); int pending,i; char *name; if (args.debug) { name = XGetAtomName(XtDisplay(widget),scs->target); fprintf(stderr,"elem_transfer %s\n",name); XFree(name); } pending = scs->remaining; if (scs->target == XA_TARGETS) { for (i = 0; i < scs->length; i++) { if (ldata[i] == _XMOVER_TYPEUNIT) { XmTransferValue(scs->transfer_id, ldata[i], elem_transfer, elem, time); pending++; } } if (pending == scs->remaining) { /* no target found */ status = XmTRANSFER_DONE_FAIL; } } if (scs->target == _XMOVER_TYPEUNIT) { struct changer_move move; if (args.debug) fprintf(stderr,"move: %ld/%ld => %d/%d\n", ldata[0] >> 16, ldata[0] & 0xffff, elem->type, elem->unit); memset(&move,0,sizeof(move)); move.cm_fromtype = ldata[0] >> 16; move.cm_fromunit = ldata[0] & 0xffff; move.cm_totype = elem->type; move.cm_tounit = elem->unit; ptr_set(ptr_busy); if (move.cm_fromtype != move.cm_totype || move.cm_fromunit != move.cm_tounit) if (-1 == ioctl(fd,CHIOMOVE,&move)) perror("CHIOMOVE"); update_elem(elem); ptr_set(ptr_idle); XmTransferValue(scs->transfer_id, _XMOVER_REFRESH, elem_transfer, elem, time); pending++; } XFree(scs->value); if (1 == pending) XmTransferDone(scs->transfer_id, status); } static void elem_destination(Widget widget, XtPointer clientdata, XtPointer call_data) { XmDestinationCallbackStruct *dcs = call_data; struct changer_elem *elem = clientdata; Time time = XtLastTimestampProcessed(XtDisplay(widget)); if (args.debug) fprintf(stderr,"elem_destination\n"); XmTransferValue(dcs->transfer_id, XA_TARGETS, elem_transfer, elem, time); } /* --------------------------------------------------------------------- */ /* gui + basic callbacks */ static void destroy_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { XtDestroyWidget(clientdata); } static void about_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { Widget msgbox; msgbox = XmCreateInformationDialog(widget,"aboutbox",NULL,0); XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_HELP_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(msgbox,XmDIALOG_CANCEL_BUTTON)); XtAddCallback(msgbox,XmNokCallback,destroy_cb,msgbox); XtManageChild(msgbox); } static void close_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { Widget shell = clientdata; XtDestroyWidget(shell); exit(0); } static void refresh_cb(Widget widget, XtPointer clientdata, XtPointer call_data) { struct list_head *item; struct changer_elem *elem; ptr_set(ptr_busy); list_for_each(item,&elems) { elem = list_entry(item, struct changer_elem, list); update_elem(elem); } ptr_set(ptr_idle); } static void create_widgets(Widget shell) { Widget form,menubar,menu,push,frame,scroll,rowcol; int i; /* shell stuff */ XmdRegisterEditres(shell); XmAddWMProtocolCallback(shell,XA_WM_DELETE_WINDOW,close_cb,shell); /* form container */ form = XtVaCreateManagedWidget("form", xmFormWidgetClass, shell, NULL); /* menu- & toolbar */ menubar = XmCreateMenuBar(form,"bar",NULL,0); XtManageChild(menubar); /* menu -- file */ menu = XmCreatePulldownMenu(menubar,"fileM",NULL,0); XtVaCreateManagedWidget("file",xmCascadeButtonWidgetClass,menubar, XmNsubMenuId,menu,NULL); push = XtVaCreateManagedWidget("quit",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,close_cb,shell); /* menu -- changer */ menu = XmCreatePulldownMenu(menubar,"changerM",NULL,0); XtVaCreateManagedWidget("changer",xmCascadeButtonWidgetClass,menubar, XmNsubMenuId,menu,NULL); push = XtVaCreateManagedWidget("refresh",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,refresh_cb,NULL); /* menu - help */ menu = XmCreatePulldownMenu(menubar,"helpM",NULL,0); push = XtVaCreateManagedWidget("help",xmCascadeButtonWidgetClass,menubar, XmNsubMenuId,menu,NULL); XtVaSetValues(menubar,XmNmenuHelpWidget,push,NULL); push = XtVaCreateManagedWidget("man",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,man_cb,"xmover"); XtVaCreateManagedWidget("sep",xmSeparatorWidgetClass,menu,NULL); push = XtVaCreateManagedWidget("about",xmPushButtonWidgetClass,menu,NULL); XtAddCallback(push,XmNactivateCallback,about_cb,NULL); /* ie elems */ frame = XtVaCreateManagedWidget("ie", xmFrameWidgetClass, form, NULL); XtVaCreateManagedWidget("label",xmLabelWidgetClass,frame,NULL); rowcol = XtVaCreateManagedWidget("rc",xmRowColumnWidgetClass, frame,NULL); for (i = 0; i < params.cp_nportals; i++) add_elem(rowcol, CHET_IE, i); /* dt elems */ frame = XtVaCreateManagedWidget("dt", xmFrameWidgetClass, form, NULL); XtVaCreateManagedWidget("label",xmLabelWidgetClass,frame,NULL); rowcol = XtVaCreateManagedWidget("rc",xmRowColumnWidgetClass, frame,NULL); for (i = 0; i < params.cp_ndrives; i++) add_elem(rowcol, CHET_DT, i); /* st elems */ frame = XtVaCreateManagedWidget("st", xmFrameWidgetClass, form, NULL); XtVaCreateManagedWidget("label",xmLabelWidgetClass,frame,NULL); scroll = XmCreateScrolledWindow(frame,"scroll",NULL,0); XtManageChild(scroll); rowcol = XtVaCreateManagedWidget("rc",xmRowColumnWidgetClass, scroll,NULL); for (i = 0; i < params.cp_nslots; i++) add_elem(rowcol, CHET_ST, i); } /* --------------------------------------------------------------------- */ /* main */ static void usage(void) { fprintf(stderr, "xmover -- X11 frontend for scsi media changers\n" "options:\n" " -debug enable debug messages\n" " -device use instead of /dev/sch0\n"); } int main(int argc, char *argv[]) { Widget app_shell; int err; /* init X11 */ XtSetLanguageProc(NULL,NULL,NULL); app_shell = XtVaAppInitialize(&app_context, "xmover", opt_desc, opt_count, &argc, argv, fallback_ressources, NULL); XtGetApplicationResources(app_shell,&args, args_desc,args_count, NULL,0); if (args.help) { usage(); exit(1); } /* init changer */ fd = open(args.device,O_RDONLY); if (-1 == fd) { fprintf(stderr,"open %s: %s\n",args.device,strerror(errno)); exit(1); } err = ioctl(fd,CHIOGPARAMS,¶ms); if (-1 == err) { perror("CHIOGPARAMS"); exit(1); } INIT_LIST_HEAD(&elems); init_atoms(XtDisplay(app_shell)); if (!args.debug) stderr_init(app_shell); ptr_init(app_shell); ptr_register(app_shell); create_widgets(app_shell); XtRealizeWidget(app_shell); XtAppMainLoop(app_context); return 0; }