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
64PLDLLIMPEXP_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
94
95void plD_init_tk( PLStream * );
96void plD_line_tk( PLStream *, short, short, short, short );
97void plD_polyline_tk( PLStream *, short *, short *, PLINT );
98void plD_eop_tk( PLStream * );
99void plD_bop_tk( PLStream * );
100void plD_tidy_tk( PLStream * );
101void plD_state_tk( PLStream *, PLINT );
102void plD_esc_tk( PLStream *, PLINT, void * );
103void plD_init_dp( PLStream *pls );
104
105// various
106
107static void init( PLStream *pls );
108static void tk_start( PLStream *pls );
109static void tk_stop( PLStream *pls );
110static void tk_di( PLStream *pls );
111static void tk_fill( PLStream *pls );
112static void WaitForPage( PLStream *pls );
113static void CheckForEvents( PLStream *pls );
114static void HandleEvents( PLStream *pls );
115static void init_server( PLStream *pls );
116static void launch_server( PLStream *pls );
117static void flush_output( PLStream *pls );
118static void plwindow_init( PLStream *pls );
119static void link_init( PLStream *pls );
120static void GetCursor( PLStream *pls, PLGraphicsIn *ptr );
121static void tk_XorMod( PLStream *pls, PLINT *ptr );
122static void set_windowname( PLStream *pls );
123
124// performs Tk-driver-specific initialization
125
126static int pltkdriver_Init( PLStream *pls );
127
128// Tcl/TK utility commands
129
130static void tk_wait( PLStream *pls, const char * );
131static void abort_session( PLStream *pls, const char * );
132static void server_cmd( PLStream *pls, const char *, int );
133static void tcl_cmd( PLStream *pls, const char * );
134static void copybuf( PLStream *pls, const char *cmd );
135static int pltk_toplevel( Tk_Window *w, Tcl_Interp *interp );
136
137static void ProcessKey( PLStream *pls );
138static void ProcessButton( PLStream *pls );
139static void LocateKey( PLStream *pls );
140static void LocateButton( PLStream *pls );
141static void Locate( PLStream *pls );
142
143// These are internal TCL commands
144
145static int Abort( ClientData, Tcl_Interp *, int, char ** );
146static int Plfinfo( ClientData, Tcl_Interp *, int, char ** );
147static int KeyEH( ClientData, Tcl_Interp *, int, char ** );
148static int ButtonEH( ClientData, Tcl_Interp *, int, char ** );
149static int LookupTkKeyEvent( PLStream *pls, Tcl_Interp *interp,
150 int argc, char **argv );
151static int LookupTkButtonEvent( PLStream *pls, Tcl_Interp *interp,
152 int argc, char **argv );
153
154static char *drvoptcmd = NULL; // tcl command from command line option parsing
155
156static DrvOpt tk_options[] = { { "tcl_cmd", DRV_STR, &drvoptcmd, "Execute tcl command" },
157 { NULL, DRV_INT, NULL, NULL } };
158
160{
161#ifndef ENABLE_DYNDRIVERS
162 pdt->pl_MenuStr = "Tcl/TK Window";
163 pdt->pl_DevName = "tk";
164#endif
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
187void
188plD_init_tk( PLStream *pls )
189{
190 pls->dp = 0;
191 plParseDrvOpts( tk_options );
192 init( pls );
193}
194
195void
196plD_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
207static void
208tk_wr_header( PLStream *pls, const char *header )
209{
210 tk_wr( pdf_wr_header( pls->pdfs, header ) );
211}
212
213static void
214init( PLStream *pls )
215{
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
335void
336plD_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
377void
378plD_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
405void
406plD_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
424void
425plD_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
444void
445plD_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
461void
462plD_state_tk( PLStream *pls, PLINT op )
463{
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
547void
548plD_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
601static void
602tk_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
617static void
618GetCursor( 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
650static void
651tk_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
730static void
731tk_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
753static void
754tk_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
846static void
847tk_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
902static void
903abort_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
926static int
927pltkdriver_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
1088static void
1089init_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
1133static void
1134launch_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 )
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
1372static void
1373plwindow_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
1461static void
1462set_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
1516static void
1517link_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
1596static void
1597WaitForPage( 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
1621static void
1622CheckForEvents( 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
1640static void
1641HandleEvents( 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
1663static void
1664flush_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
1697static int
1698Abort( 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
1715static int
1716Plfinfo( 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
1752static int
1753KeyEH( 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
1778static int
1779ButtonEH( 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
1819static int
1820LookupTkKeyEvent( 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
1888static int
1889LookupTkButtonEvent( 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
1924static void
1925ProcessKey( 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
1973static void
1974ProcessButton( 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
2006static void
2007LocateKey( 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
2033static void
2034LocateButton( 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
2076static void
2077Locate( 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
2133static int
2134pltk_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
2162static void
2163tk_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
2201static void
2202server_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
2247static void
2248tcl_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
2270static void
2271copybuf( 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
2293int
2295{
2296 return 0;
2297}
2298
2299#endif // PLD_tk
const char header[]
Definition: deltaT-gen.c:41
void(* plD_line_fp)(struct PLStream_struct *, short, short, short, short)
Definition: disptab.h:68
void(* plD_tidy_fp)(struct PLStream_struct *)
Definition: disptab.h:72
void(* plD_bop_fp)(struct PLStream_struct *)
Definition: disptab.h:71
void(* plD_state_fp)(struct PLStream_struct *, PLINT)
Definition: disptab.h:73
void(* plD_eop_fp)(struct PLStream_struct *)
Definition: disptab.h:70
@ plDevType_Interactive
Definition: disptab.h:14
void(* plD_init_fp)(struct PLStream_struct *)
Definition: disptab.h:67
void(* plD_esc_fp)(struct PLStream_struct *, PLINT, void *)
Definition: disptab.h:74
void(* plD_polyline_fp)(struct PLStream_struct *, short *, short *, PLINT)
Definition: disptab.h:69
PLDLLIMPEXP_DRIVER void plD_dispatch_init_tk(PLDispatchTable *pdt)
#define PLSERV_HEADER
Definition: metadefs.h:21
#define LINETO
Definition: metadefs.h:62
#define INITIALIZE
Definition: metadefs.h:53
#define EOP
Definition: metadefs.h:57
#define BOP
Definition: metadefs.h:58
#define CHANGE_STATE
Definition: metadefs.h:68
#define PLSERV_VERSION
Definition: metadefs.h:22
#define POLYLINE
Definition: metadefs.h:65
#define LINE
Definition: metadefs.h:61
#define ESCAPE
Definition: metadefs.h:63
integer(kind=private_plint), parameter, private maxlen
Definition: plplot.f90:48
#define U_SHORT
Definition: pdf.h:30
#define U_CHAR
Definition: pdf.h:26
int pdf_wr_ieeef(PDFstrm *pdfs, float f)
Definition: pdfutils.c:916
int pdf_wr_1byte(PDFstrm *pdfs, U_CHAR s)
Definition: pdfutils.c:636
int pdf_wr_2bytes(PDFstrm *pdfs, U_SHORT s)
Definition: pdfutils.c:684
int pdf_wr_header(PDFstrm *pdfs, PLCHAR_VECTOR header)
Definition: pdfutils.c:509
int pdf_wr_2nbytes(PDFstrm *pdfs, U_SHORT *s, PLINT n)
Definition: pdfutils.c:741
int pdf_close(PDFstrm *pdfs)
Definition: pdfutils.c:238
PDFstrm * pdf_bopen(U_CHAR *buffer, size_t bufmax)
Definition: pdfutils.c:152
int plParseDrvOpts(DrvOpt *acc_opt)
Definition: plargs.c:1461
void plP_setpxl(PLFLT xpmm, PLFLT ypmm)
Definition: plcore.c:4238
void plP_setphy(PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax)
Definition: plcore.c:4249
static PLStream * pls[PL_NSTREAMS]
Definition: plcore.h:88
void plCloseFile(PLStream *pls)
Definition: plctrl.c:2635
void plGinInit(PLGraphicsIn *gin)
Definition: plctrl.c:2887
void plexit(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1958
char * plFindCommand(PLCHAR_VECTOR fn)
Definition: plctrl.c:2146
char PLDLLIMPEXP * plstrdup(PLCHAR_VECTOR src)
Definition: plctrl.c:2985
void plabort(PLCHAR_VECTOR errormsg)
Definition: plctrl.c:1894
#define PLDLLIMPEXP_DRIVER
Definition: pldll.h:81
#define PLK_Next
Definition: plevent.h:68
#define PLK_Return
Definition: plevent.h:50
#define PLK_Escape
Definition: plevent.h:51
#define PLK_Linefeed
Definition: plevent.h:49
PLINT plTranslateCursor(PLGraphicsIn *plg)
Definition: plpage.c:259
#define PLSTATE_FILL
Definition: plplotP.h:365
#define PLDI_DEV
Definition: plplotP.h:381
#define LPAGE_X
Definition: plplotP.h:308
#define PIXELS_X
Definition: plplotP.h:304
#define PLSTATE_WIDTH
Definition: plplotP.h:362
#define PLSTATE_CMAP0
Definition: plplotP.h:366
#define snprintf
Definition: plplotP.h:235
#define PLDI_ORI
Definition: plplotP.h:379
#define PLSTATE_COLOR1
Definition: plplotP.h:364
#define TRUE
Definition: plplotP.h:176
#define FALSE
Definition: plplotP.h:177
#define PL_UNDEFINED
Definition: plplotP.h:219
#define DPMM
Definition: plplotP.h:306
#define LPAGE_Y
Definition: plplotP.h:309
#define PLSTATE_CMAP1
Definition: plplotP.h:367
#define PL_RGB_COLOR
Definition: plplotP.h:285
@ DRV_INT
Definition: plplotP.h:758
@ DRV_STR
Definition: plplotP.h:758
#define PLDI_PLT
Definition: plplotP.h:380
#define PLSTATE_COLOR0
Definition: plplotP.h:363
#define free_mem(a)
Definition: plplotP.h:182
#define PIXELS_Y
Definition: plplotP.h:305
#define PLESC_GETC
Definition: plplot.h:283
float PLFLT
Definition: plplot.h:163
#define plgra
Definition: plplot.h:740
#define PLESC_EH
Definition: plplot.h:282
#define PLESC_FLUSH
Definition: plplot.h:281
#define PL_UNUSED(x)
Definition: plplot.h:138
#define PLESC_DI
Definition: plplot.h:280
#define pltext
Definition: plplot.h:855
#define PLESC_FILL
Definition: plplot.h:279
#define PLESC_XORMOD
Definition: plplot.h:286
int PLINT
Definition: plplot.h:181
#define NAME_plserver
char * pl_create_tempfifo(const char **p_fifoname, const char **p_dirname)
Definition: plstdio.c:344
PLDLLIMPEXP_TCLTK int plWait_Until(ClientData, Tcl_Interp *, int, const char **)
int plHost_ID(ClientData clientData, Tcl_Interp *interp, int argc, const char **argv)
PLDLLIMPEXP_TCLTK int pls_auto_path(Tcl_Interp *interp)
Definition: tclAPI.c:716
static int argc
Definition: qt.cpp:48
static char ** argv
Definition: qt.cpp:49
Definition: pdf.h:50
size_t bp
Definition: pdf.h:56
unsigned char r
Definition: plplot.h:548
unsigned char g
Definition: plplot.h:549
unsigned char b
Definition: plplot.h:550
PLFLT c1
Definition: plplot.h:559
PLFLT c2
Definition: plplot.h:560
PLFLT c3
Definition: plplot.h:561
int alt_hue_path
Definition: plplot.h:564
Definition: plstrm.h:41
PLINT xold
Definition: plstrm.h:43
PLINT yold
Definition: plstrm.h:43
plD_eop_fp pl_eop
Definition: disptab.h:86
const char * pl_DevName
Definition: disptab.h:80
plD_esc_fp pl_esc
Definition: disptab.h:90
plD_polyline_fp pl_polyline
Definition: disptab.h:85
plD_state_fp pl_state
Definition: disptab.h:89
plD_tidy_fp pl_tidy
Definition: disptab.h:88
plD_line_fp pl_line
Definition: disptab.h:84
plD_init_fp pl_init
Definition: disptab.h:83
plD_bop_fp pl_bop
Definition: disptab.h:87
const char * pl_MenuStr
Definition: disptab.h:79
PLFLT dipymax
Definition: plstrm.h:657
void(* KeyEH)(PLGraphicsIn *gin, void *KeyEH_data, int *exit_eventloop)
Definition: plstrm.h:598
void * ButtonEH_data
Definition: plstrm.h:604
PLINT ncol1
Definition: plstrm.h:539
PLINT ncp1
Definition: plstrm.h:539
char * plserver
Definition: plstrm.h:640
PLINT ncol0
Definition: plstrm.h:539
char * user
Definition: plstrm.h:639
PLColor * cmap0
Definition: plstrm.h:544
PLINT dev_fill0
Definition: plstrm.h:571
PLINT ipls
Definition: plstrm.h:527
PLINT page
Definition: plstrm.h:578
void * LocateEH_data
Definition: plstrm.h:608
PLINT dev_npts
Definition: plstrm.h:581
PLFLT dipxmax
Definition: plstrm.h:657
PLINT debug
Definition: plstrm.h:527
int server_nokill
Definition: plstrm.h:643
PLFLT jy
Definition: plstrm.h:659
PLINT color
Definition: plstrm.h:569
char * server_port
Definition: plstrm.h:639
short * dev_y
Definition: plstrm.h:582
int nopixmap
Definition: plstrm.h:635
void(* LocateEH)(PLGraphicsIn *gin, void *LocateEH_data, int *locate_mode)
Definition: plstrm.h:606
int db
Definition: plstrm.h:635
PLINT plbuf_write
Definition: plstrm.h:567
PLFLT width
Definition: plstrm.h:552
short * dev_x
Definition: plstrm.h:582
PLFLT jx
Definition: plstrm.h:659
char * plwindow
Definition: plstrm.h:640
char * geometry
Definition: plstrm.h:633
PLFLT aspect
Definition: plstrm.h:659
PLColor curcolor
Definition: plstrm.h:543
PLINT icol1
Definition: plstrm.h:539
char * FileName
Definition: plstrm.h:576
PLINT dev_xor
Definition: plstrm.h:572
char * program
Definition: plstrm.h:530
char * tk_file
Definition: plstrm.h:642
PLINT patt
Definition: plstrm.h:669
PLFLT dipxmin
Definition: plstrm.h:657
PLFLT dipymin
Definition: plstrm.h:657
PLINT termin
Definition: plstrm.h:568
PLFLT mar
Definition: plstrm.h:659
void * dev
Definition: plstrm.h:594
PLColor * cmap1
Definition: plstrm.h:545
PLINT difilt
Definition: plstrm.h:656
char * server_name
Definition: plstrm.h:639
PLINT dev_fill1
Definition: plstrm.h:571
PLINT level
Definition: plstrm.h:527
PLINT nopause
Definition: plstrm.h:568
char * auto_path
Definition: plstrm.h:641
PLControlPt cmap1cp[PL_MAX_CMAP1CP]
Definition: plstrm.h:547
PLINT dev_flush
Definition: plstrm.h:571
PDFstrm * pdfs
Definition: plstrm.h:579
int dp
Definition: plstrm.h:643
void(* ButtonEH)(PLGraphicsIn *gin, void *ButtonEH_data, int *exit_eventloop)
Definition: plstrm.h:602
PLINT dev_di
Definition: plstrm.h:571
int bufmax
Definition: plstrm.h:643
PLINT icol0
Definition: plstrm.h:539
char * server_host
Definition: plstrm.h:639
void * KeyEH_data
Definition: plstrm.h:600
PLFLT diorot
Definition: plstrm.h:661
Definition: pdf.h:62
FILE * file
Definition: pdf.h:64
Definition: pltkd.h:31
size_t cmdbuf_len
Definition: pltkd.h:39
int max_instr
Definition: pltkd.h:44
PLINT yold
Definition: pltkd.h:34
Tcl_Interp * interp
Definition: pltkd.h:33
Tk_Window w
Definition: pltkd.h:32
int instr
Definition: pltkd.h:43
const char * updatecmd
Definition: pltkd.h:41
PLGraphicsIn gin
Definition: pltkd.h:46
PLiodev * iodev
Definition: pltkd.h:40
PLINT xold
Definition: pltkd.h:34
pid_t child_pid
Definition: pltkd.h:42
unsigned height
Definition: pltkd.h:35
char * cmdbuf
Definition: pltkd.h:38
int locate_mode
Definition: pltkd.h:45
unsigned width
Definition: pltkd.h:35
int pass_thru
Definition: pltkd.h:37
int exit_eventloop
Definition: pltkd.h:36
static int tcl_cmd(Tcl_Interp *interp, const char *cmd)
Definition: tclAPI.c:848
#define dbug_enter(a)
Definition: tclMatrix.c:59
PLDLLIMPEXP_TCLTK int pl_PacketSend(Tcl_Interp *interp, PLiodev *iodev, PDFstrm *pdfs)
static Tcl_Interp * interp
Definition: tkMain.c:120
static Tcl_DString command
Definition: tkMain.c:121
int pldummy_tk()
Definition: tk.c:2294