1 /*
2 * xnec2c - GTK2-based version of nec2c, the C translation of NEC2
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 /*
20 * Initial main.c file generated by Glade. Edit as required.
21 * Glade will not overwrite this file.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include <gtk/gtk.h>
29 #include <sys/wait.h>
30 #include <locale.h>
31
32 #include "main.h"
33 #include "shared.h"
34
35 static void sig_handler(int signal);
36
37 /* Child process pid returned by fork() */
38 static pid_t child_pid = (pid_t)(-1);
39
40 /*------------------------------------------------------------------------*/
41
42 int
main(int argc,char * argv[])43 main (int argc, char *argv[])
44 {
45 /* getopt() variables */
46 int option, idx, err;
47 gboolean new = TRUE;
48
49 /*** Signal handler related code ***/
50 /* new and old actions for sigaction() */
51 struct sigaction sa_new, sa_old;
52
53 /* initialize new actions */
54 sa_new.sa_handler = sig_handler;
55 sigemptyset( &sa_new.sa_mask );
56 sa_new.sa_flags = 0;
57
58 /* register function to handle signals */
59 sigaction( SIGINT, &sa_new, &sa_old );
60 sigaction( SIGSEGV, &sa_new, NULL );
61 sigaction( SIGFPE, &sa_new, NULL );
62 sigaction( SIGTERM, &sa_new, NULL );
63 sigaction( SIGABRT, &sa_new, NULL );
64 sigaction( SIGCHLD, &sa_new, NULL );
65
66 /* Process command line options */
67 calc_data.num_jobs = 1;
68 while( (option = getopt(argc, argv, "i:j:hv") ) != -1 )
69 {
70 switch( option )
71 {
72 case 'i': /* specify input file name */
73 if( strlen(optarg) > 80 )
74 {
75 fprintf ( stderr,
76 _("xnec2c: input file name too long (>80 char)\n") );
77 exit(-1);
78 }
79 Strlcpy( infile, optarg, sizeof(infile) ); /* For null term. */
80 break;
81
82 case 'j': /* number of child processes = num of processors */
83 calc_data.num_jobs = atoi( optarg );
84 break;
85
86 case 'h': /* print usage and exit */
87 usage();
88 exit(0);
89
90 case 'v': /* print xnec2c version */
91 puts( PACKAGE_STRING );
92 exit(0);
93
94 default:
95 usage();
96 exit(0);
97
98 } /* end of switch( option ) */
99
100 } /* while( (option = getopt(argc, argv, "i:o:hv") ) != -1 ) */
101
102 /* Read input file path name if not supplied by -i option */
103 if( (strlen(infile) == 0) &&
104 (strstr(argv[argc - 1], ".nec") || strstr(argv[argc - 1], ".NEC")) )
105 {
106 if( strlen(argv[argc - 1]) > 80 )
107 {
108 fprintf ( stderr,
109 _("xnec2c: input file path name too long (>80 char)\n") );
110 exit(-1);
111 }
112 Strlcpy( infile, argv[argc-1], sizeof(infile) ); /* For null term. */
113 }
114
115 /* When forking is useful, e.g. if more than 1 processor is
116 * available, the parent process handles the GUI and delegates
117 * calculations to the child processes, one per processor. The
118 * requested number of child processes = number of processors */
119
120 /* Allocate buffers for fork data */
121 if( calc_data.num_jobs > 1 )
122 {
123 size_t mreq = (size_t)calc_data.num_jobs * sizeof(forked_proc_data_t *);
124 mem_alloc( (void **)&forked_proc_data, mreq, "in main.c" );
125 for( idx = 0; idx < calc_data.num_jobs; idx++ )
126 {
127 forked_proc_data[idx] = NULL;
128 mreq = sizeof(forked_proc_data_t);
129 mem_alloc( (void **)&forked_proc_data[idx], mreq, "in main.c" );
130 }
131
132 /* Fork child processes */
133 for( idx = 0; idx < calc_data.num_jobs; idx++ )
134 {
135 /* Make pipes to transfer data */
136 err = pipe( forked_proc_data[idx]->pnt2child_pipe );
137 if( err )
138 {
139 perror( "xnec2c: pipe()" );
140 fprintf( stderr,
141 _("xnec2c: exiting after fatal error (pipe() failed)") );
142 exit(-1);
143 }
144
145 err = pipe( forked_proc_data[idx]->child2pnt_pipe );
146 if( err )
147 {
148 perror( "xnec2c: pipe()" );
149 fprintf( stderr,
150 _("xnec2c: exiting after fatal error (pipe() failed)") );
151 exit(-1);
152 }
153
154 /* Fork child process */
155 forked_proc_data[idx]->child_pid = fork();
156 if( forked_proc_data[idx]->child_pid == -1 )
157 {
158 perror( "xnec2c: fork()" );
159 fprintf( stderr,
160 _("xnec2c: exiting after fatal error (fork() failed)") );
161 exit(-1);
162 }
163 else child_pid = forked_proc_data[idx]->child_pid;
164
165 /* Child get out of forking loop! */
166 if( CHILD ) Child_Process( idx );
167
168 /* Ready to accept a job */
169 forked_proc_data[idx]->busy = FALSE;
170
171 /* Close unwanted pipe ends */
172 close( forked_proc_data[idx]->pnt2child_pipe[READ] );
173 close( forked_proc_data[idx]->child2pnt_pipe[WRITE] );
174
175 /* Set file descriptors for select() */
176 FD_ZERO( &forked_proc_data[idx]->read_fds );
177 FD_SET( forked_proc_data[idx]->child2pnt_pipe[READ],
178 &forked_proc_data[idx]->read_fds );
179 FD_ZERO( &forked_proc_data[idx]->write_fds );
180 FD_SET( forked_proc_data[idx]->pnt2child_pipe[WRITE],
181 &forked_proc_data[idx]->write_fds );
182
183 /* Count child processes */
184 num_child_procs++;
185 } /* for( idx = 0; idx < calc_data.num_jobs; idx++ ) */
186
187 FORKED = TRUE;
188 } /* if( calc_data.num_jobs > 1 ) */
189
190 gtk_set_locale ();
191 gtk_init (&argc, &argv);
192 setlocale(LC_NUMERIC, "C");
193 add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
194
195 /*
196 * The following code was added by Glade to create one of each component
197 * (except popup menus), just so that you see something after building
198 * the project. Delete any components that you don't want shown initially.
199 */
200 main_window = create_main_window ();
201 gtk_window_set_title( GTK_WINDOW(main_window), PACKAGE_STRING );
202 gtk_widget_show (main_window);
203 mainwin_frequency = GTK_SPIN_BUTTON(lookup_widget(
204 main_window, "main_freq_spinbutton") );
205
206 gtk_widget_hide_all( lookup_widget(
207 main_window, "main_hbox1") );
208 gtk_widget_hide_all( lookup_widget(
209 main_window, "main_hbox2") );
210 gtk_widget_hide_all( lookup_widget(
211 main_window, "main_hbox3") );
212 gtk_widget_hide_all( lookup_widget(
213 main_window, "main_view_menuitem") );
214 gtk_widget_hide( lookup_widget(
215 main_window, "structure_drawingarea") );
216
217 structure_drawingarea = lookup_widget(
218 main_window, "structure_drawingarea");
219 gtk_widget_add_events(
220 GTK_WIDGET(structure_drawingarea),
221 GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK );
222 structure_motion_handler = g_signal_connect (
223 (gpointer) structure_drawingarea,
224 "motion_notify_event",
225 G_CALLBACK (on_structure_drawingarea_motion_notify_event),
226 NULL);
227
228 /* Initialize structure projection angles */
229 rotate_structure = GTK_SPIN_BUTTON(lookup_widget(
230 main_window, "main_rotate_spinbutton"));
231 incline_structure = GTK_SPIN_BUTTON(lookup_widget(
232 main_window, "main_incline_spinbutton"));
233 structure_zoom = GTK_SPIN_BUTTON(lookup_widget(
234 main_window, "structure_zoom_spinbutton"));
235 structure_fstep_entry = GTK_ENTRY(lookup_widget(
236 main_window, "structure_fstep_entry")) ;
237
238 structure_proj_params.Wr =
239 gtk_spin_button_get_value(rotate_structure);
240 structure_proj_params.Wi =
241 gtk_spin_button_get_value(incline_structure);
242
243 structure_proj_params.xy_zoom = 1.0;
244 structure_proj_params.reset = TRUE;
245 structure_proj_params.type = STRUCTURE_DRAWINGAREA;
246
247 rdpattern_proj_params.xy_zoom = 1.0;
248 rdpattern_proj_params.reset = TRUE;
249 rdpattern_proj_params.type = RDPATTERN_DRAWINGAREA;
250 calc_data.zo = 50.0;
251
252 /* Open input file if specified */
253 if( strlen(infile) > 0 )
254 g_idle_add( Open_Input_File, (gpointer)&new );
255 else
256 SetFlag( INPUT_PENDING );
257
258 gtk_main ();
259
260 return 0;
261 }
262
263 /*-----------------------------------------------------------------------*/
264
265 /* Open_Input_File()
266 *
267 * Opens NEC2 input file
268 */
269
270 gboolean
Open_Input_File(gpointer udata)271 Open_Input_File( gpointer udata )
272 {
273 gboolean ok, new;
274
275 /* Stop freq loop */
276 if( isFlagSet(FREQ_LOOP_RUNNING) )
277 Stop_Frequency_Loop();
278
279 /* Close open files if any */
280 Close_File( &input_fp );
281
282 /* Suppress activity while input file opened */
283 ClearFlag( OPEN_INPUT_FLAGS );
284 SetFlag( INPUT_PENDING );
285 calc_data.fstep = -1;
286
287 /* Open NEC2 input file */
288 Open_File( &input_fp, infile, "r");
289
290 /* Read input file, record failures */
291 ok = Read_Comments() && Read_Geometry() && Read_Commands();
292 if( !ok )
293 {
294 /* Hide main control buttons etc */
295 gtk_widget_hide_all( lookup_widget(
296 main_window, "main_hbox1") );
297 gtk_widget_hide_all( lookup_widget(
298 main_window, "main_hbox2") );
299 gtk_widget_hide_all( lookup_widget(
300 main_window, "main_view_menuitem") );
301 gtk_widget_hide( lookup_widget(
302 main_window, "structure_drawingarea") );
303
304 /* Close plot/rdpat windows */
305 if( rdpattern_window != NULL )
306 gtk_widget_destroy( rdpattern_window );
307 if( freqplots_window != NULL )
308 gtk_widget_destroy( freqplots_window );
309
310 if( nec2_edit_window == NULL )
311 Open_Nec2_Editor( NEC2_EDITOR_RELOAD );
312 else
313 Nec2_Input_File_Treeview( NEC2_EDITOR_REVERT );
314
315 return( FALSE );
316 }
317
318 /* Ask child processes to read input file */
319 if( FORKED )
320 {
321 int idx;
322 size_t lenc, leni;
323
324 lenc = strlen( fork_commands[INFILE] );
325 leni = strlen( infile );
326 for( idx = 0; idx < num_child_procs; idx++ )
327 {
328 Write_Pipe( idx, fork_commands[INFILE], (ssize_t)lenc, TRUE );
329 Write_Pipe( idx, infile, (ssize_t)leni, TRUE );
330 }
331 } /* if( FORKED ) */
332
333 /* Allow redraws on expose events etc */
334 ClearFlag( INPUT_PENDING );
335
336 /* Initialize xnec2c */
337 SetFlag( COMMON_PROJECTION );
338 SetFlag( COMMON_FREQUENCY );
339 SetFlag( MAIN_NEW_FREQ );
340 SetFlag( FREQ_LOOP_INIT );
341 floop_tag = 0;
342 save.last_freq = 0.0;
343 crnt.newer = 0;
344 crnt.valid = 0;
345 near_field.newer = 0;
346 near_field.valid = 0;
347
348 /* Set projection at 45 deg rotation and
349 * inclination if NEC2 editor window is not open */
350 if( nec2_edit_window == NULL )
351 New_Viewer_Angle( 45.0, 45.0, rotate_structure,
352 incline_structure, &structure_proj_params );
353 New_Structure_Projection_Angle();
354
355 /* Show current frequency */
356 gtk_spin_button_set_value(
357 mainwin_frequency, (gdouble)calc_data.fmhz );
358
359 /* Show main control buttons etc */
360 gtk_widget_show_all( lookup_widget(
361 main_window, "main_hbox1") );
362 gtk_widget_show_all( lookup_widget(
363 main_window, "main_hbox2") );
364 gtk_widget_show_all( lookup_widget(
365 main_window, "main_hbox3") );
366 gtk_widget_show_all( lookup_widget(
367 main_window, "main_view_menuitem") );
368 gtk_widget_show( lookup_widget(
369 main_window, "structure_drawingarea") );
370
371 /* If currents or charges draw button is active
372 * re-initialize structure currents/charges drawing */
373 if( isFlagSet(DRAW_CURRENTS) )
374 Main_Currents_Togglebutton_Toggled( TRUE );
375 if( isFlagSet(DRAW_CHARGES) )
376 Main_Charges_Togglebutton_Toggled( TRUE );
377
378 /* Set input file to NEC2 editor. It will only
379 * happen if the NEC2 editor window is open */
380 new = *(gboolean *)udata;
381 if( new )
382 Nec2_Input_File_Treeview( NEC2_EDITOR_REVERT );
383 else
384 Nec2_Input_File_Treeview( NEC2_EDITOR_RELOAD );
385
386 /* Display sructure */
387 Draw_Structure( structure_drawingarea );
388
389 /* Re-initialize Rad Pattern drawing if window open */
390 if( rdpattern_window != NULL )
391 {
392 Main_Rdpattern_Activate( FALSE );
393 crnt.valid = FALSE;
394
395 if( isFlagSet(DRAW_GAIN) )
396 Rdpattern_Gain_Togglebutton_Toggled( TRUE );
397 else
398 Rdpattern_Gain_Togglebutton_Toggled( FALSE );
399
400 if( isFlagSet(DRAW_EHFIELD) )
401 Rdpattern_EH_Togglebutton_Toggled( TRUE );
402 else
403 Rdpattern_EH_Togglebutton_Toggled( FALSE );
404 }
405
406 /* Re-initiate plots if window open */
407 if( freqplots_window != NULL )
408 {
409 Main_Freqplots_Activate();
410 Start_Frequency_Loop();
411 }
412
413 return( FALSE );
414 } /* Open_Input_File() */
415
416 /*------------------------------------------------------------------------*/
417
sig_handler(int signal)418 static void sig_handler( int signal )
419 {
420 switch( signal )
421 {
422 case SIGINT:
423 fprintf( stderr, _("xnec2c: exiting via user interrupt\n") );
424 break;
425
426 case SIGSEGV:
427 fprintf( stderr, _("xnec2c: segmentation fault, exiting\n") );
428 break;
429
430 case SIGFPE:
431 fprintf( stderr, _("xnec2c: floating point exception, exiting\n") );
432 break;
433
434 case SIGABRT:
435 fprintf( stderr, _("xnec2c: abort signal received, exiting\n") );
436 break;
437
438 case SIGTERM:
439 fprintf( stderr, _("xnec2c: termination request received, exiting\n") );
440 break;
441
442 case SIGCHLD:
443 {
444 int idx;
445 pid_t pid = getpid();
446
447 if( !FORKED )
448 {
449 fprintf( stderr,
450 _("xnec2c: not forked, ignoring SIGCHLD from pid %d\n"), pid );
451 return;
452 }
453 else
454 {
455 for( idx = 0; idx < calc_data.num_jobs; idx++ )
456 if( forked_proc_data[idx]->child_pid == pid )
457 {
458 fprintf( stderr, _("xnec2c: child process pid %d exited\n"), pid );
459 if( isFlagSet(MAIN_QUIT) ) return;
460 else break;
461 }
462 return;
463 }
464 }
465
466 default:
467 fprintf( stderr, _("xnec2c: default exit with signal: %d\n"), signal );
468 } /* switch( signal ) */
469
470 /* Kill child processes */
471 if( FORKED && !CHILD )
472 while( num_child_procs )
473 {
474 num_child_procs--;
475 kill( forked_proc_data[num_child_procs]->child_pid, SIGKILL );
476 }
477
478 Close_File( &input_fp );
479
480 if( CHILD ) _exit( 0 );
481 else exit( signal );
482
483 } /* End of sig_handler() */
484
485 /*------------------------------------------------------------------------*/
486
487 /* Tests for child process */
488 gboolean
isChild(void)489 isChild(void)
490 {
491 return( child_pid == (pid_t)(0) );
492 }
493
494 /*------------------------------------------------------------------------*/
495
496