1 /*------------------------------------------------------------------------*/
2 /*--- AFNI Image Viewer - quicky adaptation from afni_plugin.c stuff. ----*/
3 /*------------------------------------------------------------------------*/
4 
5 #include "mrilib.h"
6 #include "imseq.h"
7 
8 /*------------------------------------------------------------------------*/
9 
10 typedef struct {
11    MCW_imseq *seq ;
12    MRI_IMARR *imar ;
13    int rgb_count ;
14    generic_func *kill_func ;
15    void *kill_data ;
16 } AIVVVV_imseq ;
17 
18 static AIVVVV_imseq *psq_global ;
19 static XtAppContext AIVVV_appcontext ;
20 
21 static NI_stream AIVVV_stream = (NI_stream)NULL ;
22 static char      AIVVV_strnam[64] ;
23 static int       AIVVV_have_dummy = 0 ;
24 
25 static void * AIVVV_imseq_popup( MRI_IMARR *, generic_func *, void * ) ;
26 static void   AIVVV_imseq_retitle( void * , char * ) ;
27 static XtPointer AIVVV_imseq_getim( int , int , XtPointer ) ;
28 static void AIVVV_imseq_send_CB( MCW_imseq * , XtPointer , ISQ_cbs * ) ;
29 static void AIVVV_imseq_addto( MRI_IMAGE *im ) ;  /* 25 Jul 2005 */
30 static RwcBoolean AIVVV_workproc( XtPointer ) ;      /* 25 Jul 2005 */
31 static void AIVVV_niml_quitter( char *, NI_stream , NI_element * ) ;
32 
33 /*------------------------------------------------------------------------*/
34 
35 static char *FALLback[] =
36   {   "AFNI*fontList:              9x15bold=charset1"    ,
37       "AFNI*pbar*fontList:         6x10=charset1"        ,
38       "AFNI*imseq*fontList:        7x13=charset1"        ,
39       "AFNI*background:            gray20"               ,
40       "AFNI*menu*background:       gray10"               ,
41       "AFNI*borderColor:           gray20"               ,
42       "AFNI*foreground:            yellow"               ,
43       "AFNI*borderWidth:           0"                    ,
44       "AFNI*troughColor:           blue"                 ,
45       "AFNI*XmLabel.translations:  #override<Btn2Down>:" , /* Motif 2.0 bug */
46       "AFNI*help*background:       black"                ,
47       "AFNI*help*foreground:       yellow"               ,
48       "AFNI*help*helpborder:       False"                ,
49       "AFNI*help*waitPeriod:       1066"                 ,
50       "AFNI*help*fontList:         9x15bold=charset1"    ,
51       "AFNI*cluefont:              9x15bold"             ,
52       "AFNI*help*cancelWaitPeriod: 50"                   ,
53       "AFNI*XmList.translations: #override"                /* 24 Feb 2007 */
54            "<Btn4Down>: ListPrevItem()\\n"
55            "<Btn5Down>: ListNextItem()"                  ,
56       "AFNI*XmText.translations: #override"
57            "<Btn4Down>: previous-line()\\n"
58            "<Btn5Down>: next-line()"                     ,
59       "AFNI*XmScrollBar.translations: #override"
60            "<Btn4Down>: IncrementUpOrLeft(0) IncrementUpOrLeft(1)\\n"
61            "<Btn5Down>: IncrementDownOrRight(1) IncrementDownOrRight(0)" ,
62    NULL } ;
63 
64 static MCW_DC       *MAIN_dc ;
65 static XtAppContext  MAIN_app ;
66 static MRI_IMARR    *MAIN_imar ;
67 
68 #define DEFAULT_NCOLOVR 20
69 
70 static char *INIT_colovr[DEFAULT_NCOLOVR] = {
71    "#ffff00" , "#ffcc00"   , "#ff9900"  , "#ff6900" , "#ff4400" , "#ff0000" ,
72    "#0000ff" , "#0044ff"   , "#0069ff"  , "#0099ff" , "#00ccff" , "#00ffff" ,
73    "green"   , "limegreen" , "violet"   , "hotpink" ,
74    "white"   , "#dddddd"   , "#bbbbbb"  , "black"
75 } ;
76 
77 static char *INIT_labovr[DEFAULT_NCOLOVR] = {
78    "yellow" , "yell-oran" , "oran-yell" , "orange"   , "oran-red" , "red"   ,
79    "dk-blue", "blue"      , "lt-blue1"  , "lt-blue2" , "blue-cyan", "cyan"  ,
80    "green"  , "limegreen" , "violet"    , "hotpink"  ,
81    "white"  , "gry-dd"    , "gry-bb"    , "black"
82 } ;
83 
84 static char wintit[256] = { "Images" } ;
85 /*------------------------------------------------------------------------*/
86 
killer(void * pt)87 static void killer( void *pt ){ exit(0); }
AFNI_handler(char * msg)88 static void AFNI_handler(char *msg){ return ; } /* hide X11 warnings */
89 
90 /*------------------------------------------------------------------------*/
91 /*! Called to start up display, after X11 has had time to get going. */
92 
timeout_CB(XtPointer client_data,XtIntervalId * id)93 static void timeout_CB( XtPointer client_data , XtIntervalId *id )
94 {
95 ENTRY("timeout_CB") ;
96    (void) AIVVV_imseq_popup( MAIN_imar , killer , NULL ) ;
97    if( AIVVV_stream != (NI_stream)NULL ){
98      XtAppAddWorkProc( AIVVV_appcontext, AIVVV_workproc, NULL ) ;
99      NI_register_doer( "QUIT" , AIVVV_niml_quitter ) ;
100      NI_register_doer( "EXIT" , AIVVV_niml_quitter ) ;
101    }
102    EXRETURN ;
103 }
104 
105 /*------------------------------------------------------------------------*/
106 
main(int argc,char * argv[])107 int main( int argc , char *argv[] )
108 {
109    int ii , quiet=0, verb=0 , iarg=1 , jj , dopad=0 , xpad=0,ypad=0 ;
110    MRI_IMAGE *im ;     /* 1 input image */
111    MRI_IMARR *qar ;    /* all input images */
112    Widget shell ;
113    int gnim ; char **gname=NULL ;   /* 23 Dec 2002: glob filenames */
114 
115    if( argc < 2 || strcmp(argv[1],"-help") == 0 ){
116      printf(
117       "Usage: aiv [-v] [-q] [-title] [-p xxxx ] image ...\n"
118       "\n"
119       "AFNI Image Viewer program.\n"
120       "\n"
121       "Shows the 2D images on the command line in an AFNI-like image viewer.\n"
122       "\n"
123       "Can also read images in NIML '<MRI_IMAGE...>' format from a TCP/IP socket.\n"
124       "\n"
125       "Image file formats are those supported by to3d:\n"
126       " * various MRI formats (e.g., DICOM, GEMS I.xxx)\n"
127       " * raw PPM or PGM\n"
128       " * JPEG (if djpeg is in the path)\n"
129       " * GIF, TIFF, BMP, and PNG (if netpbm is in the path)\n"
130       "\n"
131       "The '-v' option will make aiv print out the image filenames\n"
132       "  as it reads them - this can be a useful progress meter if\n"
133       "  the program starts up slowly.\n"
134       "\n"
135       "The '-q' option tells the program to be very quiet.\n"
136       "\n"
137       "The '-pad' option tells the program to pad all input images\n"
138       " (from the command line) to be the same size.\n"
139       " Images that are much smaller than the largest image will\n"
140       " also be inflated somewhat so as not to look tiny.\n"
141       "In the form '-pad X Y', where 'X' and 'Y' are integers >= 64,\n"
142       " then all images will be resized to fit inside those dimensions.\n"
143       "\n"
144       "The '-title WORD' option titles the window WORD. \n"
145       "The default is the name of the image file if only one is \n"
146       "specified on the command line. If many images are read in\n"
147       "the default window title is 'Images'.\n"
148       "\n"
149       "The '-p xxxx' option will make aiv listen to TCP/IP port 'xxxx'\n"
150       "for incoming images in the NIML '<MRI_IMAGE...>' format.  The\n"
151       "port number must be between 1024 and 65535, inclusive.  For\n"
152       "conversion to NIML '<MRI_IMAGE...>' format, see program im2niml.\n"
153       "\n"
154       "Normally, at least one image must be given on the command line.\n"
155       "If the '-p xxxx' option is used, then you don't have to input\n"
156       "any images this way; however, since the program requires at least\n"
157       "one image to start up, a crude 'X' will be displayed.  When the\n"
158       "first image arrives via the socket, the 'X' image will be replaced.\n"
159       "Subsequent images arriving by socket will be added to the sequence.\n"
160       "\n-----------------------------------------------------------------\n"
161       "Sample program fragment, for sending images from one program\n"
162       "into a copy of aiv (which that program also starts up):\n"
163       "\n"
164       "#include \"mrilib.h\"\n"
165       "NI_stream ns; MRI_IMAGE *im; float *far; int nx,ny;\n"
166       "system(\"aiv -p 4444 &\");                               /* start aiv */\n"
167       "ns = NI_stream_open( \"tcp:localhost:4444\" , \"w\" ); /* connect to it */\n"
168       "while(1){\n"
169       "  /** ......... create 2D nx X ny data into the far array .........**/\n"
170       "  im = mri_new_vol_empty( nx , ny , 1 , MRI_float );  /* fake image */\n"
171       "  mri_fix_data_pointer( far , im );                  /* attach data */\n"
172       "  NI_element nel = mri_to_niml(im);      /* convert to NIML element */\n"
173       "  NI_write_element( ns , nel , NI_BINARY_MODE );     /* send to aiv */\n"
174       "  NI_free_element(nel); mri_clear_data_pointer(im); mri_free(im);\n"
175       "}\n"
176       "NI_stream_writestring( ns , \"<ni_do ni_verb='QUIT'>\" ) ;\n"
177       "NI_stream_close( ns ) ;  /* do this, or the above, if done with aiv */\n"
178       "\n"
179       "-- Authors: RW Cox and DR Glen\n"
180      ) ;
181      PRINT_COMPILE_DATE ; exit(0) ;
182    }
183 
184    mainENTRY("aiv main") ; machdep() ;
185 
186    /* options? */
187 
188    while( iarg < argc && argv[iarg][0] == '-' ){
189      /*-- title --*/
190      if( strncmp(argv[iarg],"-title",6) == 0 ){
191          if (iarg+1 > argc) {
192             ERROR_message("Need a string after -title");
193             /* ignore option */
194          } else {
195             snprintf(wintit,128*sizeof(char),"%s", argv[++iarg]);
196          }
197          iarg++; continue;
198      }
199      /*-- verbosity --*/
200 
201      if( strncmp(argv[iarg],"-v",2) == 0 ){ verb=1; iarg++; continue; }
202      if( strncmp(argv[iarg],"-q",2) == 0 ){ quiet=1; iarg++; continue; }
203 
204      if( strcmp(argv[iarg],"-pad") == 0 ){  /* 02 Nov 2017 */
205        dopad++ ;
206        if( iarg+2 < argc && isdigit(argv[iarg+1][0]) && isdigit(argv[iarg+2][0]) ){
207          xpad = (int)strtod(argv[++iarg],NULL) ; if( xpad < 64 || xpad > 6666 ) xpad = 0 ;
208          ypad = (int)strtod(argv[++iarg],NULL) ; if( ypad < 64 || ypad > 6666 ) ypad = 0 ;
209        }
210        iarg++ ; continue ;
211      }
212 
213      /*-- port or sherry? --*/
214 
215      if( strncmp(argv[iarg],"-p",2) == 0 ){
216        int port = (int)strtol(argv[++iarg],NULL,10) ;
217        if( AIVVV_stream != NULL ){
218          ERROR_message("Can't use multiple '-p' options!") ;
219          iarg++ ; continue ;   /* skip to next option */
220        }
221        if( port <= 1023 ){
222          ERROR_message("Illegal value after -p; not listening.") ;
223        } else {
224          sprintf(AIVVV_strnam,"tcp:x:%d",port) ;
225          AIVVV_stream = NI_stream_open( AIVVV_strnam , "r" ) ;
226          if( AIVVV_stream == (NI_stream)NULL ){
227            ERROR_message("Can't listen to port %d!",port) ;
228          } else {
229            int nn ;
230            nn = NI_stream_goodcheck(AIVVV_stream,66) ;
231            if( verb ){
232              if( nn > 0 ) INFO_message("Connected to port %d",port) ;
233              else         INFO_message("Listening to port %d",port) ;
234            }
235          }
236        }
237        iarg++ ; continue ;
238      }
239 
240      /*-- WTF? --*/
241 
242      ERROR_exit("Unknown option: %s",argv[iarg]) ;
243    }
244    if (!quiet) { PRINT_VERSION("aiv") ; }
245 
246    /* glob filenames, read images */
247 
248    MCW_file_expand( argc-iarg , argv+iarg , &gnim , &gname ) ;
249    if( gnim == 0 && AIVVV_stream==(NI_stream)NULL )
250      ERROR_exit("No filenames on command line (after wildcard expansion)?!") ;
251 
252    INIT_IMARR(MAIN_imar) ;
253 
254    for( ii=0 ; ii < gnim ; ii++ ){
255      if( !THD_filename_ok(gname[ii]) ) continue ;  /* 23 Apr 2003 */
256      if (!strcmp(wintit,"Images") && gnim == 1) { /* give window a useful name */
257          snprintf(wintit,128*sizeof(char),"%s", gname[ii]);
258      }
259      if( verb ) fprintf(stderr,"+") ;
260      qar = mri_read_file( gname[ii] ) ;  /* may have more than 1 2D image */
261      if( qar == NULL || IMARR_COUNT(qar) < 1 ){
262        if( verb )
263          fprintf(stderr,"\n** Can't read file %s - skipping!",gname[ii]) ;
264        else
265          fprintf(stderr,"** AIV ERROR: Can't read file %s - skipping!\n",gname[ii]) ;
266        continue ;
267      } else if( verb ){
268        fprintf(stderr,"%s",gname[ii]) ;
269      }
270 
271      if( IMARR_COUNT(qar) == 1 ){  /* possibly a 3D dataset from AFNI */
272        im = IMARR_SUBIM(qar,0) ;
273        if( im != NULL && im->nz > 1 ){  /* break 3D array into 2D images */
274          MRI_IMARR *zar = mri_to_imarr(im) ;
275          if( zar != NULL ){ DESTROY_IMARR(qar) ; qar = zar ; }
276        }
277      }
278 
279      for( jj=0 ; jj < IMARR_COUNT(qar) ; jj++ ){
280        im = IMARR_SUBIM(qar,jj) ;
281        if( im != NULL && im->nx > 1 && im->ny > 1 ){
282          if( im->nx > 6666 || im->ny > 6666 ){ /* no big images [03 Nov 2017] */
283            float sx,sy ; MRI_IMAGE *qim ;
284            sx = (im->nx > 6000) ? im->nx/6000.0f : 1.0f ;
285            sy = (im->ny > 6000) ? im->ny/6000.0f : 1.0f ;
286            sx = MIN(sx,sy) ;
287            qim = mri_resize( im , (int)(sx*im->nx) , (int)(sx*im->ny) ) ;
288            if( qim != NULL ){ mri_free(im) ; im = qim ; }
289          }
290          ADDTO_IMARR(MAIN_imar,im);
291        }
292      }
293      FREE_IMARR(qar) ;  /* just FREE, not DESTROY */
294    }
295 
296    /* print a message about the images? */
297 
298    if( IMARR_COUNT(MAIN_imar) == 0 && AIVVV_stream==(NI_stream)NULL )
299      ERROR_exit("No images found on command line!?") ;
300 
301    if( !quiet && IMARR_COUNT(MAIN_imar) > 0 ){
302      fprintf(stderr, (verb) ? " = " : "++ " ) ;
303      if( IMARR_COUNT(MAIN_imar) == 1 )fprintf(stderr,"1 image\n") ;
304      else fprintf(stderr,"%d images\n",IMARR_COUNT(MAIN_imar)) ;
305    }
306 
307    if( gnim > 0 ) MCW_free_expand( gnim , gname ) ;
308 
309    /*----- padding? [02 Nov 2017] -----*/
310 
311    if( dopad && IMARR_COUNT(MAIN_imar) > 1 ){
312      int nxtop=0,nytop=0, nx,ny, bx,tx,by,ty, fx,fy ; MRI_IMAGE *qim ;
313      float sx,sy ; char mark='\0'; int xfin,yfin ;
314 
315      if( !quiet ) fprintf(stderr,"++ padding ") ;
316      for( ii=0 ; ii < IMARR_COUNT(MAIN_imar) ; ii++ ){
317        im = IMARR_SUBIM(MAIN_imar,ii) ;
318        if( im != NULL ){ nxtop = MAX(nxtop,im->nx); nytop = MAX(nytop,im->ny); }
319      }
320      if( xpad > 0 && ypad > 0 ){ xfin = xpad ; yfin = ypad ; }
321      else                      { xfin = nxtop; yfin = nytop; }
322      for( ii=0 ; ii < IMARR_COUNT(MAIN_imar) ; ii++ ){
323        im = IMARR_SUBIM(MAIN_imar,ii) ;
324        nx = im->nx ; ny = im->ny ; mark = '\0' ;
325        if( xpad > 0 && ypad > 0 ){
326          if( nx == xpad && ny == ypad ) continue ;
327          sx = (float)(xpad) / (float)(nx) ;
328          sy = (float)(ypad) / (float)(ny) ; sx = MIN(sx,sy) ;
329          qim = mri_resize( im , (int)(sx*nx) , (int)(sx*ny) ) ;
330          if( qim != NULL ){ mri_free(im) ; im = qim ; }
331          nx = im->nx ; ny = im->ny ;
332          mark = '*' ;
333        } else {
334          if( nx >= nxtop && ny >= nytop ) continue ;
335          if( nx <= 2     || ny <= 2     ) continue ;
336          sx = (float)(nxtop) / (float)(nx) ;
337          sy = (float)(nytop) / (float)(ny) ; sx = MIN(sx,sy) ;
338          fx = (int)sx; fy = (int)sy; fx = MIN(fx,fy) ;
339          if( fx > 2 && dopad == 1 ){
340            qim = mri_dup2D(fx,im) ;
341            if( qim != NULL ){ mri_free(im) ; im = qim ; }
342            nx = im->nx ; ny = im->ny ;
343            mark = '+' ;
344          } else if( sx > 1.1f ){
345            qim = mri_resize( im , (int)(sx*nx) , (int)(sx*ny) ) ;
346            if( qim != NULL ){ mri_free(im) ; im = qim ; }
347            nx = im->nx ; ny = im->ny ;
348            mark = '*' ;
349          }
350        }
351        if( nx < xfin ){ bx = (xfin-nx)/2 ; tx = xfin-nx-bx ; }
352        else           { bx = tx = 0 ; }
353        if( ny < yfin ){ by = (yfin-ny)/2 ; ty = yfin-ny-by ; }
354        else           { by = ty = 0 ; }
355        if( bx > 0 || tx > 0 || by > 0 || ty > 0 ){
356          qim = mri_zeropad_2D( bx,tx , by,ty , im ) ;
357          if( qim != NULL ){ mri_free(im) ; im = qim ; }
358          if( mark == '\0' ) mark = '.' ;
359        }
360        IMARR_SUBIM(MAIN_imar,ii) = im ;
361        if( !quiet ) fprintf(stderr,"%c",mark) ;
362      }
363      if( !quiet ) fprintf(stderr,"!\n") ;
364 
365    } /*----- end of padding ----*/
366 
367    /* connect to X11 */
368 
369    shell = XtVaAppInitialize( &MAIN_app , "AFNI" , NULL , 0 ,
370                               &argc , argv , FALLback , NULL ) ;
371 
372    if( shell == NULL )
373      ERROR_exit("Can't initialize X11") ;
374 
375    AIVVV_appcontext = XtWidgetToApplicationContext(shell) ;
376 
377    (void) XtAppSetWarningHandler(MAIN_app,AFNI_handler) ;
378 
379    MAIN_dc = MCW_new_DC( shell, 128,
380                          DEFAULT_NCOLOVR, INIT_colovr, INIT_labovr, 1.0, 0 ) ;
381 
382    srand48((long)time(NULL)+(long)getpid()) ;
383 
384    /* wait a little bit, then popup the image viewer window */
385 
386    (void) XtAppAddTimeOut( MAIN_app, 234, timeout_CB, NULL ) ;
387    XtAppMainLoop(MAIN_app) ;  /* will never return */
388    exit(0) ;
389 }
390 
391 /*-------------------------------------------------------------------------*/
392 /*! Open the image viewer. */
393 
AIVVV_imseq_popup(MRI_IMARR * imar,generic_func * kfunc,void * kdata)394 static void * AIVVV_imseq_popup( MRI_IMARR *imar, generic_func *kfunc, void *kdata )
395 {
396    int ntot , ii ;
397    MRI_IMAGE *im , *cim ;
398    AIVVVV_imseq *psq ;
399 
400 ENTRY("AIVVV_imseq_popup") ;
401 
402    if( imar == NULL ) RETURN(NULL) ;
403 
404    ntot = IMARR_COUNT(imar) ;
405    if( ntot == 0 ){               /** dummy 'X' image **/
406 #define QQ_NXYZ 16
407      static byte xxx[QQ_NXYZ*QQ_NXYZ] = {
408        0,0,21,131,135,135,135,8,0,0,3,100,135,135,135,128,
409        0,0,0,108,255,255,255,86,0,0,115,255,255,255,255,121,
410        0,0,0,21,216,255,255,213,0,19,223,255,255,255,187,5,
411        0,0,0,0,92,244,255,255,114,114,255,255,255,234,58,0,
412        0,0,0,0,0,174,255,255,252,230,255,255,255,130,0,0,
413        0,0,0,0,0,58,244,255,255,255,255,255,228,29,0,0,
414        0,0,0,0,0,0,118,255,255,255,255,255,74,0,0,0,
415        0,0,0,0,0,0,55,248,255,255,255,199,3,0,0,0,
416        0,0,0,0,0,5,170,255,255,255,255,227,32,0,0,0,
417        0,0,0,0,0,104,255,255,255,255,255,255,140,5,0,0,
418        0,0,0,0,13,217,255,255,252,215,255,255,255,67,0,0,
419        0,0,0,0,159,255,255,255,212,23,233,255,255,187,7,0,
420        0,0,0,81,241,255,255,255,85,0,72,255,255,255,66,0,
421        0,0,16,206,255,255,255,212,0,0,8,193,255,255,237,12,
422        0,0,94,255,255,255,255,86,0,0,0,73,255,255,255,121,
423        0,14,129,134,134,134,85,1,0,0,0,3,106,134,134,127 } ;
424      byte *ar ; MRI_IMAGE *xim ;
425 
426      ar = (byte *)malloc(sizeof(byte)*QQ_NXYZ*QQ_NXYZ) ;
427      memcpy(ar,xxx,sizeof(byte)*QQ_NXYZ*QQ_NXYZ) ;
428      xim = mri_new_vol_empty( QQ_NXYZ,QQ_NXYZ,1 , MRI_byte ) ;
429      mri_fix_data_pointer( ar , xim ) ;
430      xim->dx = xim->dy = 16.0 ;
431      ADDTO_IMARR(imar,xim) ; ntot = 1 ; AIVVV_have_dummy = 1 ;
432    }
433 
434    /* psq holds all the data needed for viewing */
435 
436    psq = psq_global = (AIVVVV_imseq *)calloc(1,sizeof(AIVVVV_imseq)) ;
437    if( psq == NULL ) RETURN(NULL) ;  /* should never happen */
438 
439    psq->imar = imar ;
440 
441    psq->kill_func = kfunc ;
442    psq->kill_data = kdata ;
443    psq->rgb_count = 0 ;
444 
445    /* actually create the viewer */
446 
447    psq->seq = open_MCW_imseq( MAIN_dc , AIVVV_imseq_getim , psq ) ;
448 
449    drive_MCW_imseq( psq->seq , isqDR_clearstat , NULL ) ;
450 
451    { ISQ_options opt ;       /* change some options from the defaults */
452 
453      ISQ_DEFAULT_OPT(opt) ;
454      opt.save_one = False ;  /* change to Save:bkg */
455      opt.save_pnm = False ;
456      drive_MCW_imseq( psq->seq , isqDR_options      , (XtPointer) &opt ) ;
457      drive_MCW_imseq( psq->seq , isqDR_periodicmont , (XtPointer) 0    ) ;
458      drive_MCW_imseq( psq->seq , isqDR_penbbox      , (XtPointer) 0    ) ;
459    }
460 
461    /* make it popup */
462 
463    drive_MCW_imseq( psq->seq , isqDR_realize, NULL ) ;
464    drive_MCW_imseq( psq->seq , isqDR_title, wintit ) ;
465 
466    if( ntot == 1 )
467      drive_MCW_imseq( psq->seq , isqDR_onoffwid , (XtPointer) isqDR_offwid );
468    else
469      drive_MCW_imseq( psq->seq , isqDR_onoffwid , (XtPointer) isqDR_onwid  );
470 
471    /* show the first image */
472 
473    drive_MCW_imseq( psq->seq , isqDR_display, (XtPointer)0 ) ;
474 
475    drive_MCW_imseq( psq->seq , isqDR_imhelptext ,
476                     (XtPointer)
477                      "Keyboard Shortcuts:\n\n"
478                      "q = close window         a = fix aspect ratio\n"
479                      "p = toggle panning mode  c = crop image mode\n"
480                      "s = sharpen image        m = toggle Min-to-Max\n"
481                      "D = open Disp panel      M = open Montage panel\n"
482                      "S = Save image           l = left-right mirror\n"
483                      "> = Page Up   = forward  1 image\n"
484                      "< = Page Down = backward 1 image\n"
485                      "v/V = Video image sequence up/down\n"
486                      "r/R = Ricochet image sequence up/down\n"
487                      "i/I = image fraction down/up\n"
488                      "e = toggle edge detection in image\n"
489                    ) ;
490 
491    RETURN( (void *)psq ) ;
492 }
493 
494 /*-----------------------------------------------------------------------*/
495 
AIVVV_imseq_retitle(void * handle,char * title)496 static void AIVVV_imseq_retitle( void *handle , char *title )
497 {
498    AIVVVV_imseq *psq = (AIVVVV_imseq *) handle ;
499 
500    if( psq == NULL || psq->seq == NULL || title == NULL ) return ;
501    drive_MCW_imseq( psq->seq , isqDR_title, title ) ;
502    return ;
503 }
504 
505 /*------------------------------------------------------------------
506    Routine to provide data to the imseq.
507    Just returns the control information, or the selected image.
508 --------------------------------------------------------------------*/
509 
AIVVV_imseq_getim(int n,int type,XtPointer handle)510 static XtPointer AIVVV_imseq_getim( int n, int type, XtPointer handle )
511 {
512    AIVVVV_imseq *psq = (AIVVVV_imseq *) handle ;
513    int ntot = 0 ;
514 
515 ENTRY("AIVVV_imseq_getim") ;
516 
517    if( psq->imar != NULL ) ntot = IMARR_COUNT(psq->imar) ;
518    if( ntot < 1 ) ntot = 1 ;
519 
520    /*--- send control info ---*/
521 
522    if( type == isqCR_getstatus ){
523      MCW_imseq_status *stat = myXtNew( MCW_imseq_status ) ; /* will be freed */
524                                                             /* when imseq is */
525                                                             /* destroyed    */
526      stat->num_total  = ntot ;
527      stat->num_series = ntot ;
528      stat->send_CB    = AIVVV_imseq_send_CB ;
529      stat->parent     = NULL ;
530      stat->aux        = NULL ;
531 
532      stat->transforms0D = NULL ;
533      stat->transforms2D = NULL ;
534      stat->slice_proj   = NULL ;
535 
536      RETURN( (XtPointer)stat ) ;
537    }
538 
539    /*--- return a copy of an image
540          (since the imseq will delete it when it is done) ---*/
541 
542    if( type == isqCR_getimage || type == isqCR_getqimage ){
543      MRI_IMAGE *im = NULL , *rim ;
544 
545      if( psq->imar != NULL ){
546        if( n < 0 ) n = 0 ; else if( n >= ntot ) n = ntot-1 ;
547        rim = IMARR_SUBIMAGE(psq->imar,n) ;
548        if( psq->rgb_count > 0 )
549          im = mri_to_rgb( rim ) ;
550        else
551          im = mri_copy( rim ) ;
552      }
553      RETURN( (XtPointer)im ) ;
554    }
555 
556    RETURN(NULL) ; /* any other request gets nothing */
557 }
558 
559 /*---------------------------------------------------------------------------
560    Routine called when the imseq wants to send a message.
561    In this case, all we need to handle is the destroy message,
562    so that we can free some memory.
563 -----------------------------------------------------------------------------*/
564 
AIVVV_imseq_send_CB(MCW_imseq * seq,XtPointer handle,ISQ_cbs * cbs)565 static void AIVVV_imseq_send_CB( MCW_imseq *seq, XtPointer handle, ISQ_cbs *cbs )
566 {
567    AIVVVV_imseq *psq = (AIVVVV_imseq *) handle ;
568 
569 ENTRY("AIVVV_imseq_send_CB") ;
570 
571    switch( cbs->reason ){
572      case isqCR_destroy:{
573        myXtFree(psq->seq) ;
574        DESTROY_IMARR( psq->imar ) ;
575 
576        if( psq->kill_func != NULL )
577          psq->kill_func( psq->kill_data ) ;
578          free(psq) ;
579      }
580      break ;
581 
582      case isqCR_newimage:{
583        drive_MCW_imseq( psq->seq, isqDR_display, (XtPointer)ITOP(cbs->nim) );
584      }
585      break ;
586    }
587    EXRETURN ;
588 }
589 
590 /*---------------------------------------------------------------------------*/
591 /* Add an image to the display sequence. */
592 
AIVVV_imseq_addto(MRI_IMAGE * im)593 static void AIVVV_imseq_addto( MRI_IMAGE *im )
594 {
595    int ntot , num ;
596    AIVVVV_imseq *psq = psq_global ;
597 
598 ENTRY("AIVVV_imseq_addto") ;
599 
600    if( im == NULL ) EXRETURN ;
601 
602    if( im->nx < 4 || im->ny < 4 ) EXRETURN ;
603 
604    /** if more than 1 slice, carve up the volume
605        into multiple 2D slices and recursively add each one **/
606 
607    num = im->nz * im->nt * im->nu * im->nv * im->nw ;
608    if( num > 1 ){
609      MRI_IMAGE *qim ; int kk,nb ; char *iar=(char *)mri_data_pointer(im) ;
610      nb = im->nx * im->ny * im->pixel_size ;
611      for( kk=0 ; kk < num ; kk++ ){
612        qim = mri_new_vol_empty( im->nx , im->ny , 1, im->kind ) ;
613        qim->dx = im->dx ; qim->dy = im->dy ;
614        mri_fix_data_pointer( iar + kk*nb , qim ) ;
615        AIVVV_imseq_addto( qim ) ;
616      }
617      EXRETURN ;
618    }
619 
620    if( AIVVV_have_dummy ){             /* replace the dummy 'X' */
621      IMARR_SUBIM(psq->imar,0) = im ;
622      AIVVV_have_dummy = 0 ;
623    } else {
624      ADDTO_IMARR(psq->imar,im) ;       /* add to sequence */
625    }
626    if( im->kind == MRI_rgb ) psq->rgb_count++ ;
627 
628    drive_MCW_imseq( psq->seq , isqDR_newseq , psq ) ;
629 
630    ntot = IMARR_COUNT(psq->imar) ;
631    if( ntot == 2 )
632      drive_MCW_imseq( psq->seq , isqDR_onoffwid , (XtPointer) isqDR_onwid ) ;
633 
634    drive_MCW_imseq( psq->seq , isqDR_reimage , (XtPointer)ITOP(ntot-1) ) ;
635    EXRETURN ;
636 }
637 
638 /*---------------------------------------------------------------------------*/
639 /* Listen to the socket and see if any new <MRI_IMAGE ...> NIML elements
640    appear.
641 -----------------------------------------------------------------------------*/
642 
AIVVV_workproc(XtPointer fred)643 static RwcBoolean AIVVV_workproc( XtPointer fred )
644 {
645    int nn ;
646    NI_element *nel ;
647    MRI_IMAGE *im ;
648 
649    nn = NI_stream_goodcheck(AIVVV_stream,3) ;
650    if( nn < 0 ){                              /* dead? reopen it */
651      NI_stream_closenow(AIVVV_stream) ;
652      NI_sleep(9) ;
653      AIVVV_stream = NI_stream_open( AIVVV_strnam , "r" ) ;
654      return False ;
655    }
656 
657    nn = NI_stream_hasinput(AIVVV_stream,9) ;   /* anything? */
658    if( nn <= 0 ) return False ;                /* no data */
659 
660    /* read data, add image */
661 
662    nel = (NI_element *)NI_read_element(AIVVV_stream,99) ;
663    if( NI_element_type(nel) != NI_ELEMENT_TYPE ){  /* bad read */
664      NI_free_element(nel) ; return False ;
665    }
666 
667    /* the only type of element we deal with is MRI_IMAGE */
668 
669    im = niml_to_mri( nel ) ;   /* convert element to image */
670    NI_free_element( nel ) ;
671    AIVVV_imseq_addto( im ) ;   /* add image to the display sequence */
672    return False ;
673 }
674 
675 /*---------------------------------------------------------------------------*/
676 
AIVVV_niml_quitter(char * obj,NI_stream ns,NI_element * nel)677 static void AIVVV_niml_quitter( char *obj, NI_stream ns, NI_element *nel )
678 {
679    INFO_message("Received remote command to exit") ;
680    NI_stream_closenow(AIVVV_stream) ;
681    NI_sleep(333) ;
682    exit(0) ;
683 }
684