PLplot: tk.c Source File

PLplot 5.15.0

tk.c
Go to the documentation of this file.
1 // PLplot Tcl/Tk and Tcl-DP device drivers.
2 // Should be broken up somewhat better to allow use of DP w/o X.
3 //
4 // Maurice LeBrun
5 // 30-Apr-93
6 //
7 // Copyright (C) 2004 Maurice LeBrun
8 // Copyright (C) 2004 Joao Cardoso
9 // Copyright (C) 2004 Andrew Ross
10 //
11 // This file is part of PLplot.
12 //
13 // PLplot is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU Library General Public License as published
15 // by the Free Software Foundation; either version 2 of the License, or
16 // (at your option) any later version.
17 //
18 // PLplot is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU Library General Public License for more details.
22 //
23 // You should have received a copy of the GNU Library General Public License
24 // along with PLplot; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 //
27
28 //
29 // #define DEBUG_ENTER
30 //
31
32 #define DEBUG
33
34 #include "plDevs.h"
35
36 #ifdef PLD_tk
37
38 #define NEED_PLDEBUG
39 #include "pltkd.h"
40 #include "pltcl.h"
41 #include "tcpip.h"
42 #include "drivers.h"
43 #include "metadefs.h"
44 #include "plevent.h"
45 #include <X11/keysym.h>
46
47 #if PL_HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50 #include <sys/types.h>
51 #if HAVE_SYS_WAIT_H
52 # include <sys/wait.h>
53 #endif
54 #include <sys/stat.h>
55 #include <fcntl.h>
56 #include <errno.h>
57 #include <signal.h>
58
59 #ifdef PLD_dp
60 # include <dp.h>
61 #endif
62
63 // Device info
64 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_tk = "tk:Tcl/TK Window:1:tk:7:tk\n";
65
66
67 // Number of instructions to skip between updates
68
69 #define MAX_INSTR 100
70
71 // Pixels/mm
72
73 #define PHYSICAL 0 // Enables physical scaling..
74
75 // These need to be distinguished since the handling is slightly different.
76
77 #define LOCATE_INVOKED_VIA_API 1
78 #define LOCATE_INVOKED_VIA_DRIVER 2
79
80 #define STR_LEN 10
81 #define CMD_LEN 100
82
83 // A handy command wrapper
84
85 #define tk_wr( code ) \
86 if ( code ) { abort_session( pls, "Unable to write to PDFstrm" ); }
87
88 //--------------------------------------------------------------------------
89 // Function prototypes
90
91 // Driver entry and dispatch setup
92
93 void plD_dispatch_init_tk( PLDispatchTable *pdt );
94
95 void plD_init_tk( PLStream * );
96 void plD_line_tk( PLStream *, short, short, short, short );
97 void plD_polyline_tk( PLStream *, short *, short *, PLINT );
98 void plD_eop_tk( PLStream * );
99 void plD_bop_tk( PLStream * );
100 void plD_tidy_tk( PLStream * );
101 void plD_state_tk( PLStream *, PLINT );
102 void plD_esc_tk( PLStream *, PLINT, void * );
103 void plD_init_dp( PLStream *pls );
104
105 // various
106
107 static void init( PLStream *pls );
108 static void tk_start( PLStream *pls );
109 static void tk_stop( PLStream *pls );
110 static void tk_di( PLStream *pls );
111 static void tk_fill( PLStream *pls );
112 static void WaitForPage( PLStream *pls );
113 static void CheckForEvents( PLStream *pls );
114 static void HandleEvents( PLStream *pls );
115 static void init_server( PLStream *pls );
116 static void launch_server( PLStream *pls );
117 static void flush_output( PLStream *pls );
118 static void plwindow_init( PLStream *pls );
119 static void link_init( PLStream *pls );
120 static void GetCursor( PLStream *pls, PLGraphicsIn *ptr );
121 static void tk_XorMod( PLStream *pls, PLINT *ptr );
122 static void set_windowname( PLStream *pls );
123
124 // performs Tk-driver-specific initialization
125
126 static int pltkdriver_Init( PLStream *pls );
127
128 // Tcl/TK utility commands
129
130 static void tk_wait( PLStream *pls, const char * );
131 static void abort_session( PLStream *pls, const char * );
132 static void server_cmd( PLStream *pls, const char *, int );
133 static void tcl_cmd( PLStream *pls, const char * );
134 static void copybuf( PLStream *pls, const char *cmd );
135 static int pltk_toplevel( Tk_Window *w, Tcl_Interp *interp );
136
137 static void ProcessKey( PLStream *pls );
138 static void ProcessButton( PLStream *pls );
139 static void LocateKey( PLStream *pls );
140 static void LocateButton( PLStream *pls );
141 static void Locate( PLStream *pls );
142
143 // These are internal TCL commands
144
145 static int Abort( ClientData, Tcl_Interp *, int, char ** );
146 static int Plfinfo( ClientData, Tcl_Interp *, int, char ** );
147 static int KeyEH( ClientData, Tcl_Interp *, int, char ** );
148 static int ButtonEH( ClientData, Tcl_Interp *, int, char ** );
149 static int LookupTkKeyEvent( PLStream *pls, Tcl_Interp *interp,
150 int argc, char **argv );
151 static int LookupTkButtonEvent( PLStream *pls, Tcl_Interp *interp,
152 int argc, char **argv );
153
154 static char *drvoptcmd = NULL; // tcl command from command line option parsing
155
156 static DrvOpt tk_options[] = { { "tcl_cmd", DRV_STR, &drvoptcmd, "Execute tcl command" },
157 { NULL, DRV_INT, NULL, NULL } };
158
159 void plD_dispatch_init_tk( PLDispatchTable *pdt )
160 {
161 #ifndef ENABLE_DYNDRIVERS
162 pdt->pl_MenuStr = "Tcl/TK Window";
163 pdt->pl_DevName = "tk";
164 #endif
165 pdt->pl_type = plDevType_Interactive;
166 pdt->pl_seq = 7;
167 pdt->pl_init = (plD_init_fp) plD_init_tk;
168 pdt->pl_line = (plD_line_fp) plD_line_tk;
169 pdt->pl_polyline = (plD_polyline_fp) plD_polyline_tk;
170 pdt->pl_eop = (plD_eop_fp) plD_eop_tk;
171 pdt->pl_bop = (plD_bop_fp) plD_bop_tk;
172 pdt->pl_tidy = (plD_tidy_fp) plD_tidy_tk;
173 pdt->pl_state = (plD_state_fp) plD_state_tk;
174 pdt->pl_esc = (plD_esc_fp) plD_esc_tk;
175 }
176
177 //--------------------------------------------------------------------------
178 // plD_init_dp()
179 // plD_init_tk()
180 // init_tk()
181 //
182 // Initialize device.
183 // TK-dependent stuff done in tk_start(). You can set the display by
184 // calling plsfnam() with the display name as the (string) argument.
185 //--------------------------------------------------------------------------
186
187 void
188 plD_init_tk( PLStream *pls )
189 {
190 pls->dp = 0;
191 plParseDrvOpts( tk_options );
192 init( pls );
193 }
194
195 void
196 plD_init_dp( PLStream *pls )
197 {
198 #ifdef PLD_dp
199 pls->dp = 1;
200 #else
201 fprintf( stderr, "The Tcl-DP driver hasn't been installed!\n" );
202 pls->dp = 0;
203 #endif
204 init( pls );
205 }
206
207 static void
208 tk_wr_header( PLStream *pls, const char *header )
209 {
210 tk_wr( pdf_wr_header( pls->pdfs, header ) );
211 }
212
213 static void
214 init( PLStream *pls )
215 {
216 U_CHAR c = (U_CHAR) INITIALIZE;
217 TkDev *dev;
218 PLFLT pxlx, pxly;
219 int xmin = 0;
220 int xmax = PIXELS_X - 1;
221 int ymin = 0;
222 int ymax = PIXELS_Y - 1;
223
224 dbug_enter( "plD_init_tk" );
225
226 pls->color = 1; // Is a color device
227 pls->termin = 1; // Is an interactive terminal
228 pls->dev_di = 1; // Handle driver interface commands
229 pls->dev_flush = 1; // Handle our own flushes
230 pls->dev_fill0 = 1; // Handle solid fills
231 pls->dev_fill1 = 1; // Driver handles pattern fills
232 pls->server_nokill = 1; // don't kill if ^C
233 pls->dev_xor = 1; // device support xor mode
234
235 // Activate plot buffer. To programmatically save a file we can't call
236 // plreplot(), but instead one must send a command to plserver. As there is
237 // no API call for this, the user must use the plserver "save/print" menu
238 // entries. Activating the plot buffer enables the normal
239 // plmkstrm/plcpstrm/plreplot/plend1 way of saving plots.
240 //
241 pls->plbuf_write = 1;
242
243 // Specify buffer size if not yet set (can be changed by -bufmax option).
244 // A small buffer works best for socket communication
245
246 if ( pls->bufmax == 0 )
247 {
248 if ( pls->dp )
249 pls->bufmax = 450;
250 else
251 pls->bufmax = 3500;
252 }
253
254 // Allocate and initialize device-specific data
255
256 if ( pls->dev != NULL )
257 free( (void *) pls->dev );
258
259 pls->dev = calloc( 1, (size_t) sizeof ( TkDev ) );
260 if ( pls->dev == NULL )
261 plexit( "plD_init_tk: Out of memory." );
262
263 dev = (TkDev *) pls->dev;
264
265 dev->iodev = (PLiodev *) calloc( 1, (size_t) sizeof ( PLiodev ) );
266 if ( dev->iodev == NULL )
267 plexit( "plD_init_tk: Out of memory." );
268
269 dev->exit_eventloop = FALSE;
270
271 // Variables used in querying plserver for events
272
273 dev->instr = 0;
274 dev->max_instr = MAX_INSTR;
275
276 // Start interpreter and spawn server process
277
278 tk_start( pls );
279
280 // Get ready for plotting
281
282 dev->xold = PL_UNDEFINED;
283 dev->yold = PL_UNDEFINED;
284
285 #if PHYSICAL
286 pxlx = (double) PIXELS_X / dev->width * DPMM;
287 pxly = (double) PIXELS_Y / dev->height * DPMM;
288 #else
289 pxlx = (double) PIXELS_X / LPAGE_X;
290 pxly = (double) PIXELS_Y / LPAGE_Y;
291 #endif
292
293 plP_setpxl( pxlx, pxly );
294 plP_setphy( xmin, xmax, ymin, ymax );
295
296 // Send init info
297
298 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
299
300 // The header and version fields are useful when the client & server
301 // reside on different machines
302
303 tk_wr_header( pls, PLSERV_HEADER );
304 tk_wr_header( pls, PLSERV_VERSION );
305
306 tk_wr_header( pls, "xmin" );
307 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) xmin ) );
308
309 tk_wr_header( pls, "xmax" );
310 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) xmax ) );
311
312 tk_wr_header( pls, "ymin" );
313 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) ymin ) );
314
315 tk_wr_header( pls, "ymax" );
316 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) ymax ) );
317
318 tk_wr_header( pls, "" );
319
320 // Write color map state info
321 plD_state_tk( pls, PLSTATE_CMAP0 );
322 plD_state_tk( pls, PLSTATE_CMAP1 );
323
324 // Good place to make sure the data transfer is working OK
325
326 flush_output( pls );
327 }
328
329 //--------------------------------------------------------------------------
330 // plD_line_tk()
331 //
332 // Draw a line in the current color from (x1,y1) to (x2,y2).
333 //--------------------------------------------------------------------------
334
335 void
336 plD_line_tk( PLStream *pls, short x1, short y1, short x2, short y2 )
337 {
338 U_CHAR c;
339 U_SHORT xy[4];
340 TkDev *dev = (TkDev *) pls->dev;
341
342 CheckForEvents( pls );
343
344 if ( x1 == dev->xold && y1 == dev->yold )
345 {
346 c = (U_CHAR) LINETO;
347 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
348
349 xy[0] = (U_SHORT) x2;
350 xy[1] = (U_SHORT) y2;
351 tk_wr( pdf_wr_2nbytes( pls->pdfs, xy, 2 ) );
352 }
353 else
354 {
355 c = (U_CHAR) LINE;
356 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
357
358 xy[0] = (U_SHORT) x1;
359 xy[1] = (U_SHORT) y1;
360 xy[2] = (U_SHORT) x2;
361 xy[3] = (U_SHORT) y2;
362 tk_wr( pdf_wr_2nbytes( pls->pdfs, xy, 4 ) );
363 }
364 dev->xold = x2;
365 dev->yold = y2;
366
367 if ( pls->pdfs->bp > (size_t) pls->bufmax )
368 flush_output( pls );
369 }
370
371 //--------------------------------------------------------------------------
372 // plD_polyline_tk()
373 //
374 // Draw a polyline in the current color from (x1,y1) to (x2,y2).
375 //--------------------------------------------------------------------------
376
377 void
378 plD_polyline_tk( PLStream *pls, short *xa, short *ya, PLINT npts )
379 {
380 U_CHAR c = (U_CHAR) POLYLINE;
381 TkDev *dev = (TkDev *) pls->dev;
382
383 CheckForEvents( pls );
384
385 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
386 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) npts ) );
387
388 tk_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) xa, npts ) );
389 tk_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) ya, npts ) );
390
391 dev->xold = xa[npts - 1];
392 dev->yold = ya[npts - 1];
393
394 if ( pls->pdfs->bp > (size_t) pls->bufmax )
395 flush_output( pls );
396 }
397
398 //--------------------------------------------------------------------------
399 // plD_eop_tk()
400 //
401 // End of page.
402 // User must hit <RETURN> to continue.
403 //--------------------------------------------------------------------------
404
405 void
406 plD_eop_tk( PLStream *pls )
407 {
408 U_CHAR c = (U_CHAR) EOP;
409
410 dbug_enter( "plD_eop_tk" );
411
412 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
413 flush_output( pls );
414 if ( !pls->nopause )
415 WaitForPage( pls );
416 }
417
418 //--------------------------------------------------------------------------
419 // plD_bop_tk()
420 //
421 // Set up for the next page.
422 //--------------------------------------------------------------------------
423
424 void
425 plD_bop_tk( PLStream *pls )
426 {
427 U_CHAR c = (U_CHAR) BOP;
428 TkDev *dev = (TkDev *) pls->dev;
429
430 dbug_enter( "plD_bop_tk" );
431
432 dev->xold = PL_UNDEFINED;
433 dev->yold = PL_UNDEFINED;
434 pls->page++;
435 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
436 }
437
438 //--------------------------------------------------------------------------
439 // plD_tidy_tk()
440 //
441 // Close graphics file
442 //--------------------------------------------------------------------------
443
444 void
445 plD_tidy_tk( PLStream *pls )
446 {
447 TkDev *dev = (TkDev *) pls->dev;
448
449 dbug_enter( "plD_tidy_tk" );
450
451 if ( dev != NULL )
452 tk_stop( pls );
453 }
454
455 //--------------------------------------------------------------------------
456 // plD_state_tk()
457 //
458 // Handle change in PLStream state (color, pen width, fill attribute, etc).
459 //--------------------------------------------------------------------------
460
461 void
462 plD_state_tk( PLStream *pls, PLINT op )
463 {
464 U_CHAR c = (U_CHAR) CHANGE_STATE;
465 int i;
466
467 dbug_enter( "plD_state_tk" );
468
469 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
470 tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) );
471
472 switch ( op )
473 {
474 case PLSTATE_WIDTH:
475 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) ( pls->width ) ) );
476 break;
477
478 case PLSTATE_COLOR0:
479 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->icol0 ) );
480
481 if ( pls->icol0 == PL_RGB_COLOR )
482 {
483 tk_wr( pdf_wr_1byte( pls->pdfs, pls->curcolor.r ) );
484 tk_wr( pdf_wr_1byte( pls->pdfs, pls->curcolor.g ) );
485 tk_wr( pdf_wr_1byte( pls->pdfs, pls->curcolor.b ) );
486 }
487 break;
488
489 case PLSTATE_COLOR1:
490 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->icol1 ) );
491 break;
492
493 case PLSTATE_FILL:
494 tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) pls->patt ) );
495 break;
496
497 case PLSTATE_CMAP0:
498 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->ncol0 ) );
499 for ( i = 0; i < pls->ncol0; i++ )
500 {
501 tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap0[i].r ) );
502 tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap0[i].g ) );
503 tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap0[i].b ) );
504 }
505 break;
506
507 case PLSTATE_CMAP1:
508 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->ncol1 ) );
509 for ( i = 0; i < pls->ncol1; i++ )
510 {
511 tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap1[i].r ) );
512 tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap1[i].g ) );
513 tk_wr( pdf_wr_1byte( pls->pdfs, pls->cmap1[i].b ) );
514 }
515 // Need to send over the control points too!
516 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->ncp1 ) );
517 for ( i = 0; i < pls->ncp1; i++ )
518 {
519 tk_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->cmap1cp[i].c1 ) );
520 tk_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->cmap1cp[i].c2 ) );
521 tk_wr( pdf_wr_ieeef( pls->pdfs, (float) pls->cmap1cp[i].c3 ) );
522 tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) pls->cmap1cp[i].alt_hue_path ) );
523 }
524 break;
525 }
526
527 if ( pls->pdfs->bp > (size_t) pls->bufmax )
528 flush_output( pls );
529 }
530
531 //--------------------------------------------------------------------------
532 // plD_esc_tk()
533 //
534 // Escape function.
535 // Functions:
536 //
537 // PLESC_EXPOSE Force an expose (just passes token)
538 // PLESC_RESIZE Force a resize (just passes token)
539 // PLESC_REDRAW Force a redraw
540 // PLESC_FLUSH Flush X event buffer
541 // PLESC_FILL Fill polygon
542 // PLESC_EH Handle events only
543 // PLESC_XORMOD Xor mode
544 //
545 //--------------------------------------------------------------------------
546
547 void
548 plD_esc_tk( PLStream *pls, PLINT op, void *ptr )
549 {
550 U_CHAR c = (U_CHAR) ESCAPE;
551
552 dbug_enter( "plD_esc_tk" );
553
554 switch ( op )
555 {
556 case PLESC_DI:
557 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
558 tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) );
559 tk_di( pls );
560 break;
561
562 case PLESC_EH:
563 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
564 tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) );
565 HandleEvents( pls );
566 break;
567
568 case PLESC_FLUSH:
569 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
570 tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) );
571 flush_output( pls );
572 break;
573
574 case PLESC_FILL:
575 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
576 tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) );
577 tk_fill( pls );
578 break;
579
580 case PLESC_GETC:
581 GetCursor( pls, (PLGraphicsIn *) ptr );
582 break;
583
584 case PLESC_XORMOD:
585 tk_XorMod( pls, (PLINT *) ptr );
586 break;
587
588 default:
589 tk_wr( pdf_wr_1byte( pls->pdfs, c ) );
590 tk_wr( pdf_wr_1byte( pls->pdfs, (U_CHAR) op ) );
591 }
592 }
593
594 //--------------------------------------------------------------------------
595 // tk_XorMod()
596 //
597 // enter (mod = 1) or leave (mod = 0) xor mode
598 //
599 //--------------------------------------------------------------------------
600
601 static void
602 tk_XorMod( PLStream *pls, PLINT *ptr )
603 {
604 if ( *ptr != 0 )
605 server_cmd( pls, "$plwidget cmd plxormod 1 st", 1 );
606 else
607 server_cmd( pls, "$plwidget cmd plxormod 0 st", 1 );
608 }
609
610
611 //--------------------------------------------------------------------------
612 // GetCursor()
613 //
614 // Waits for a graphics input event and returns coordinates.
615 //--------------------------------------------------------------------------
616
617 static void
618 GetCursor( PLStream *pls, PLGraphicsIn *ptr )
619 {
620 TkDev *dev = (TkDev *) pls->dev;
621 PLGraphicsIn *gin = &( dev->gin );
622
623 // Initialize
624
625 plGinInit( gin );
626 dev->locate_mode = LOCATE_INVOKED_VIA_API;
627 plD_esc_tk( pls, PLESC_FLUSH, NULL );
628 server_cmd( pls, "$plwidget configure -xhairs on", 1 );
629
630 // Run event loop until a point is selected
631
632 while ( gin->pX < 0 && dev->locate_mode )
633 {
634 Tk_DoOneEvent( 0 );
635 }
636
637 // Clean up
638
639 server_cmd( pls, "$plwidget configure -xhairs off", 1 );
640 *ptr = *gin;
641 }
642
643 //--------------------------------------------------------------------------
644 // tk_di
645 //
646 // Process driver interface command.
647 // Just send the command to the remote PLplot library.
648 //--------------------------------------------------------------------------
649
650 static void
651 tk_di( PLStream *pls )
652 {
653 TkDev *dev = (TkDev *) pls->dev;
654 char str[STR_LEN];
655
656 dbug_enter( "tk_di" );
657
658 // Safety feature, should never happen
659
660 if ( dev == NULL )
661 {
662 plabort( "tk_di: Illegal call to driver (not yet initialized)" );
663 return;
664 }
665
666 // Flush the buffer before proceeding
667
668 flush_output( pls );
669
670 // Change orientation
671
672 if ( pls->difilt & PLDI_ORI )
673 {
674 snprintf( str, STR_LEN, "%f", pls->diorot );
675 Tcl_SetVar( dev->interp, "rot", str, 0 );
676
677 server_cmd( pls, "$plwidget cmd plsetopt -ori $rot", 1 );
678 pls->difilt &= ~PLDI_ORI;
679 }
680
681 // Change window into plot space
682
683 if ( pls->difilt & PLDI_PLT )
684 {
685 snprintf( str, STR_LEN, "%f", pls->dipxmin );
686 Tcl_SetVar( dev->interp, "xl", str, 0 );
687 snprintf( str, STR_LEN, "%f", pls->dipymin );
688 Tcl_SetVar( dev->interp, "yl", str, 0 );
689 snprintf( str, STR_LEN, "%f", pls->dipxmax );
690 Tcl_SetVar( dev->interp, "xr", str, 0 );
691 snprintf( str, STR_LEN, "%f", pls->dipymax );
692 Tcl_SetVar( dev->interp, "yr", str, 0 );
693
694 server_cmd( pls, "$plwidget cmd plsetopt -wplt $xl,$yl,$xr,$yr", 1 );
695 pls->difilt &= ~PLDI_PLT;
696 }
697
698 // Change window into device space
699
700 if ( pls->difilt & PLDI_DEV )
701 {
702 snprintf( str, STR_LEN, "%f", pls->mar );
703 Tcl_SetVar( dev->interp, "mar", str, 0 );
704 snprintf( str, STR_LEN, "%f", pls->aspect );
705 Tcl_SetVar( dev->interp, "aspect", str, 0 );
706 snprintf( str, STR_LEN, "%f", pls->jx );
707 Tcl_SetVar( dev->interp, "jx", str, 0 );
708 snprintf( str, STR_LEN, "%f", pls->jy );
709 Tcl_SetVar( dev->interp, "jy", str, 0 );
710
711 server_cmd( pls, "$plwidget cmd plsetopt -mar $mar", 1 );
712 server_cmd( pls, "$plwidget cmd plsetopt -a $aspect", 1 );
713 server_cmd( pls, "$plwidget cmd plsetopt -jx $jx", 1 );
714 server_cmd( pls, "$plwidget cmd plsetopt -jy $jy", 1 );
715 pls->difilt &= ~PLDI_DEV;
716 }
717
718 // Update view
719
720 server_cmd( pls, "update", 1 );
721 server_cmd( pls, "plw::update_view $plwindow", 1 );
722 }
723
724 //--------------------------------------------------------------------------
725 // tk_fill()
726 //
727 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
728 //--------------------------------------------------------------------------
729
730 static void
731 tk_fill( PLStream *pls )
732 {
733 PLDev *dev = (PLDev *) pls->dev;
734
735 dbug_enter( "tk_fill" );
736
737 tk_wr( pdf_wr_2bytes( pls->pdfs, (U_SHORT) pls->dev_npts ) );
738
739 tk_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) pls->dev_x, pls->dev_npts ) );
740 tk_wr( pdf_wr_2nbytes( pls->pdfs, (U_SHORT *) pls->dev_y, pls->dev_npts ) );
741
742 dev->xold = PL_UNDEFINED;
743 dev->yold = PL_UNDEFINED;
744 }
745
746 //--------------------------------------------------------------------------
747 // tk_start
748 //
749 // Create TCL interpreter and spawn off server process.
750 // Each stream that uses the tk driver gets its own interpreter.
751 //--------------------------------------------------------------------------
752
753 static void
754 tk_start( PLStream *pls )
755 {
756 TkDev *dev = (TkDev *) pls->dev;
757
758 dbug_enter( "tk_start" );
759
760 // Instantiate a TCL interpreter, and get rid of the exec command
761
762 dev->interp = Tcl_CreateInterp();
763
764 if ( Tcl_Init( dev->interp ) != TCL_OK )
765 {
766 fprintf( stderr, "%s\n", Tcl_GetStringResult( dev->interp ) );
767 abort_session( pls, "Unable to initialize Tcl" );
768 }
769
770 tcl_cmd( pls, "rename exec {}" );
771
772 // Set top level window name & initialize
773
774 set_windowname( pls );
775 if ( pls->dp )
776 {
777 Tcl_SetVar( dev->interp, "dp", "1", TCL_GLOBAL_ONLY );
778 dev->updatecmd = "dp_update";
779 }
780 else
781 {
782 Tcl_SetVar( dev->interp, "dp", "0", TCL_GLOBAL_ONLY );
783
784 // tk_init needs this. Use pls->FileName first, then DISPLAY, then :0.0
785
786 if ( pls->FileName != NULL )
787 Tcl_SetVar2( dev->interp, "env", "DISPLAY", pls->FileName, TCL_GLOBAL_ONLY );
788 else if ( getenv( "DISPLAY" ) != NULL )
789 Tcl_SetVar2( dev->interp, "env", "DISPLAY", getenv( "DISPLAY" ), TCL_GLOBAL_ONLY ); // tk_init need this
790 else
791 Tcl_SetVar2( dev->interp, "env", "DISPLAY", "unix:0.0", TCL_GLOBAL_ONLY ); // tk_init need this
792
793 dev->updatecmd = "update";
794 if ( pltk_toplevel( &dev->w, dev->interp ) )
795 abort_session( pls, "Unable to create top-level window" );
796 }
797
798 // Eval startup procs
799
800 if ( pltkdriver_Init( pls ) != TCL_OK )
801 {
802 abort_session( pls, "" );
803 }
804
805 if ( pls->debug )
806 tcl_cmd( pls, "global auto_path; puts \"auto_path: $auto_path\"" );
807
808 // Other initializations.
809 // Autoloaded, so the user can customize it if desired
810
811 tcl_cmd( pls, "plclient_init" );
812
813 // A different way to customize the interface.
814 // E.g. used by plrender to add a back page button.
815
816 if ( drvoptcmd )
817 tcl_cmd( pls, drvoptcmd );
818
819 // Initialize server process
820
821 init_server( pls );
822
823 // By now we should be done with all autoloaded procs, so blow away
824 // the open command just in case security has been compromised
825
826 tcl_cmd( pls, "rename open {}" );
827 tcl_cmd( pls, "rename rename {}" );
828
829 // Initialize widgets
830
831 plwindow_init( pls );
832
833 // Initialize data link
834
835 link_init( pls );
836
837 return;
838 }
839
840 //--------------------------------------------------------------------------
841 // tk_stop
842 //
843 // Normal termination & cleanup.
844 //--------------------------------------------------------------------------
845
846 static void
847 tk_stop( PLStream *pls )
848 {
849 TkDev *dev = (TkDev *) pls->dev;
850
851 dbug_enter( "tk_stop" );
852
853 // Safety check for out of control code
854
855 if ( dev->pass_thru )
856 return;
857
858 dev->pass_thru = 1;
859
860 // Kill plserver
861
862 tcl_cmd( pls, "plclient_link_end" );
863
864 // Wait for child process to complete
865
866 if ( dev->child_pid )
867 {
868 waitpid( dev->child_pid, NULL, 0 );
869 //
870 // problems if parent has not caught/ignore SIGCHLD. Returns -1 and errno=EINTR
871 // if (waitpid(dev->child_pid, NULL, 0) != dev->child_pid)
872 // fprintf(stderr, "tk_stop: waidpid error");
873 //
874 }
875
876 // Blow away interpreter
877
878 Tcl_DeleteInterp( dev->interp );
879 dev->interp = NULL;
880
881 // Free up memory and other miscellanea
882
883 pdf_close( pls->pdfs );
884 if ( dev->iodev != NULL )
885 {
886 if ( dev->iodev->file != NULL )
887 plCloseFile( pls );
888
889 free( (void *) dev->iodev );
890 }
891 free_mem( dev->cmdbuf );
892 }
893
894 //--------------------------------------------------------------------------
895 // abort_session
896 //
897 // Terminates with an error.
898 // Cleanup is done here, and once pls->level is cleared the driver will
899 // never be called again.
900 //--------------------------------------------------------------------------
901
902 static void
903 abort_session( PLStream *pls, const char *msg )
904 {
905 TkDev *dev = (TkDev *) pls->dev;
906
907 dbug_enter( "abort_session" );
908
909 // Safety check for out of control code
910
911 if ( dev->pass_thru )
912 return;
913
914 tk_stop( pls );
915 pls->level = 0;
916
917 plexit( msg );
918 }
919
920 //--------------------------------------------------------------------------
921 // pltkdriver_Init
922 //
923 // Performs PLplot/TK driver-specific Tcl initialization.
924 //--------------------------------------------------------------------------
925
926 static int
927 pltkdriver_Init( PLStream *pls )
928 {
929 TkDev *dev = (TkDev *) pls->dev;
930 Tcl_Interp *interp = (Tcl_Interp *) dev->interp;
931
932 //
933 // Call the init procedures for included packages. Each call should
934 // look like this:
935 //
936 // if (Mod_Init(interp) == TCL_ERROR) {
937 // return TCL_ERROR;
938 // }
939 //
940 // where "Mod" is the name of the module.
941 //
942
943 if ( Tcl_Init( interp ) == TCL_ERROR )
944 {
945 return TCL_ERROR;
946 }
947 #ifdef PLD_dp
948 if ( pls->dp )
949 {
950 if ( Tdp_Init( interp ) == TCL_ERROR )
951 {
952 return TCL_ERROR;
953 }
954 }
955 #endif
956
957 //
958 // Call Tcl_CreateCommand for application-specific commands, if
959 // they weren't already created by the init procedures called above.
960 //
961
962 Tcl_CreateCommand( interp, "wait_until", (Tcl_CmdProc *) plWait_Until,
963 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL );
964
965 #ifdef PLD_dp
966 if ( pls->dp )
967 {
968 Tcl_CreateCommand( interp, "host_id", (Tcl_CmdProc *) plHost_ID,
969 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL );
970 }
971 #endif
972
973 Tcl_CreateCommand( interp, "abort", (Tcl_CmdProc *) Abort,
974 (ClientData) pls, (Tcl_CmdDeleteProc *) NULL );
975
976 Tcl_CreateCommand( interp, "plfinfo", (Tcl_CmdProc *) Plfinfo,
977 (ClientData) pls, (Tcl_CmdDeleteProc *) NULL );
978
979 Tcl_CreateCommand( interp, "keypress", (Tcl_CmdProc *) KeyEH,
980 (ClientData) pls, (Tcl_CmdDeleteProc *) NULL );
981
982 Tcl_CreateCommand( interp, "buttonpress", (Tcl_CmdProc *) ButtonEH,
983 (ClientData) pls, (Tcl_CmdDeleteProc *) NULL );
984
985 // Set some relevant interpreter variables
986
987 if ( !pls->dp )
988 tcl_cmd( pls, "set client_name [winfo name .]" );
989
990 if ( pls->server_name != NULL )
991 Tcl_SetVar( interp, "server_name", pls->server_name, 0 );
992
993 if ( pls->server_host != NULL )
994 Tcl_SetVar( interp, "server_host", pls->server_host, 0 );
995
996 if ( pls->server_port != NULL )
997 Tcl_SetVar( interp, "server_port", pls->server_port, 0 );
998
999 // Set up auto_path
1000
1001 if ( pls_auto_path( interp ) == TCL_ERROR )
1002 return TCL_ERROR;
1003
1004 return TCL_OK;
1005 }
1006
1007 //--------------------------------------------------------------------------
1008 // init_server
1009 //
1010 // Starts interaction with server process, launching it if necessary.
1011 //
1012 // There are several possibilities we must account for, depending on the
1013 // message protocol, input flags, and whether plserver is already running
1014 // or not. From the point of view of the code, they are:
1015 //
1016 // 1. Driver: tk
1017 // Flags: <none>
1018 // Meaning: need to start up plserver (same host)
1019 // Actions: fork plserver, passing it our TK main window name
1020 // for communication. Once started, plserver will send
1021 // back its main window name.
1022 //
1023 // 2. Driver: dp
1024 // Flags: <none>
1025 // Meaning: need to start up plserver (same host)
1026 // Actions: fork plserver, passing it our Tcl-DP communication port
1027 // for communication. Once started, plserver will send
1028 // back its created message port number.
1029 //
1030 // 3. Driver: tk
1031 // Flags: -server_name
1032 // Meaning: plserver already running (same host)
1033 // Actions: communicate to plserver our TK main window name.
1034 //
1035 // 4. Driver: dp
1036 // Flags: -server_port
1037 // Meaning: plserver already running (same host)
1038 // Actions: communicate to plserver our Tcl-DP port number.
1039 //
1040 // 5. Driver: dp
1041 // Flags: -server_host
1042 // Meaning: need to start up plserver (remote host)
1043 // Actions: rsh (remsh) plserver, passing it our host ID and Tcl-DP
1044 // port for communication. Once started, plserver will send
1045 // back its created message port number.
1046 //
1047 // 6. Driver: dp
1048 // Flags: -server_host -server_port
1049 // Meaning: plserver already running (remote host)
1050 // Actions: communicate to remote plserver our host ID and Tcl-DP
1051 // port number.
1052 //
1053 // For a bit more flexibility, you can change the name of the process
1054 // invoked from "plserver" to something else, using the -plserver flag.
1055 //
1056 // The startup procedure involves some rather involved handshaking between
1057 // client and server. This is made easier by using the Tcl variables:
1058 //
1059 // client_host client_port server_host server_port
1060 //
1061 // when using Tcl-DP sends and
1062 //
1063 // client_name server_name
1064 //
1065 // when using TK sends. The global Tcl variables
1066 //
1067 // client server
1068 //
1069 // are used as the defining identification for the client and server
1070 // respectively -- they denote the main window name when TK sends are used
1071 // and the respective process's listening socket when Tcl-DP sends are
1072 // used. Note that in the former case, $client is just the same as
1073 // $client_name. In addition, since the server may need to communicate
1074 // with many different client processes, every command to the server
1075 // contains the sender's client id (so it knows how to report back if
1076 // necessary). Thus the Tk driver's interpreter must know both $server as
1077 // well as $client. It is most convenient to set $client from the server,
1078 // as a way to signal that communication has been set up and it is safe to
1079 // proceed.
1080 //
1081 // Often it is necessary to use constructs such as [list $server] instead
1082 // of just $server. This occurs since you could have multiple copies
1083 // running on the display (resulting in names of the form "plserver #2",
1084 // etc). Embedding such a string in a "[list ...]" construct prevents the
1085 // string from being interpreted as two separate strings.
1086 //--------------------------------------------------------------------------
1087
1088 static void
1089 init_server( PLStream *pls )
1090 {
1091 int server_exists = 0;
1092
1093 dbug_enter( "init_server" );
1094
1095 pldebug( "init_server", "%s -- PID: %d, PGID: %d, PPID: %d\n",
1096 __FILE__, (int) getpid(), (int) getpgrp(), (int) getppid() );
1097
1098 // If no means of communication provided, need to launch plserver
1099
1100 if ( ( !pls->dp && pls->server_name != NULL ) ||
1101 ( pls->dp && pls->server_port != NULL ) )
1102 server_exists = 1;
1103
1104 // So launch it
1105
1106 if ( !server_exists )
1107 launch_server( pls );
1108
1109 // Set up communication channel to server
1110
1111 if ( pls->dp )
1112 {
1113 tcl_cmd( pls,
1114 "set server [dp_MakeRPCClient $server_host $server_port]" );
1115 }
1116 else
1117 {
1118 tcl_cmd( pls, "set server $server_name" );
1119 }
1120
1121 // If server didn't need launching, contact it here
1122
1123 if ( server_exists )
1124 tcl_cmd( pls, "plclient_link_init" );
1125 }
1126
1127 //--------------------------------------------------------------------------
1128 // launch_server
1129 //
1130 // Launches plserver, locally or remotely.
1131 //--------------------------------------------------------------------------
1132
1133 static void
1134 launch_server( PLStream *pls )
1135 {
1136 TkDev *dev = (TkDev *) pls->dev;
1137 const char *argv[20];
1138 char *plserver_exec = NULL, *ptr;
1139 char *tmp = NULL;
1140 int i;
1141
1142 dbug_enter( "launch_server" );
1143
1144 if ( pls->plserver == NULL )
1145 pls->plserver = plstrdup( NAME_plserver );
1146
1147 // Build argument list
1148
1149 i = 0;
1150
1151 // If we're doing a rsh, need to set up its arguments first.
1152
1153 if ( pls->dp && pls->server_host != NULL )
1154 {
1155 argv[i++] = pls->server_host; // Host name for rsh
1156
1157 if ( pls->user != NULL )
1158 {
1159 argv[i++] = "-l";
1160 argv[i++] = pls->user; // User name on remote node
1161 }
1162 }
1163
1164 // The invoked executable name comes next
1165
1166 argv[i++] = pls->plserver;
1167
1168 // The rest are arguments to plserver
1169
1170 argv[i++] = "-child"; // Tell plserver its ancestry
1171
1172 argv[i++] = "-e"; // Startup script
1173 argv[i++] = "plserver_init";
1174
1175 // aaahhh. This is it! Without the next statements, control is either
1176 // in tk or octave, because tcl/tk was in interative mode (I think).
1177 // This had the inconvenient of having to press the enter key or cliking a
1178 // mouse button in the plot window after every plot.
1179 //
1180 // This couldn't be done with
1181 // Tcl_SetVar(dev->interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY);
1182 // after plserver has been launched? It doesnt work, hoewever.
1183 // Tk_CreateFileHandler (0, TK_READABLE, NULL, 0) doesnt work also
1184 //
1185
1186 argv[i++] = "-file"; // Startup file
1187 if ( pls->tk_file )
1188 argv[i++] = pls->tk_file;
1189 else
1190 argv[i++] = "/dev/null";
1191
1192
1193 //
1194 // Give interpreter the base name of the plwindow.
1195 // Useful to know the interpreter name
1196 //
1197
1198 if ( pls->plwindow != NULL )
1199 {
1200 char *t;
1201 argv[i++] = "-name"; // plserver name
1202 tmp = plstrdup( pls->plwindow + 1 ); // get rid of the initial dot
1203 argv[i++] = tmp;
1204 if ( ( t = strchr( tmp, '.' ) ) != NULL )
1205 *t = '\0'; // and keep only the base name
1206 }
1207 else
1208 {
1209 argv[i++] = "-name"; // plserver name
1210 argv[i++] = pls->program;
1211 }
1212
1213 if ( pls->auto_path != NULL )
1214 {
1215 argv[i++] = "-auto_path"; // Additional directory(s)
1216 argv[i++] = pls->auto_path; // to autoload
1217 }
1218
1219 if ( pls->geometry != NULL )
1220 {
1221 argv[i++] = "-geometry"; // Top level window geometry
1222 argv[i++] = pls->geometry;
1223 }
1224
1225 // If communicating via Tcl-DP, specify communications port id
1226 // If communicating via TK send, specify main window name
1227
1228 if ( pls->dp )
1229 {
1230 argv[i++] = "-client_host";
1231 argv[i++] = Tcl_GetVar( dev->interp, "client_host", TCL_GLOBAL_ONLY );
1232
1233 argv[i++] = "-client_port";
1234 argv[i++] = Tcl_GetVar( dev->interp, "client_port", TCL_GLOBAL_ONLY );
1235
1236 if ( pls->user != NULL )
1237 {
1238 argv[i++] = "-l";
1239 argv[i++] = pls->user;
1240 }
1241 }
1242 else
1243 {
1244 argv[i++] = "-client_name";
1245 argv[i++] = Tcl_GetVar( dev->interp, "client_name", TCL_GLOBAL_ONLY );
1246 }
1247
1248 // The display absolutely must be set if invoking a remote server (by rsh)
1249 // Use the DISPLAY environmental, if set. Otherwise use the remote host.
1250
1251 if ( pls->FileName != NULL )
1252 {
1253 argv[i++] = "-display";
1254 argv[i++] = pls->FileName;
1255 }
1256 else if ( pls->dp && pls->server_host != NULL )
1257 {
1258 argv[i++] = "-display";
1259 if ( ( ptr = getenv( "DISPLAY" ) ) != NULL )
1260 argv[i++] = ptr;
1261 else
1262 argv[i++] = "unix:0.0";
1263 }
1264
1265 // Add terminating null
1266
1267 argv[i++] = NULL;
1268 #ifdef DEBUG
1269 if ( pls->debug )
1270 {
1271 int j;
1272 fprintf( stderr, "argument list: \n " );
1273 for ( j = 0; j < i; j++ )
1274 fprintf( stderr, "%s ", argv[j] );
1275 fprintf( stderr, "\n" );
1276 }
1277 #endif
1278
1279 // Start server process
1280 // It's a fork/rsh if on a remote machine
1281
1282 if ( pls->dp && pls->server_host != NULL )
1283 {
1284 if ( ( dev->child_pid = fork() ) < 0 )
1285 {
1286 abort_session( pls, "Unable to fork server process" );
1287 }
1288 else if ( dev->child_pid == 0 )
1289 {
1290 fprintf( stderr, "Starting up %s on node %s\n", pls->plserver,
1291 pls->server_host );
1292
1293 if ( execvp( "rsh", (char * const *) argv ) )
1294 {
1295 perror( "Unable to exec server process" );
1296 _exit( 1 );
1297 }
1298 }
1299 }
1300
1301 // Running locally, so its a fork/exec
1302
1303 else
1304 {
1305 plserver_exec = plFindCommand( pls->plserver );
1306 if ( ( plserver_exec == NULL ) || ( dev->child_pid = fork() ) < 0 )
1307 {
1308 abort_session( pls, "Unable to fork server process" );
1309 }
1310 else if ( dev->child_pid == 0 )
1311 {
1312 // Don't kill plserver on a ^C if pls->server_nokill is set
1313
1314 if ( pls->server_nokill )
1315 {
1316 sigset_t set;
1317 sigemptyset( &set );
1318 sigaddset( &set, SIGINT );
1319 if ( sigprocmask( SIG_BLOCK, &set, 0 ) < 0 )
1320 fprintf( stderr, "PLplot: sigprocmask failure\n" );
1321 }
1322
1323 pldebug( "launch_server", "Starting up %s\n", plserver_exec );
1324 if ( execv( plserver_exec, (char * const *) argv ) )
1325 {
1326 fprintf( stderr, "Unable to exec server process.\n" );
1327 _exit( 1 );
1328 }
1329 }
1330 free_mem( plserver_exec );
1331 }
1332 free_mem( tmp );
1333
1334 // Wait for server to set up return communication channel
1335
1336 tk_wait( pls, "[info exists client]" );
1337 }
1338
1339 //--------------------------------------------------------------------------
1340 // plwindow_init
1341 //
1342 // Configures the widget hierarchy we are sending the data stream to.
1343 //
1344 // If a widget name (identifying the actual widget or a container widget)
1345 // hasn't been supplied already we assume it needs to be created.
1346 //
1347 // In order to achieve maximum flexibility, the PLplot tk driver requires
1348 // only that certain TCL procs must be defined in the server interpreter.
1349 // These can be used to set up the desired widget configuration. The procs
1350 // invoked from this driver currently include:
1351 //
1352 // $plw_create_proc Creates the widget environment
1353 // $plw_start_proc Does any remaining startup necessary
1354 // $plw_end_proc Prepares for shutdown
1355 // $plw_flash_proc Invoked when waiting for page advance
1356 //
1357 // Since all of these are interpreter variables, they can be trivially
1358 // changed by the user.
1359 //
1360 // Each of these utility procs is called with a widget name ($plwindow)
1361 // as argument. "plwindow" is set from the value of pls->plwindow, and
1362 // if null is generated from the name of the client main window (to
1363 // ensure uniqueness). $plwindow usually indicates the container frame
1364 // for the actual PLplot widget, but can be arbitrary -- as long as the
1365 // usage in all the TCL procs is consistent.
1366 //
1367 // In order that the TK driver be able to invoke the actual PLplot
1368 // widget, the proc "$plw_create_proc" deposits the widget name in the local
1369 // interpreter variable "plwidget".
1370 //--------------------------------------------------------------------------
1371
1372 static void
1373 plwindow_init( PLStream *pls )
1374 {
1375 TkDev *dev = (TkDev *) pls->dev;
1376 char command[CMD_LEN];
1377 unsigned int bg;
1378 char *tmp;
1379 int i, n;
1380
1381 dbug_enter( "plwindow_init" );
1382
1383 // Set tcl plwindow variable to be pls->plwindow with a . prepended and
1384 // and with ' ' replaced by '_' and all other '.' by '_' to avoid
1385 // quoting and bad window name problems. Also avoid name starting with
1386 // an upper case letter.
1387 n = (int) strlen( pls->plwindow ) + 1;
1388 tmp = (char *) malloc( sizeof ( char ) * (size_t) ( n + 1 ) );
1389 sprintf( tmp, ".%s", pls->plwindow );
1390 for ( i = 1; i < n; i++ )
1391 {
1392 if ( ( tmp[i] == ' ' ) || ( tmp[i] == '.' ) )
1393 tmp[i] = '_';
1394 }
1395 if ( isupper( tmp[1] ) )
1396 tmp[1] = tolower( tmp[1] );
1397 Tcl_SetVar( dev->interp, "plwindow", tmp, 0 );
1398 free( tmp );
1399
1400 // Create the plframe widget & anything else you want with it.
1401
1402 server_cmd( pls,
1403 "$plw_create_proc $plwindow [list $client]", 1 );
1404
1405 tk_wait( pls, "[info exists plwidget]" );
1406
1407 // Now we should have the actual PLplot widget name in $plwidget
1408 // Configure remote PLplot stream.
1409
1410 // Configure background color if anything other than black
1411 // The default color is handled from a resource setting in plconfig.tcl
1412
1413 bg = (unsigned int) ( pls->cmap0[0].b | ( pls->cmap0[0].g << 8 ) | ( pls->cmap0[0].r << 16 ) );
1414 if ( bg > 0 )
1415 {
1416 snprintf( command, CMD_LEN, "$plwidget configure -plbg #%06x", bg );
1417 server_cmd( pls, command, 0 );
1418 }
1419
1420 // nopixmap option
1421
1422 if ( pls->nopixmap )
1423 server_cmd( pls, "$plwidget cmd plsetopt -nopixmap", 0 );
1424
1425 // debugging
1426
1427 if ( pls->debug )
1428 server_cmd( pls, "$plwidget cmd plsetopt -debug", 0 );
1429
1430 // double buffering
1431
1432 if ( pls->db )
1433 server_cmd( pls, "$plwidget cmd plsetopt -db", 0 );
1434
1435 // color map options
1436
1437 if ( pls->ncol0 )
1438 {
1439 snprintf( command, CMD_LEN, "$plwidget cmd plsetopt -ncol0 %d", pls->ncol0 );
1440 server_cmd( pls, command, 0 );
1441 }
1442
1443 if ( pls->ncol1 )
1444 {
1445 snprintf( command, CMD_LEN, "$plwidget cmd plsetopt -ncol1 %d", pls->ncol1 );
1446 server_cmd( pls, command, 0 );
1447 }
1448
1449 // Start up remote PLplot
1450
1451 server_cmd( pls, "$plw_start_proc $plwindow", 1 );
1452 tk_wait( pls, "[info exists widget_is_ready]" );
1453 }
1454
1455 //--------------------------------------------------------------------------
1456 // set_windowname
1457 //
1458 // Set up top level window name. Use pls->program, modified appropriately.
1459 //--------------------------------------------------------------------------
1460
1461 static void
1462 set_windowname( PLStream *pls )
1463 {
1464 const char *pname;
1465 int i;
1466 size_t maxlen;
1467
1468 // Set to "plclient" if not initialized via plargs or otherwise
1469
1470 if ( pls->program == NULL )
1471 pls->program = plstrdup( "plclient" );
1472
1473 // Eliminate any leading path specification
1474
1475 pname = strrchr( pls->program, '/' );
1476 if ( pname )
1477 pname++;
1478 else
1479 pname = pls->program;
1480
1481 if ( pls->plwindow == NULL ) // dont override -plwindow cmd line option
1482 {
1483 maxlen = strlen( pname ) + 10;
1484 pls->plwindow = (char *) malloc( maxlen * sizeof ( char ) );
1485
1486 // Allow for multiple widgets created by multiple streams
1487
1488 if ( pls->ipls == 0 )
1489 snprintf( pls->plwindow, maxlen, ".%s", pname );
1490 else
1491 snprintf( pls->plwindow, maxlen, ".%s_%d", pname, (int) pls->ipls );
1492
1493 // Replace any ' 's with '_'s to avoid quoting problems.
1494 // Replace any '.'s (except leading) with '_'s to avoid bad window names.
1495
1496 for ( i = 0; i < (int) strlen( pls->plwindow ); i++ )
1497 {
1498 if ( pls->plwindow[i] == ' ' )
1499 pls->plwindow[i] = '_';
1500 if ( i == 0 )
1501 continue;
1502 if ( pls->plwindow[i] == '.' )
1503 pls->plwindow[i] = '_';
1504 }
1505 }
1506 }
1507
1508 //--------------------------------------------------------------------------
1509 // link_init
1510 //
1511 // Initializes the link between the client and the PLplot widget for
1512 // data transfer. Defaults to a FIFO when the TK driver is selected and
1513 // a socket when the DP driver is selected.
1514 //--------------------------------------------------------------------------
1515
1516 static void
1517 link_init( PLStream *pls )
1518 {
1519 TkDev *dev = (TkDev *) pls->dev;
1520 PLiodev *iodev = (PLiodev *) dev->iodev;
1521 size_t bufmax = (size_t) ( pls->bufmax * 1.2 );
1522 const char *dirname = NULL;
1523
1524 dbug_enter( "link_init" );
1525
1526 // Create FIFO for data transfer to the plframe widget
1527
1528 if ( !pls->dp )
1529 {
1530 // This uses the pl_create_tempfifo function to create
1531 // the fifo in a safe manner by first creating a private
1532 // temporary directory.
1533 iodev->fileName = pl_create_tempfifo( (const char **) &iodev->fileName, &dirname );
1534 if ( dirname == NULL || iodev->fileName == NULL )
1535 abort_session( pls, "mkfifo error" );
1536
1537 // Tell plframe widget to open FIFO (for reading).
1538
1539 Tcl_SetVar( dev->interp, "fifoname", iodev->fileName, 0 );
1540 server_cmd( pls, "$plwidget openlink fifo $fifoname", 1 );
1541
1542 // Open the FIFO for writing
1543 // This will block until the server opens it for reading
1544
1545 if ( ( iodev->fd = open( iodev->fileName, O_WRONLY ) ) == -1 )
1546 abort_session( pls, "Error opening fifo for write" );
1547
1548 // Create stream interface (C file handle) to FIFO
1549
1550 iodev->type = 0;
1551 iodev->typeName = "fifo";
1552 iodev->file = fdopen( iodev->fd, "wb" );
1553
1554 // Unlink FIFO so that it isn't left around if program crashes.
1555 // This also ensures no other program can mess with it.
1556
1557 if ( unlink( iodev->fileName ) == -1 )
1558 abort_session( pls, "Error removing fifo" );
1559 free( (void *) iodev->fileName );
1560 iodev->fileName = NULL;
1561 if ( rmdir( dirname ) == -1 )
1562 abort_session( pls, "Error removing temporary directory" );
1563 free( (void *) dirname );
1564 }
1565
1566 // Create socket for data transfer to the plframe widget
1567
1568 else
1569 {
1570 iodev->type = 1;
1571 iodev->typeName = "socket";
1572 tcl_cmd( pls, "plclient_dp_init" );
1573 iodev->fileHandle = Tcl_GetVar( dev->interp, "data_sock", 0 );
1574
1575 if ( Tcl_GetOpenFile( dev->interp, iodev->fileHandle,
1576 0, 1, ( ClientData ) & iodev->file ) != TCL_OK )
1577 {
1578 fprintf( stderr, "Cannot get file info:\n\t %s\n",
1579 Tcl_GetStringResult( dev->interp ) );
1580 abort_session( pls, "" );
1581 }
1582 iodev->fd = fileno( iodev->file );
1583 }
1584
1585 // Create data buffer
1586
1587 pls->pdfs = pdf_bopen( NULL, (size_t) bufmax );
1588 }
1589
1590 //--------------------------------------------------------------------------
1591 // WaitForPage()
1592 //
1593 // Waits for a page advance.
1594 //--------------------------------------------------------------------------
1595
1596 static void
1597 WaitForPage( PLStream *pls )
1598 {
1599 TkDev *dev = (TkDev *) pls->dev;
1600
1601 dbug_enter( "WaitForPage" );
1602
1603 while ( !dev->exit_eventloop )
1604 {
1605 Tk_DoOneEvent( 0 );
1606 }
1607 dev->exit_eventloop = 0;
1608 }
1609
1610 //--------------------------------------------------------------------------
1611 // CheckForEvents()
1612 //
1613 // A front-end to HandleEvents(), which is only called if certain conditions
1614 // are satisfied:
1615 //
1616 // - only check for events and process them every dev->max_instr times this
1617 // function is called (good for performance since performing an update is
1618 // a nontrivial performance hit).
1619 //--------------------------------------------------------------------------
1620
1621 static void
1622 CheckForEvents( PLStream *pls )
1623 {
1624 TkDev *dev = (TkDev *) pls->dev;
1625
1626 if ( ++dev->instr % dev->max_instr == 0 )
1627 {
1628 dev->instr = 0;
1629 HandleEvents( pls );
1630 }
1631 }
1632
1633 //--------------------------------------------------------------------------
1634 // HandleEvents()
1635 //
1636 // Just a front-end to the update command, for use when not actually waiting
1637 // for an event but only checking the event queue.
1638 //--------------------------------------------------------------------------
1639
1640 static void
1641 HandleEvents( PLStream *pls )
1642 {
1643 TkDev *dev = (TkDev *) pls->dev;
1644
1645 dbug_enter( "HandleEvents" );
1646
1647 Tcl_VarEval( dev->interp, dev->updatecmd, (char **) NULL );
1648 }
1649
1650 //--------------------------------------------------------------------------
1651 // flush_output()
1652 //
1653 // Sends graphics instructions to the {FIFO|socket} via a packet send.
1654 //
1655 // The packet i/o routines are modified versions of the ones from the
1656 // Tcl-DP package. They have been altered to take a pointer to a PDFstrm
1657 // struct, and read-to or write-from pdfs->buffer. The length of the
1658 // buffer is stored in pdfs->bp (the original Tcl-DP routine assumes the
1659 // message is character data and uses strlen). Also, they can
1660 // send/receive from either a fifo or a socket.
1661 //--------------------------------------------------------------------------
1662
1663 static void
1664 flush_output( PLStream *pls )
1665 {
1666 TkDev *dev = (TkDev *) pls->dev;
1667 PDFstrm *pdfs = (PDFstrm *) pls->pdfs;
1668
1669 dbug_enter( "flush_output" );
1670
1671 HandleEvents( pls );
1672
1673 // Send packet -- plserver filehandler will be invoked automatically.
1674
1675 if ( pdfs->bp > 0 )
1676 {
1677 #ifdef DEBUG_ENTER
1678 pldebug( "flush_output", "%s: Flushing buffer, bytes = %ld\n",
1679 __FILE__, pdfs->bp );
1680 #endif
1681 if ( pl_PacketSend( dev->interp, dev->iodev, pls->pdfs ) )
1682 {
1683 fprintf( stderr, "Packet send failed:\n\t %s\n",
1684 Tcl_GetStringResult( dev->interp ) );
1685 abort_session( pls, "" );
1686 }
1687 pdfs->bp = 0;
1688 }
1689 }
1690
1691 //--------------------------------------------------------------------------
1692 // Abort
1693 //
1694 // Just a TCL front-end to abort_session().
1695 //--------------------------------------------------------------------------
1696
1697 static int
1698 Abort( ClientData clientData, Tcl_Interp *PL_UNUSED( interp ), int PL_UNUSED( argc ), char **PL_UNUSED( argv ) )
1699 {
1700 PLStream *pls = (PLStream *) clientData;
1701
1702 dbug_enter( "Abort" );
1703
1704 abort_session( pls, "" );
1705 return TCL_OK;
1706 }
1707
1708 //--------------------------------------------------------------------------
1709 // Plfinfo
1710 //
1711 // Sends info about the server plframe. Usually issued after some
1712 // modification to the plframe is made by the user, such as a resize.
1713 //--------------------------------------------------------------------------
1714
1715 static int
1716 Plfinfo( ClientData clientData, Tcl_Interp *interp, int argc, char **argv )
1717 {
1718 PLStream *pls = (PLStream *) clientData;
1719 TkDev *dev = (TkDev *) pls->dev;
1720 int result = TCL_OK;
1721
1722 dbug_enter( "Plfinfo" );
1723
1724 if ( argc < 3 )
1725 {
1726 Tcl_AppendResult( interp, "wrong # args: should be \"",
1727 " plfinfo wx wy\"", (char *) NULL );
1728 result = TCL_ERROR;
1729 }
1730 else
1731 {
1732 dev->width = (unsigned int) atoi( argv[1] );
1733 dev->height = (unsigned int) atoi( argv[2] );
1734 #if PHYSICAL
1735 {
1736 PLFLT pxlx = (double) PIXELS_X / dev->width * DPMM;
1737 PLFLT pxly = (double) PIXELS_Y / dev->height * DPMM;
1738 plP_setpxl( pxlx, pxly );
1739 }
1740 #endif
1741 }
1742
1743 return result;
1744 }
1745
1746 //--------------------------------------------------------------------------
1747 // KeyEH()
1748 //
1749 // This TCL command handles keyboard events.
1750 //--------------------------------------------------------------------------
1751
1752 static int
1753 KeyEH( ClientData clientData, Tcl_Interp *interp, int argc, char **argv )
1754 {
1755 PLStream *pls = (PLStream *) clientData;
1756 TkDev *dev = (TkDev *) pls->dev;
1757 int result;
1758
1759 dbug_enter( "KeyEH" );
1760
1761 if ( ( result = LookupTkKeyEvent( pls, interp, argc, argv ) ) != TCL_OK )
1762 return result;
1763
1764 if ( dev->locate_mode )
1765 LocateKey( pls );
1766 else
1767 ProcessKey( pls );
1768
1769 return result;
1770 }
1771
1772 //--------------------------------------------------------------------------
1773 // ButtonEH()
1774 //
1775 // This TCL command handles button events.
1776 //--------------------------------------------------------------------------
1777
1778 static int
1779 ButtonEH( ClientData clientData, Tcl_Interp *interp, int argc, char **argv )
1780 {
1781 PLStream *pls = (PLStream *) clientData;
1782 TkDev *dev = (TkDev *) pls->dev;
1783 int result;
1784
1785 dbug_enter( "ButtonEH" );
1786
1787 if ( ( result = LookupTkButtonEvent( pls, interp, argc, argv ) ) != TCL_OK )
1788 return result;
1789
1790 if ( dev->locate_mode )
1791 LocateButton( pls );
1792 else
1793 ProcessButton( pls );
1794
1795 return result;
1796 }
1797
1798 //--------------------------------------------------------------------------
1799 // LookupTkKeyEvent()
1800 //
1801 // Fills in the PLGraphicsIn from a Tk KeyEvent.
1802 //
1803 // Contents of argv array:
1804 // command name
1805 // keysym value
1806 // keysym state
1807 // absolute x coordinate of cursor
1808 // absolute y coordinate of cursor
1809 // relative x coordinate (normalized to [0.0 1.0])
1810 // relative y coordinate (normalized to [0.0 1.0])
1811 // keysym name
1812 // ASCII equivalent (optional)
1813 //
1814 // Note that the keysym name is only used for debugging, and the string is
1815 // not always passed (i.e. the character may not have an ASCII
1816 // representation).
1817 //--------------------------------------------------------------------------
1818
1819 static int
1820 LookupTkKeyEvent( PLStream *pls, Tcl_Interp *interp, int argc, char **argv )
1821 {
1822 TkDev *dev = (TkDev *) pls->dev;
1823 PLGraphicsIn *gin = &( dev->gin );
1824 char *keyname;
1825
1826 dbug_enter( "LookupTkKeyEvent" );
1827
1828 if ( argc < 8 )
1829 {
1830 Tcl_AppendResult( interp, "wrong # args: should be \"",
1831 argv[0], " key-value state pX pY dX dY key-name ?ascii-value?\"",
1832 (char *) NULL );
1833 return TCL_ERROR;
1834 }
1835
1836 gin->keysym = (unsigned int) atol( argv[1] );
1837 gin->state = (unsigned int) atol( argv[2] );
1838 gin->pX = atoi( argv[3] );
1839 gin->pY = atoi( argv[4] );
1840 gin->dX = atof( argv[5] );
1841 gin->dY = atof( argv[6] );
1842
1843 keyname = argv[7];
1844
1845 gin->string[0] = '\0';
1846 if ( argc > 8 )
1847 {
1848 gin->string[0] = argv[8][0];
1849 gin->string[1] = '\0';
1850 }
1851
1852 // Fix up keysym value -- see notes in xwin.c about key representation
1853
1854 switch ( gin->keysym )
1855 {
1856 case XK_BackSpace:
1857 case XK_Tab:
1858 case XK_Linefeed:
1859 case XK_Return:
1860 case XK_Escape:
1861 case XK_Delete:
1862 gin->keysym &= 0xFF;
1863 break;
1864 }
1865
1866 pldebug( "LookupTkKeyEvent",
1867 "KeyEH: stream: %d, Keyname %s, hex %x, ASCII: %s\n",
1868 (int) pls->ipls, keyname, (unsigned int) gin->keysym, gin->string );
1869
1870 return TCL_OK;
1871 }
1872
1873 //--------------------------------------------------------------------------
1874 // LookupTkButtonEvent()
1875 //
1876 // Fills in the PLGraphicsIn from a Tk ButtonEvent.
1877 //
1878 // Contents of argv array:
1879 // command name
1880 // button number
1881 // state (decimal string)
1882 // absolute x coordinate
1883 // absolute y coordinate
1884 // relative x coordinate (normalized to [0.0 1.0])
1885 // relative y coordinate (normalized to [0.0 1.0])
1886 //--------------------------------------------------------------------------
1887
1888 static int
1889 LookupTkButtonEvent( PLStream *pls, Tcl_Interp *interp, int argc, char **argv )
1890 {
1891 TkDev *dev = (TkDev *) pls->dev;
1892 PLGraphicsIn *gin = &( dev->gin );
1893
1894 dbug_enter( "LookupTkButtonEvent" );
1895
1896 if ( argc != 7 )
1897 {
1898 Tcl_AppendResult( interp, "wrong # args: should be \"",
1899 argv[0], " button-number state pX pY dX dY\"", (char *) NULL );
1900 return TCL_ERROR;
1901 }
1902
1903 gin->button = (unsigned int) atol( argv[1] );
1904 gin->state = (unsigned int) atol( argv[2] );
1905 gin->pX = atoi( argv[3] );
1906 gin->pY = atoi( argv[4] );
1907 gin->dX = atof( argv[5] );
1908 gin->dY = atof( argv[6] );
1909 gin->keysym = 0x20;
1910
1911 pldebug( "LookupTkButtonEvent",
1912 "button %d, state %d, pX: %d, pY: %d, dX: %f, dY: %f\n",
1913 gin->button, gin->state, gin->pX, gin->pY, gin->dX, gin->dY );
1914
1915 return TCL_OK;
1916 }
1917
1918 //--------------------------------------------------------------------------
1919 // ProcessKey()
1920 //
1921 // Process keyboard events other than locate input.
1922 //--------------------------------------------------------------------------
1923
1924 static void
1925 ProcessKey( PLStream *pls )
1926 {
1927 TkDev *dev = (TkDev *) pls->dev;
1928 PLGraphicsIn *gin = &( dev->gin );
1929
1930 dbug_enter( "ProcessKey" );
1931
1932 // Call user keypress event handler. Since this is called first, the user
1933 // can disable all internal event handling by setting key.keysym to 0.
1934 //
1935 if ( pls->KeyEH != NULL )
1936 ( *pls->KeyEH )( gin, pls->KeyEH_data, &dev->exit_eventloop );
1937
1938 // Handle internal events
1939
1940 switch ( gin->keysym )
1941 {
1942 case PLK_Return:
1943 case PLK_Linefeed:
1944 case PLK_Next:
1945 // Advance to next page (i.e. terminate event loop) on a <eol>
1946 // Check for both <CR> and <LF> for portability, also a <Page Down>
1947 dev->exit_eventloop = TRUE;
1948 break;
1949
1950 case 'Q':
1951 // Terminate on a 'Q' (not 'q', since it's too easy to hit by mistake)
1952 tcl_cmd( pls, "abort" );
1953 break;
1954
1955 case 'L':
1956 // Begin locate mode
1957 dev->locate_mode = LOCATE_INVOKED_VIA_DRIVER;
1958 server_cmd( pls, "$plwidget configure -xhairs on", 1 );
1959 break;
1960 }
1961 }
1962
1963 //--------------------------------------------------------------------------
1964 // ProcessButton()
1965 //
1966 // Process ButtonPress events other than locate input.
1967 // On:
1968 // Button1: nothing (except when in locate mode, see ButtonLocate)
1969 // Button2: nothing
1970 // Button3: set page advance flag
1971 //--------------------------------------------------------------------------
1972
1973 static void
1974 ProcessButton( PLStream *pls )
1975 {
1976 TkDev *dev = (TkDev *) pls->dev;
1977 PLGraphicsIn *gin = &( dev->gin );
1978
1979 dbug_enter( "ButtonEH" );
1980
1981 // Call user event handler. Since this is called first, the user can
1982 // disable all PLplot internal event handling by setting gin->button to 0.
1983 //
1984 if ( pls->ButtonEH != NULL )
1985 ( *pls->ButtonEH )( gin, pls->ButtonEH_data, &dev->exit_eventloop );
1986
1987 // Handle internal events
1988
1989 switch ( gin->button )
1990 {
1991 case Button3:
1992 dev->exit_eventloop = TRUE;
1993 break;
1994 }
1995 }
1996
1997 //--------------------------------------------------------------------------
1998 // LocateKey()
1999 //
2000 // Front-end to locate handler for KeyPress events.
2001 // Only provides for:
2002 //
2003 // <Escape> Ends locate mode
2004 //--------------------------------------------------------------------------
2005
2006 static void
2007 LocateKey( PLStream *pls )
2008 {
2009 TkDev *dev = (TkDev *) pls->dev;
2010 PLGraphicsIn *gin = &( dev->gin );
2011
2012 // End locate mode on <Escape>
2013
2014 if ( gin->keysym == PLK_Escape )
2015 {
2016 dev->locate_mode = 0;
2017 server_cmd( pls, "$plwidget configure -xhairs off", 1 );
2018 plGinInit( gin );
2019 }
2020 else
2021 {
2022 Locate( pls );
2023 }
2024 }
2025
2026 //--------------------------------------------------------------------------
2027 // LocateButton()
2028 //
2029 // Front-end to locate handler for ButtonPress events.
2030 // Only passes control to Locate() for Button1 presses.
2031 //--------------------------------------------------------------------------
2032
2033 static void
2034 LocateButton( PLStream *pls )
2035 {
2036 TkDev *dev = (TkDev *) pls->dev;
2037 PLGraphicsIn *gin = &( dev->gin );
2038
2039 switch ( gin->button )
2040 {
2041 case Button1:
2042 Locate( pls );
2043 break;
2044 }
2045 }
2046
2047 //--------------------------------------------------------------------------
2048 // Locate()
2049 //
2050 // Handles locate mode events.
2051 //
2052 // In locate mode: move cursor to desired location and select by pressing a
2053 // key or by clicking on the mouse (if available). Typically the world
2054 // coordinates of the selected point are reported.
2055 //
2056 // There are two ways to enter Locate mode -- via the API, or via a driver
2057 // command. The API entry point is the call plGetCursor(), which initiates
2058 // locate mode and does not return until input has been obtained. The
2059 // driver entry point is by entering a 'L' while the driver is waiting for
2060 // events.
2061 //
2062 // Locate mode input is reported in one of three ways:
2063 // 1. Through a returned PLGraphicsIn structure, when user has specified a
2064 // locate handler via (*pls->LocateEH).
2065 // 2. Through a returned PLGraphicsIn structure, when locate mode is invoked
2066 // by a plGetCursor() call.
2067 // 3. Through writes to stdout, when locate mode is invoked by a driver
2068 // command and the user has not supplied a locate handler.
2069 //
2070 // Hitting <Escape> will at all times end locate mode. Other keys will
2071 // typically be interpreted as locator input. Selecting a point out of
2072 // bounds will end locate mode unless the user overrides with a supplied
2073 // Locate handler.
2074 //--------------------------------------------------------------------------
2075
2076 static void
2077 Locate( PLStream *pls )
2078 {
2079 TkDev *dev = (TkDev *) pls->dev;
2080 PLGraphicsIn *gin = &( dev->gin );
2081
2082 // Call user locate mode handler if provided
2083
2084 if ( pls->LocateEH != NULL )
2085 ( *pls->LocateEH )( gin, pls->LocateEH_data, &dev->locate_mode );
2086
2087 // Use default procedure
2088
2089 else
2090 {
2091 // Try to locate cursor
2092
2093 if ( plTranslateCursor( gin ) )
2094 {
2095 // If invoked by the API, we're done
2096 // Otherwise send report to stdout
2097
2098 if ( dev->locate_mode == LOCATE_INVOKED_VIA_DRIVER )
2099 {
2100 pltext();
2101 if ( gin->keysym < 0xFF && isprint( gin->keysym ) )
2102 printf( "%f %f %c\n", gin->wX, gin->wY, gin->keysym );
2103 else
2104 printf( "%f %f 0x%02x\n", gin->wX, gin->wY, gin->keysym );
2105
2106 plgra();
2107 }
2108 }
2109 else
2110 {
2111 // Selected point is out of bounds, so end locate mode
2112
2113 dev->locate_mode = 0;
2114 server_cmd( pls, "$plwidget configure -xhairs off", 1 );
2115 }
2116 }
2117 }
2118
2119 //--------------------------------------------------------------------------
2120 //
2121 // pltk_toplevel --
2122 //
2123 // Create top level window without mapping it.
2124 //
2125 // Results:
2126 // Returns 1 on error.
2127 //
2128 // Side effects:
2129 // Returns window ID as *w.
2130 //
2131 //--------------------------------------------------------------------------
2132
2133 static int
2134 pltk_toplevel( Tk_Window *PL_UNUSED( w ), Tcl_Interp *interp )
2135 {
2136 static char wcmd[] = "wm withdraw .";
2137
2138 // Create the main window without mapping it
2139
2140 if ( Tk_Init( interp ) )
2141 {
2142 fprintf( stderr, "tk_init:%s\n", Tcl_GetStringResult( interp ) );
2143 return 1;
2144 }
2145
2146 Tcl_VarEval( interp, wcmd, (char *) NULL );
2147
2148 return 0;
2149 }
2150
2151 //--------------------------------------------------------------------------
2152 // tk_wait()
2153 //
2154 // Waits for the specified expression to evaluate to true before
2155 // proceeding. While we are waiting to proceed, all events (for this
2156 // or other interpreters) are handled.
2157 //
2158 // Use a static string buffer to hold the command, to ensure it's in
2159 // writable memory (grrr...).
2160 //--------------------------------------------------------------------------
2161
2162 static void
2163 tk_wait( PLStream *pls, const char *cmd )
2164 {
2165 TkDev *dev = (TkDev *) pls->dev;
2166 int result = 0;
2167
2168 dbug_enter( "tk_wait" );
2169
2170 copybuf( pls, cmd );
2171 for (;; )
2172 {
2173 if ( Tcl_ExprBoolean( dev->interp, dev->cmdbuf, &result ) )
2174 {
2175 fprintf( stderr, "tk_wait command \"%s\" failed:\n\t %s\n",
2176 cmd, Tcl_GetStringResult( dev->interp ) );
2177 break;
2178 }
2179 if ( result )
2180 break;
2181
2182 Tk_DoOneEvent( 0 );
2183 }
2184 }
2185
2186 //--------------------------------------------------------------------------
2187 // server_cmd
2188 //
2189 // Sends specified command to server, aborting on an error.
2190 // If nowait is set, the command is issued in the background.
2191 //
2192 // If commands MUST proceed in a certain order (e.g. initialization), it
2193 // is safest to NOT run them in the background.
2194 //
2195 // In order to protect args that have embedded spaces in them, I enclose
2196 // the entire command in a [list ...], but for TK sends ONLY. If done with
2197 // Tcl-DP RPC, the sent command is no longer recognized. Evidently an
2198 // extra scan of the line is done with TK sends for some reason.
2199 //--------------------------------------------------------------------------
2200
2201 static void
2202 server_cmd( PLStream *pls, const char *cmd, int nowait )
2203 {
2204 TkDev *dev = (TkDev *) pls->dev;
2205 static char dpsend_cmd0[] = "dp_RPC $server ";
2206 static char dpsend_cmd1[] = "dp_RDO $server ";
2207 static char tksend_cmd0[] = "send $server ";
2208 static char tksend_cmd1[] = "send $server after 1 ";
2209 int result;
2210
2211 dbug_enter( "server_cmd" );
2212 pldebug( "server_cmd", "Sending command: %s\n", cmd );
2213
2214 if ( pls->dp )
2215 {
2216 if ( nowait )
2217 result = Tcl_VarEval( dev->interp, dpsend_cmd1, cmd,
2218 (char **) NULL );
2219 else
2220 result = Tcl_VarEval( dev->interp, dpsend_cmd0, cmd,
2221 (char **) NULL );
2222 }
2223 else
2224 {
2225 if ( nowait )
2226 result = Tcl_VarEval( dev->interp, tksend_cmd1, "[list ",
2227 cmd, "]", (char **) NULL );
2228 else
2229 result = Tcl_VarEval( dev->interp, tksend_cmd0, "[list ",
2230 cmd, "]", (char **) NULL );
2231 }
2232
2233 if ( result != TCL_OK )
2234 {
2235 fprintf( stderr, "Server command \"%s\" failed:\n\t %s\n",
2236 cmd, Tcl_GetStringResult( dev->interp ) );
2237 abort_session( pls, "" );
2238 }
2239 }
2240
2241 //--------------------------------------------------------------------------
2242 // tcl_cmd
2243 //
2244 // Evals the specified command, aborting on an error.
2245 //--------------------------------------------------------------------------
2246
2247 static void
2248 tcl_cmd( PLStream *pls, const char *cmd )
2249 {
2250 TkDev *dev = (TkDev *) pls->dev;
2251
2252 dbug_enter( "tcl_cmd" );
2253
2254 pldebug( "tcl_cmd", "Evaluating command: %s\n", cmd );
2255 if ( Tcl_VarEval( dev->interp, cmd, (char **) NULL ) != TCL_OK )
2256 {
2257 fprintf( stderr, "TCL command \"%s\" failed:\n\t %s\n",
2258 cmd, Tcl_GetStringResult( dev->interp ) );
2259 abort_session( pls, "" );
2260 }
2261 }
2262
2263 //--------------------------------------------------------------------------
2264 // copybuf
2265 //
2266 // Puts command in a static string buffer, to ensure it's in writable
2267 // memory (grrr...).
2268 //--------------------------------------------------------------------------
2269
2270 static void
2271 copybuf( PLStream *pls, const char *cmd )
2272 {
2273 TkDev *dev = (TkDev *) pls->dev;
2274
2275 if ( dev->cmdbuf == NULL )
2276 {
2277 dev->cmdbuf_len = 100;
2278 dev->cmdbuf = (char *) malloc( dev->cmdbuf_len );
2279 }
2280
2281 if ( strlen( cmd ) >= dev->cmdbuf_len )
2282 {
2283 free( (void *) dev->cmdbuf );
2284 dev->cmdbuf_len = strlen( cmd ) + 20;
2285 dev->cmdbuf = (char *) malloc( dev->cmdbuf_len );
2286 }
2287
2288 strcpy( dev->cmdbuf, cmd );
2289 }
2290
2291 //--------------------------------------------------------------------------
2292 #else
2293 int
2294 pldummy_tk()
2295 {
2296 return 0;
2297 }
2298
2299 #endif // PLD_tk
plParseDrvOpts
int plParseDrvOpts(DrvOpt *acc_opt)
Definition: plargs.c:1461
PLControlPt::alt_hue_path
int alt_hue_path
Definition: plplot.h:564
PLiodev
Definition: pdf.h:61
PLStream::plwindow
char * plwindow
Definition: plstrm.h:640
PLSTATE_CMAP0
#define PLSTATE_CMAP0
Definition: plplotP.h:366
PLStream::cmap1cp
PLControlPt cmap1cp[PL_MAX_CMAP1CP]
Definition: plstrm.h:547
header
const char header[]
Definition: deltaT-gen.c:41
PLStream::server_port
char * server_port
Definition: plstrm.h:639
argv
static char ** argv
Definition: qt.cpp:49
plplot::maxlen
integer(kind=private_plint), parameter, private maxlen
Definition: plplot.f90:48
pldummy_tk
int pldummy_tk()
Definition: tk.c:2294
PLESC_XORMOD
#define PLESC_XORMOD
Definition: plplot.h:286
TkDev::exit_eventloop
int exit_eventloop
Definition: pltkd.h:36
PLK_Escape
#define PLK_Escape
Definition: plevent.h:51
PLStream::LocateEH
void(* LocateEH)(PLGraphicsIn *gin, void *LocateEH_data, int *locate_mode)
Definition: plstrm.h:606
tcpip.h
TkDev::yold
PLINT yold
Definition: pltkd.h:34
plexit
void plexit(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1958
PLStream::dev_di
PLINT dev_di
Definition: plstrm.h:571
LINETO
#define LINETO
Definition: metadefs.h:62
PLStream::level
PLINT level
Definition: plstrm.h:527
PLStream::icol1
PLINT icol1
Definition: plstrm.h:539
plDevType_Interactive
Definition: disptab.h:14
pdf_bopen
PDFstrm * pdf_bopen(U_CHAR *buffer, size_t bufmax)
Definition: pdfutils.c:152
PLDev::xold
PLINT xold
Definition: plstrm.h:43
plevent.h
PLColor::b
unsigned char b
Definition: plplot.h:550
PLStream::dev_fill1
PLINT dev_fill1
Definition: plstrm.h:571
PLStream::ncp1
PLINT ncp1
Definition: plstrm.h:539
TkDev::max_instr
int max_instr
Definition: pltkd.h:44
PLESC_FILL
#define PLESC_FILL
Definition: plplot.h:279
PL_RGB_COLOR
#define PL_RGB_COLOR
Definition: plplotP.h:285
pdf_wr_header
int pdf_wr_header(PDFstrm *pdfs, PLCHAR_VECTOR header)
Definition: pdfutils.c:509
INITIALIZE
#define INITIALIZE
Definition: metadefs.h:53
PLStream::mar
PLFLT mar
Definition: plstrm.h:659
PLDispatchTable::pl_esc
plD_esc_fp pl_esc
Definition: disptab.h:90
PDFstrm
Definition: pdf.h:49
pltkd.h
pdf_wr_2bytes
int pdf_wr_2bytes(PDFstrm *pdfs, U_SHORT s)
Definition: pdfutils.c:684
plCloseFile
void plCloseFile(PLStream *pls)
Definition: plctrl.c:2635
PLSERV_VERSION
#define PLSERV_VERSION
Definition: metadefs.h:22
pdf_wr_2nbytes
int pdf_wr_2nbytes(PDFstrm *pdfs, U_SHORT *s, PLINT n)
Definition: pdfutils.c:741
plD_tidy_fp
void(* plD_tidy_fp)(struct PLStream_struct *)
Definition: disptab.h:72
argc
static int argc
Definition: qt.cpp:48
PLDispatchTable::pl_seq
int pl_seq
Definition: disptab.h:82
plGinInit
void plGinInit(PLGraphicsIn *gin)
Definition: plctrl.c:2887
PLStream::jy
PLFLT jy
Definition: plstrm.h:659
PLStream::dev_npts
PLINT dev_npts
Definition: plstrm.h:581
PLStream::bufmax
int bufmax
Definition: plstrm.h:643
PLDispatchTable::pl_MenuStr
const char * pl_MenuStr
Definition: disptab.h:79
PLStream::ButtonEH
void(* ButtonEH)(PLGraphicsIn *gin, void *ButtonEH_data, int *exit_eventloop)
Definition: plstrm.h:602
PLStream::color
PLINT color
Definition: plstrm.h:569
pltcl.h
PLDispatchTable::pl_tidy
plD_tidy_fp pl_tidy
Definition: disptab.h:88
PLStream::plbuf_write
PLINT plbuf_write
Definition: plstrm.h:567
plD_init_fp
void(* plD_init_fp)(struct PLStream_struct *)
Definition: disptab.h:67
plTranslateCursor
PLINT plTranslateCursor(PLGraphicsIn *plg)
Definition: plpage.c:259
PLStream::diorot
PLFLT diorot
Definition: plstrm.h:661
pls_auto_path
PLDLLIMPEXP_TCLTK int pls_auto_path(Tcl_Interp *interp)
Definition: tclAPI.c:716
PLStream::jx
PLFLT jx
Definition: plstrm.h:659
PLStream::dipymin
PLFLT dipymin
Definition: plstrm.h:657
plD_dispatch_init_tk
PLDLLIMPEXP_DRIVER void plD_dispatch_init_tk(PLDispatchTable *pdt)
plD_eop_fp
void(* plD_eop_fp)(struct PLStream_struct *)
Definition: disptab.h:70
PLDispatchTable::pl_DevName
const char * pl_DevName
Definition: disptab.h:80
PLSTATE_COLOR0
#define PLSTATE_COLOR0
Definition: plplotP.h:363
PLDispatchTable::pl_init
plD_init_fp pl_init
Definition: disptab.h:83
plabort
void plabort(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1894
PLiodev::file
FILE * file
Definition: pdf.h:64
PLStream::dp
int dp
Definition: plstrm.h:643
PLStream::program
char * program
Definition: plstrm.h:530
PLESC_DI
#define PLESC_DI
Definition: plplot.h:280
PLSTATE_COLOR1
#define PLSTATE_COLOR1
Definition: plplotP.h:364
TkDev::width
unsigned width
Definition: pltkd.h:35
U_SHORT
#define U_SHORT
Definition: pdf.h:30
PLStream::server_host
char * server_host
Definition: plstrm.h:639
PLK_Linefeed
#define PLK_Linefeed
Definition: plevent.h:49
PLStream::cmap1
PLColor * cmap1
Definition: plstrm.h:545
PLStream::dev_x
short * dev_x
Definition: plstrm.h:582
PLDI_DEV
#define PLDI_DEV
Definition: plplotP.h:381
TkDev::gin
PLGraphicsIn gin
Definition: pltkd.h:46
PLStream::user
char * user
Definition: plstrm.h:639
PLSTATE_WIDTH
#define PLSTATE_WIDTH
Definition: plplotP.h:362
PLStream::pdfs
PDFstrm * pdfs
Definition: plstrm.h:579
DRV_STR
Definition: plplotP.h:758
PLINT
int PLINT
Definition: plplot.h:181
PLStream::LocateEH_data
void * LocateEH_data
Definition: plstrm.h:608
PLStream::aspect
PLFLT aspect
Definition: plstrm.h:659
PLSTATE_FILL
#define PLSTATE_FILL
Definition: plplotP.h:365
PLControlPt::c3
PLFLT c3
Definition: plplot.h:561
tcl_cmd
static int tcl_cmd(Tcl_Interp *interp, const char *cmd)
Definition: tclAPI.c:848
PLStream::plserver
char * plserver
Definition: plstrm.h:640
plD_line_fp
void(* plD_line_fp)(struct PLStream_struct *, short, short, short, short)
Definition: disptab.h:68
PLStream::ncol0
PLINT ncol0
Definition: plstrm.h:539
PLSERV_HEADER
#define PLSERV_HEADER
Definition: metadefs.h:21
PLColor::g
unsigned char g
Definition: plplot.h:549
plD_esc_fp
void(* plD_esc_fp)(struct PLStream_struct *, PLINT, void *)
Definition: disptab.h:74
PLStream::db
int db
Definition: plstrm.h:635
TkDev::locate_mode
int locate_mode
Definition: pltkd.h:45
PLStream::ButtonEH_data
void * ButtonEH_data
Definition: plstrm.h:604
plD_polyline_fp
void(* plD_polyline_fp)(struct PLStream_struct *, short *, short *, PLINT)
Definition: disptab.h:69
snprintf
#define snprintf
Definition: plplotP.h:235
PLStream::termin
PLINT termin
Definition: plstrm.h:568
PLSTATE_CMAP1
#define PLSTATE_CMAP1
Definition: plplotP.h:367
PLStream::icol0
PLINT icol0
Definition: plstrm.h:539
pl_create_tempfifo
char * pl_create_tempfifo(const char **p_fifoname, const char **p_dirname)
Definition: plstdio.c:344
PIXELS_X
#define PIXELS_X
Definition: plplotP.h:304
dbug_enter
#define dbug_enter(a)
Definition: tclMatrix.c:59
DPMM
#define DPMM
Definition: plplotP.h:306
PLDI_PLT
#define PLDI_PLT
Definition: plplotP.h:380
TRUE
#define TRUE
Definition: plplotP.h:176
PLStream::dipymax
PLFLT dipymax
Definition: plstrm.h:657
ESCAPE
#define ESCAPE
Definition: metadefs.h:63
PLDI_ORI
#define PLDI_ORI
Definition: plplotP.h:379
plWait_Until
PLDLLIMPEXP_TCLTK int plWait_Until(ClientData, Tcl_Interp *, int, const char **)
PLDispatchTable
Definition: disptab.h:77
PLStream::FileName
char * FileName
Definition: plstrm.h:576
PLStream::ipls
PLINT ipls
Definition: plstrm.h:527
PLDev
Definition: plstrm.h:40
FALSE
#define FALSE
Definition: plplotP.h:177
PLDispatchTable::pl_bop
plD_bop_fp pl_bop
Definition: disptab.h:87
EOP
#define EOP
Definition: metadefs.h:57
PLStream::KeyEH
void(* KeyEH)(PLGraphicsIn *gin, void *KeyEH_data, int *exit_eventloop)
Definition: plstrm.h:598
PLControlPt::c1
PLFLT c1
Definition: plplot.h:559
plFindCommand
char * plFindCommand(PLCHAR_VECTOR fn)
Definition: plctrl.c:2146
PLESC_EH
#define PLESC_EH
Definition: plplot.h:282
LPAGE_Y
#define LPAGE_Y
Definition: plplotP.h:309
DrvOpt
Definition: plplotP.h:748
PLDispatchTable::pl_line
plD_line_fp pl_line
Definition: disptab.h:84
TkDev::child_pid
pid_t child_pid
Definition: pltkd.h:42
TkDev::updatecmd
const char * updatecmd
Definition: pltkd.h:41
PL_UNDEFINED
#define PL_UNDEFINED
Definition: plplotP.h:219
plP_setpxl
void plP_setpxl(PLFLT xpmm, PLFLT ypmm)
Definition: plcore.c:4238
TkDev::pass_thru
int pass_thru
Definition: pltkd.h:37
pltext
#define pltext
Definition: plplot.h:855
PLStream::cmap0
PLColor * cmap0
Definition: plstrm.h:544
drivers.h
PLStream::tk_file
char * tk_file
Definition: plstrm.h:642
PLStream::server_name
char * server_name
Definition: plstrm.h:639
plDevs.h
PLDLLIMPEXP_DRIVER
#define PLDLLIMPEXP_DRIVER
Definition: pldll.h:81
PLStream::dev_xor
PLINT dev_xor
Definition: plstrm.h:572
PLStream::auto_path
char * auto_path
Definition: plstrm.h:641
pls
static PLStream * pls[PL_NSTREAMS]
Definition: plcore.h:88
plP_setphy
void plP_setphy(PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax)
Definition: plcore.c:4249
TkDev::interp
Tcl_Interp * interp
Definition: pltkd.h:33
LPAGE_X
#define LPAGE_X
Definition: plplotP.h:308
LINE
#define LINE
Definition: metadefs.h:61
PLStream::nopixmap
int nopixmap
Definition: plstrm.h:635
PLStream::dev_y
short * dev_y
Definition: plstrm.h:582
PLStream
Definition: plstrm.h:523
pdf_wr_ieeef
int pdf_wr_ieeef(PDFstrm *pdfs, float f)
Definition: pdfutils.c:916
plstrdup
char PLDLLIMPEXP * plstrdup(PLCHAR_VECTOR src)
Definition: plctrl.c:2985
PIXELS_Y
#define PIXELS_Y
Definition: plplotP.h:305
PLStream::KeyEH_data
void * KeyEH_data
Definition: plstrm.h:600
TkDev::cmdbuf_len
size_t cmdbuf_len
Definition: pltkd.h:39
PL_UNUSED
#define PL_UNUSED(x)
Definition: plplot.h:138
PLFLT
float PLFLT
Definition: plplot.h:163
PLStream::dipxmax
PLFLT dipxmax
Definition: plstrm.h:657
pdf_close
int pdf_close(PDFstrm *pdfs)
Definition: pdfutils.c:238
command
static Tcl_DString command
Definition: tkMain.c:121
PLStream::page
PLINT page
Definition: plstrm.h:578
plD_bop_fp
void(* plD_bop_fp)(struct PLStream_struct *)
Definition: disptab.h:71
PLDev::yold
PLINT yold
Definition: plstrm.h:43
free_mem
#define free_mem(a)
Definition: plplotP.h:182
PLK_Return
#define PLK_Return
Definition: plevent.h:50
PLK_Next
#define PLK_Next
Definition: plevent.h:68
PLStream::patt
PLINT patt
Definition: plstrm.h:669
PLStream::difilt
PLINT difilt
Definition: plstrm.h:656
TkDev::iodev
PLiodev * iodev
Definition: pltkd.h:40
plHost_ID
int plHost_ID(ClientData clientData, Tcl_Interp *interp, int argc, const char **argv)
PLColor::r
unsigned char r
Definition: plplot.h:548
PLStream::nopause
PLINT nopause
Definition: plstrm.h:568
PLStream::width
PLFLT width
Definition: plstrm.h:552
PLStream::server_nokill
int server_nokill
Definition: plstrm.h:643
DRV_INT
Definition: plplotP.h:758
CHANGE_STATE
#define CHANGE_STATE
Definition: metadefs.h:68
interp
static Tcl_Interp * interp
Definition: tkMain.c:120
PLStream::curcolor
PLColor curcolor
Definition: plstrm.h:543
PLDispatchTable::pl_state
plD_state_fp pl_state
Definition: disptab.h:89
POLYLINE
#define POLYLINE
Definition: metadefs.h:65
PLDispatchTable::pl_eop
plD_eop_fp pl_eop
Definition: disptab.h:86
PLControlPt::c2
PLFLT c2
Definition: plplot.h:560
TkDev::w
Tk_Window w
Definition: pltkd.h:32
PDFstrm::bp
size_t bp
Definition: pdf.h:56
NAME_plserver
#define NAME_plserver
Definition: plplot_config.h:313
PLStream::debug
PLINT debug
Definition: plstrm.h:527
PLStream::dipxmin
PLFLT dipxmin
Definition: plstrm.h:657
PLStream::ncol1
PLINT ncol1
Definition: plstrm.h:539
pl_PacketSend
PLDLLIMPEXP_TCLTK int pl_PacketSend(Tcl_Interp *interp, PLiodev *iodev, PDFstrm *pdfs)
PLESC_GETC
#define PLESC_GETC
Definition: plplot.h:283
PLStream::dev_flush
PLINT dev_flush
Definition: plstrm.h:571
PLDispatchTable::pl_polyline
plD_polyline_fp pl_polyline
Definition: disptab.h:85
plD_state_fp
void(* plD_state_fp)(struct PLStream_struct *, PLINT)
Definition: disptab.h:73
PLStream::geometry
char * geometry
Definition: plstrm.h:633
PLStream::dev
void * dev
Definition: plstrm.h:594
BOP
#define BOP
Definition: metadefs.h:58
plgra
#define plgra
Definition: plplot.h:740
pdf_wr_1byte
int pdf_wr_1byte(PDFstrm *pdfs, U_CHAR s)
Definition: pdfutils.c:636
PLStream::dev_fill0
PLINT dev_fill0
Definition: plstrm.h:571
PLDispatchTable::pl_type
int pl_type
Definition: disptab.h:81
TkDev::cmdbuf
char * cmdbuf
Definition: pltkd.h:38
TkDev
Definition: pltkd.h:30
TkDev::xold
PLINT xold
Definition: pltkd.h:34
U_CHAR
#define U_CHAR
Definition: pdf.h:26
TkDev::instr
int instr
Definition: pltkd.h:43
metadefs.h
TkDev::height
unsigned height
Definition: pltkd.h:35
PLESC_FLUSH
#define PLESC_FLUSH
Definition: plplot.h:281
PLGraphicsIn
Definition: plplot.h:433