PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
ntk.c
Go to the documentation of this file.
1 // Experimental tk driver using a plain "wish"
2 //
3 // Copyright (C) 2001 Joao Cardoso
4 // Copyright (C) 2004 Rafael Laboissiere
5 //
6 // This file is part of PLplot.
7 //
8 // PLplot is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU Library General Public License as published
10 // by the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // PLplot is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public License
19 // along with PLplot; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 //
23 
24 #include "plDevs.h"
25 
26 #ifdef PLD_ntk
27 
28 #include "plplotP.h"
29 #include "drivers.h"
30 #include "plevent.h"
31 
32 #include <tk.h>
33 
34 // Device info
35 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_ntk = "ntk:New tk driver:1:ntk:43:ntk\n";
36 
37 
39 
40 void plD_init_ntk( PLStream * );
41 void plD_line_ntk( PLStream *, short, short, short, short );
42 void plD_polyline_ntk( PLStream *, short *, short *, PLINT );
43 void plD_eop_ntk( PLStream * );
44 void plD_bop_ntk( PLStream * );
45 void plD_tidy_ntk( PLStream * );
46 void plD_state_ntk( PLStream *, PLINT );
47 void plD_esc_ntk( PLStream *, PLINT, void * );
48 
50 {
51 #ifndef ENABLE_DYNDRIVERS
52  pdt->pl_MenuStr = "New Tk device";
53  pdt->pl_DevName = "ntk";
54 #endif
56  pdt->pl_seq = 43;
57  pdt->pl_init = (plD_init_fp) plD_init_ntk;
58  pdt->pl_line = (plD_line_fp) plD_line_ntk;
59  pdt->pl_polyline = (plD_polyline_fp) plD_polyline_ntk;
60  pdt->pl_eop = (plD_eop_fp) plD_eop_ntk;
61  pdt->pl_bop = (plD_bop_fp) plD_bop_ntk;
62  pdt->pl_tidy = (plD_tidy_fp) plD_tidy_ntk;
63  pdt->pl_state = (plD_state_fp) plD_state_ntk;
64  pdt->pl_esc = (plD_esc_fp) plD_esc_ntk;
65 }
66 
67 // hardwired window size
68 #define XPIXELS 600
69 #define YPIXELS 400
70 
71 static PLFLT scale = 10.0; // Tk canvas units are in pixels, giving corse curves, fool plplot, and scale down when sending to tk
72 static PLFLT ppm; // device pixels per mm
73 
74 static Tcl_Interp *interp = NULL; // tcl interpreter
75 static Tk_Window mainw; // tk main window
76 
77 static char curcolor[80]; // current color in #rrggbb notation
78 
79 // 12000 is large enough to satisfy example 27 needs without
80 // erroring out in plD_polyline_ntk. Quadruple that to be conservative.
81 #define PLPLOT_NTK_CMD_SIZE 48000
82 static char cmd[PLPLOT_NTK_CMD_SIZE]; // buffer to build command to interp
83 static int ccanv = 0; // current canvas number
84 static char base[80]; // name of frame that contains the canvas
85 static char dash[80]; // dash string, as <mark space>*
86 
87 // line buffering
88 #define NPTS 1000
89 static short xold = -1, yold = -1; // last point of last 2 points line
90 static short xb[NPTS], yb[NPTS]; // buffer
91 static int curpts = 0; // current number of points buffered
92 
93 static int local = 1; // "local" or "remote" interpreter
94 static char rem_interp[80]; // name of remote interp
95 
96 // physical devices coordinates
97 static PLINT xmin = 0;
98 static PLINT xmax = XPIXELS;
99 static PLINT ymin = 0;
100 static PLINT ymax = YPIXELS;
101 
102 // locator
103 static PLGraphicsIn gin;
104 
105 static void
106 tk_cmd( const char *gcmd )
107 {
108  static char scmd[PLPLOT_NTK_CMD_SIZE];
109 
110  if ( local )
111  Tcl_Eval( interp, gcmd );
112  else
113  {
114  // the -async option makes it block, some times! but is *much* faster!
115  // and was working OK till now :(
116  // sprintf(scmd, "send -async %s {%s}", rem_interp, cmd);
117  //
118  sprintf( scmd, "send %s {%s}", rem_interp, gcmd ); // mess! make it more efficient
119  if ( Tcl_Eval( interp, scmd ) != TCL_OK )
120  fprintf( stderr, "%s\n", Tcl_GetStringResult( interp ) );
121  }
122 }
123 
124 static void
125 create_canvas( PLStream *pls )
126 {
127  int columnbreak;
128 
129  ccanv++;
130  columnbreak = ( ccanv % 30 == 0 );
131 
132  // create new canvas
133  sprintf( cmd, "set ccanv %d; canvas $plf.f2.c$ccanv -width $xmax -height $ymax -background #%02x%02x%02x -xscrollcommand \"$hs set\" -yscrollcommand \"$vs set\" -scrollregion \"0 0 $xmax $ymax\"", ccanv, pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b );
134  tk_cmd( cmd );
135 
136  // add new canvas to option menu
137  sprintf( cmd, "$plf.f1.mb.menu add command -label \"Page $ccanv\" -columnbreak %d -command {\n"
138  "set w $plf.f2.c%d;\n"
139  "$hs configure -command \"$w xview\";\n"
140  "$vs configure -command \"$w yview\";\n"
141  "set dname \"Page %d\";\n"
142  "pack forget $ocanvas;\n"
143  "set ocanvas $plf.f2.c%d;\n"
144  "pack $ocanvas -fill both -expand 1;\n"
145  "scan [$w xview] \"%%f %%f\" i j;\n"
146  "$hs set $i $j;\n"
147  "scan [$w yview] \"%%f %%f\" i j;\n"
148  "$vs set $i $j;}",
149  columnbreak, ccanv, ccanv, ccanv );
150  tk_cmd( cmd );
151 
152  sprintf( cmd, "set item(%d) 0", ccanv );
153  tk_cmd( cmd );
154 
155  // Shif-B1, zooms in
156  // FIXME inform the core lib of the zoom, see plframe.c around line 2818
157 
158  sprintf( cmd, "bind $plf.f2.c$ccanv <Shift-Button-1> {\n"
159  "set cc %d;\n"
160  "incr item($cc); set tt $item($cc);\n"
161  "if {$tt == 1} {\n"
162  "incr scroll_use;\n"
163  "pack $hs -side bottom -fill x;\n"
164  "pack $vs -side right -fill y;\n"
165  "pack forget %%W; pack %%W -fill both -expand 1}\n"
166  "set zx($cc,$tt) %%x;\n"
167  "set zy($cc,$tt) %%y;\n"
168  "%%W scale all %%x %%y 1.6 1.6;\n"
169  "%%W configure -scrollregion [%%W bbox all];\n"
170  "}", ccanv );
171 
172  tk_cmd( cmd );
173 
174  // Shif-B3, zooms out
175  sprintf( cmd, "bind $plf.f2.c$ccanv <Shift-Button-3> {\n"
176  "set cc %d; set tt $item($cc);\n"
177  "if {$tt != 0} {\n"
178  "%%W scale all $zx($cc,$tt) $zy($cc,$tt) 0.625 0.625\n"
179  "%%W configure -scrollregion [%%W bbox all];\n"
180  "set item($cc) [expr $tt - 1]}\n"
181  "if { $item($cc) == 0} {\n"
182  "set scroll_use [expr $scroll_use - 1];\n"
183  "if {$scroll_use == 0} {\n"
184  "pack forget $plf.f2.hscroll $plf.f2.vscroll}\n"
185  "%%W configure -scrollregion \"0 0 $xmax $ymax\"}}", ccanv );
186  tk_cmd( cmd );
187 
188  // Shift-B2, resets
189  sprintf( cmd, "bind $plf.f2.c$ccanv <Shift-Button-2> {\n"
190  "set cc %d; set tt $item($cc); \n"
191  "while {$tt != 0} {\n"
192  "%%W scale all $zx($cc,$tt) $zy($cc,$tt) 0.625 0.625\n"
193  "set tt [expr $tt - 1]};\n"
194  "set item($cc) 0;\n"
195  "%%W configure -scrollregion \"0 0 $xmax $ymax\";\n"
196  "set scroll_use [expr $scroll_use - 1];\n"
197  "if {$scroll_use == 0} {\n"
198  "pack forget $plf.f2.hscroll $plf.f2.vscroll}}", ccanv );
199  tk_cmd( cmd );
200 
201  // Control-B1-Motion, pan
202  sprintf( cmd, "bind $plf.f2.c$ccanv <Control-Button-1> \"$plf.f2.c%d scan mark %%x %%y\"", ccanv );
203  tk_cmd( cmd );
204 
205  sprintf( cmd, "bind $plf.f2.c$ccanv <Control-Button1-Motion> \"$plf.f2.c%d scan dragto %%x %%y\"", ccanv );
206  tk_cmd( cmd );
207 
208  // Control-B2, identify and (in the far future) edit object
209  tk_cmd( "bind $plf.f2.c$ccanv <Control-Button-2> {\n"
210  "set xx [ expr [winfo pointerx .] - [winfo rootx %W]];\n"
211  "set yy [ expr [winfo pointery .] - [winfo rooty %W]];\n"
212  "set near [%W find closest $xx $yy];\n"
213  "%W move $near 20 20;\n"
214  "after 500 \"%W move $near -20 -20\"}" );
215 
216  // change view to the new canvas by invoking the menu buttom
217  sprintf( cmd, "$plf.f1.mb.menu invoke %d", ccanv - 1 );
218  tk_cmd( cmd );
219 }
220 
221 //--------------------------------------------------------------------------
222 // plD_init_ntk()
223 //
224 // Initialize device (terminal).
225 //--------------------------------------------------------------------------
226 
227 void
228 plD_init_ntk( PLStream *pls )
229 {
230  pls->dev_fill0 = 1; // Handle solid fills
231  pls->dev_fill1 = 1; // Driver handles pattern fills
232  pls->color = 1; // Is a color device
233  pls->dev_dash = 1; // Handle dashed lines
234  pls->plbuf_write = 1; // Use plot buffer
235 
236  strcpy( curcolor, "black" ); // default color by name, not #rrggbb
237 
238  if ( pls->server_name != NULL )
239  {
240  local = 0;
241  strcpy( rem_interp, pls->server_name );
242  }
243 
244  if ( pls->geometry != NULL )
245  sscanf( pls->geometry, "%dx%d", &xmax, &ymax );
246 
247 // if ( pls->plwindow != NULL )
248 // strcpy( base, pls->plwindow );
249 // else
250  strcpy( base, ".plf" ); // default frame containing the canvas
251 
252  interp = Tcl_CreateInterp();
253 
254  if ( Tcl_Init( interp ) != TCL_OK )
255  plexit( "Unable to initialize Tcl." );
256 
257  if ( Tk_Init( interp ) )
258  plexit( "Unable to initialize Tk." );
259 
260  mainw = Tk_MainWindow( interp );
261  Tcl_Eval( interp, "rename exec {}" );
262 
263  Tcl_Eval( interp, "tk appname PLplot_ntk" ); // give interpreter a name
264 
265  if ( !local )
266  {
267  Tcl_Eval( interp, "wm withdraw ." );
268 
269  sprintf( cmd, "send %s \"set client [tk appname]; wm deiconify .\"", rem_interp );
270  if ( Tcl_Eval( interp, cmd ) != TCL_OK )
271  {
272  fprintf( stderr, "%s\n", Tcl_GetStringResult( interp ) );
273  plexit( "No such tk server." );
274  }
275  }
276 
277  sprintf( cmd, "set scroll_use 0; set plf %s; set vs $plf.f2.vscroll; set hs $plf.f2.hscroll; set xmax %d; set ymax %d; set ocanvas .;", base, xmax, ymax );
278  tk_cmd( cmd );
279 
280  tk_cmd( "catch \"frame $plf\"; pack $plf -fill both -expand 1" );
281 
282  sprintf( cmd, "frame $plf.f1;\n"
283  "frame $plf.f2 -width %d -height %d;\n"
284  "pack $plf.f1 -fill x;\n"
285  "pack $plf.f2 -fill both -expand 1", xmax, ymax );
286  tk_cmd( cmd );
287 
288  tk_cmd( "scrollbar $plf.f2.hscroll -orient horiz;\n"
289  "scrollbar $plf.f2.vscroll" );
290 
291  tk_cmd( "menubutton $plf.f1.mb -text \"Page 1\" -textvariable dname -relief raised -indicatoron 1 -menu $plf.f1.mb.menu;\n"
292  "menu $plf.f1.mb.menu -tearoff 0;\n"
293  "pack $plf.f1.mb -side left" );
294 
295  if ( local )
296  tk_cmd( "button $plf.f1.quit -text Quit -command exit;\n"
297  "pack $plf.f1.quit -side right" );
298  else
299  tk_cmd( "button $plf.f1.quit -text Quit -command {send -async $client exit;\n"
300  "destroy $plf;\n"
301  "wm withdraw .};\n"
302  "pack $plf.f1.quit -side right" );
303 
304  // FIXME: I just discovered that Tcl_Eval is slower than Tcl_EvalObj. Fix it global-wide, `man Tcl_Eval'
305 
306  // Set up device parameters
307 
308  Tcl_Eval( interp, "tk scaling" ); // pixels per mm
309  ppm = (PLFLT) atof( Tcl_GetStringResult( interp ) ) / ( 25.4 / 72. );
310  plP_setpxl( ppm, ppm );
311  plP_setphy( xmin, (PLINT) ( xmax * scale ), ymin, (PLINT) ( ymax * scale ) );
312 
313  tk_cmd( "update" );
314 }
315 
316 static void
317 flushbuffer( PLStream *pls )
318 {
319  if ( curpts )
320  {
321  plD_polyline_ntk( pls, xb, yb, curpts );
322 // if (curpts != 2) fprintf(stderr,"%d ", curpts);
323  xold = yold = -1; curpts = 0;
324  }
325 }
326 
327 void
328 plD_line_ntk( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
329 {
330  if ( xold == x1a && yold == y1a )
331  {
332  xold = xb[curpts] = x2a; yold = yb[curpts] = y2a; curpts++;
333  }
334  else
335  {
336  flushbuffer( pls );
337  xb[curpts] = x1a; yb[curpts] = y1a; curpts++;
338  xold = xb[curpts] = x2a; yold = yb[curpts] = y2a; curpts++;
339  }
340 
341  if ( curpts == NPTS )
342  {
343  //fprintf( stderr, "\nflush: %d ", curpts );
344  flushbuffer( pls );
345  }
346 }
347 
348 void
349 plD_polyline_ntk( PLStream * PL_UNUSED( pls ), short *xa, short *ya, PLINT npts )
350 {
351  PLINT i, j;
352 
353  // there must exist a way to code this using the tk C API
354  j = sprintf( cmd, "$plf.f2.c%d create line ", ccanv );
355  for ( i = 0; i < npts; i++ )
356  {
357  // To be completely safe, assume 5 characters to the left of the
358  // decimal point ==> 2*(5+3) characters written per sprintf
359  // call.
360  if ( ( j + 16 ) > PLPLOT_NTK_CMD_SIZE )
361  plexit( "plD_polyline_ntk: too many x, y values to hold in static cmd array" );
362  j += sprintf( &cmd[j], "%.1f %.1f ", xa[i] / scale, ymax - ya[i] / scale );
363  }
364  j += sprintf( &cmd[j], " -fill %s", curcolor );
365  if ( dash[0] == '-' )
366  j += sprintf( &cmd[j], " %s", dash );
367 
368  tk_cmd( cmd );
369 }
370 
371 // an event loop has to be designed, getcursor() and waitforpage() are just experimental
372 
373 static void
374 waitforpage( PLStream * PL_UNUSED( pls ) )
375 {
376  int key = 0, st = 0;
377  // why can't I bind to the canvas? or even any frame?
378 //tk_cmd("bind . <KeyPress> {set keypress %N; puts \"\n%k-%A-%K-%N\"}");
379  tk_cmd( "bind . <KeyPress> {set keypress %N}" );
380 
381  while ( ( key & 0xff ) != PLK_Return && ( key & 0xff ) != PLK_Linefeed && key != PLK_Next && key != 'Q' )
382  {
383  while ( st != 1 )
384  {
385  tk_cmd( "update" );
386  tk_cmd( "info exists keypress" );
387  sscanf( Tcl_GetStringResult( interp ), "%d", &st );
388  }
389 
390  tk_cmd( "set keypress" );
391  sscanf( Tcl_GetStringResult( interp ), "%d", &key );
392 //fprintf(stderr,"\n%d\n", key);fflush(stderr);
393  tk_cmd( "unset keypress" );
394  st = 0;
395  }
396 
397  tk_cmd( "bind . <Key> {};" );
398 }
399 
400 void
401 plD_eop_ntk( PLStream *pls )
402 {
403  flushbuffer( pls );
404  tk_cmd( "update" );
405 }
406 
407 void
408 plD_bop_ntk( PLStream *pls )
409 {
410  create_canvas( pls );
411 }
412 
413 void
414 plD_tidy_ntk( PLStream *pls )
415 {
416  if ( !pls->nopause )
417  waitforpage( pls );
418 
419  tk_cmd( "destroy $plf; wm withdraw ." );
420 }
421 
422 void
423 plD_state_ntk( PLStream *pls, PLINT op )
424 {
425  switch ( op )
426  {
427  case PLSTATE_COLOR0:
428  case PLSTATE_COLOR1:
429  flushbuffer( pls );
430  sprintf( curcolor, "#%02x%02x%02x",
431  pls->curcolor.r, pls->curcolor.g, pls->curcolor.b );
432  break;
433  }
434 }
435 
436 static void
437 getcursor( PLStream * PL_UNUSED( pls ), PLGraphicsIn *ptr )
438 {
439  int st = 0;
440 
441  plGinInit( &gin );
442 
443  if ( 0 )
444  {
445  while ( st != 1 )
446  {
447  tk_cmd( "update" );
448  tk_cmd( "winfo exists $plf.f2.c$ccanv" );
449  sscanf( Tcl_GetStringResult( interp ), "%d", &st );
450  }
451  st = 0;
452  // this give a "Segmentation fault", even after checking for the canvas!
453  tk_cmd( "set ocursor [lindex [$plf.f2.c$ccanv configure -cursor] 4]" );
454  }
455 
456  tk_cmd( "$plf.f2.c$ccanv configure -cursor cross;\n"
457  "bind $plf.f2.c$ccanv <Button> {set xloc %x; set yloc %y; set bloc %b; set sloc %s};\n"
458  "bind $plf.f2.c$ccanv <B1-Motion> {set xloc %x; set yloc %y; set bloc %b; set sloc %s};\n"
459  "bind $plf.f2.c$ccanv <B2-Motion> {set xloc %x; set yloc %y; set bloc %b; set sloc %s};\n"
460  "bind $plf.f2.c$ccanv <B3-Motion> {set xloc %x; set yloc %y; set bloc %b; set sloc %s};" );
461 
462  while ( st != 1 )
463  {
464  tk_cmd( "update" );
465  tk_cmd( "info exists xloc" );
466  sscanf( Tcl_GetStringResult( interp ), "%d", &st );
467  }
468  tk_cmd( "set xloc" );
469  sscanf( Tcl_GetStringResult( interp ), "%d", &gin.pX );
470  tk_cmd( "set yloc" );
471  sscanf( Tcl_GetStringResult( interp ), "%d", &gin.pY );
472  tk_cmd( "set bloc" );
473  sscanf( Tcl_GetStringResult( interp ), "%ud", &gin.button );
474  tk_cmd( "set sloc" );
475  sscanf( Tcl_GetStringResult( interp ), "%ud", &gin.state );
476 
477  gin.dX = (PLFLT) gin.pX / xmax;
478  gin.dY = 1. - (PLFLT) gin.pY / ymax;
479 
480  tk_cmd( "bind $plf.f2.c$ccanv <ButtonPress> {};\n"
481  "bind $plf.f2.c$ccanv <ButtonMotion> {};\n"
482  "bind $plf.f2.c$ccanv <B2-Motion> {};\n"
483  "bind $plf.f2.c$ccanv <B3-Motion> {};\n"
484  "unset xloc" );
485 
486  // seg fault, see above. tk_cmd("$plf.f2.c$ccanv configure -cursor $ocursor");
487  tk_cmd( "$plf.f2.c$ccanv configure -cursor {}" );
488 
489  *ptr = gin;
490 }
491 
492 void
493 plD_esc_ntk( PLStream *pls, PLINT op, void *ptr )
494 {
495  PLINT i, j;
496  short *xa, *ya;
497  //Pixmap bitmap;
498  static const unsigned char bit_pat[] = {
499  0x24, 0x01, 0x92, 0x00, 0x49, 0x00, 0x24, 0x00, 0x12, 0x00, 0x09, 0x00,
500  0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff
502  };
503 
504  switch ( op )
505  {
506  case PLESC_DASH:
507  xa = (short *) malloc( sizeof ( short ) * (size_t) pls->dev_npts );
508  ya = (short *) malloc( sizeof ( short ) * (size_t) pls->dev_npts );
509  for ( i = 0; i < pls->dev_npts; i++ )
510  {
511  xa[i] = pls->dev_x[i];
512  ya[i] = pls->dev_y[i];
513  }
514 
515  j = sprintf( dash, "-dash {" );
516  for ( i = 0; i < pls->nms; i++ )
517  j += sprintf( &dash[j], " %d %d",
518  (int) ceil( pls->mark[i] / 1e3 * ppm ),
519  (int) ceil( pls->space[i] / 1e3 * ppm ) );
520  sprintf( &dash[j], "}" );
521  plD_polyline_ntk( pls, xa, ya, pls->dev_npts );
522  free( xa ); free( ya );
523  dash[0] = 0;
524  break;
525 
526  case PLESC_FLUSH:
527  tk_cmd( "update" );
528  break;
529 
530  case PLESC_GETC:
531  getcursor( pls, (PLGraphicsIn *) ptr );
532  break;
533 
534  case PLESC_FILL:
535  if ( pls->patt != 0 )
536  {
537  // this is a hack! The real solution is in the if(0) below
538  pls->xpmm *= scale;
539  pls->ypmm *= scale;
540  plfill_soft( pls->dev_x, pls->dev_y, pls->dev_npts );
541  pls->xpmm /= scale;
542  pls->ypmm /= scale;
543  }
544  else
545  {
546  j = sprintf( cmd, "$plf.f2.c%d create polygon ", ccanv );
547  for ( i = 0; i < pls->dev_npts; i++ )
548  j += sprintf( &cmd[j], "%.1f %.1f ", pls->dev_x[i] / scale,
549  ymax - pls->dev_y[i] / scale );
550  j += sprintf( &cmd[j], " -fill %s", curcolor );
551  tk_cmd( cmd );
552  }
553 
554  if ( 0 )
555  {
556  if ( pls->patt != 0 )
557  {
558  Tk_DefineBitmap( interp, Tk_GetUid( "foo" ), (const char *) bit_pat, 16, 16 );
559  //bitmap = Tk_GetBitmap( interp, mainw, Tk_GetUid( "patt" ) );
560  }
561  j = sprintf( cmd, "$plf.f2.c%d create polygon ", ccanv );
562  for ( i = 0; i < pls->dev_npts; i++ )
563  j += sprintf( &cmd[j], "%.1f %.1f ", pls->dev_x[i] / scale,
564  ymax - pls->dev_y[i] / scale );
565  j += sprintf( &cmd[j], " -fill %s", curcolor );
566  if ( pls->patt != 0 )
567  sprintf( &cmd[j], " -stipple patt -outline black" );
568 
569  tk_cmd( cmd );
570  //Tk_FreeBitmap(display, bitmap)
571  }
572  break;
573  }
574 }
575 
576 #else
577 int
579 {
580  return 0;
581 }
582 
583 #endif // PLD_ntkdev