1 |
27 |
unneback |
# {{{ Banner
|
2 |
|
|
|
3 |
|
|
# ============================================================================
|
4 |
|
|
#
|
5 |
|
|
# ecosynth.tcl
|
6 |
|
|
#
|
7 |
|
|
# The eCos synthetic target I/O auxiliary
|
8 |
|
|
#
|
9 |
|
|
# ============================================================================
|
10 |
|
|
# ####COPYRIGHTBEGIN####
|
11 |
|
|
#
|
12 |
|
|
# ----------------------------------------------------------------------------
|
13 |
|
|
# Copyright (C) 2002 Bart Veer
|
14 |
|
|
#
|
15 |
|
|
# This file is part of the eCos host tools.
|
16 |
|
|
#
|
17 |
|
|
# This program is free software; you can redistribute it and/or modify it
|
18 |
|
|
# under the terms of the GNU General Public License as published by the Free
|
19 |
|
|
# Software Foundation; either version 2 of the License, or (at your option)
|
20 |
|
|
# any later version.
|
21 |
|
|
#
|
22 |
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
23 |
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
24 |
|
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
25 |
|
|
# more details.
|
26 |
|
|
#
|
27 |
|
|
# You should have received a copy of the GNU General Public License along with
|
28 |
|
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
29 |
|
|
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
30 |
|
|
#
|
31 |
|
|
# ----------------------------------------------------------------------------
|
32 |
|
|
#
|
33 |
|
|
# ####COPYRIGHTEND####
|
34 |
|
|
# ============================================================================
|
35 |
|
|
# #####DESCRIPTIONBEGIN####
|
36 |
|
|
#
|
37 |
|
|
# Author(s): bartv
|
38 |
|
|
# Contact(s): bartv
|
39 |
|
|
# Date: 2002/08/05
|
40 |
|
|
# Version: 0.01
|
41 |
|
|
# Description:
|
42 |
|
|
# The main script for the eCos synthetic target auxiliary. This should
|
43 |
|
|
# only ever be invoked from inside ecosynth.
|
44 |
|
|
#
|
45 |
|
|
# ####DESCRIPTIONEND####
|
46 |
|
|
# ============================================================================
|
47 |
|
|
|
48 |
|
|
# }}}
|
49 |
|
|
|
50 |
|
|
# {{{ Overview
|
51 |
|
|
|
52 |
|
|
# When an eCos synthetic target application runs it will usually
|
53 |
|
|
# fork/execve an auxiliary program, ecosynth, to provide certain
|
54 |
|
|
# I/O functionality. This happens as part of hardware initialization.
|
55 |
|
|
#
|
56 |
|
|
# The ecosynth executable in turn runs this ecosynth.tcl script which
|
57 |
|
|
# either does most of the real work or delegates it to other scripts.
|
58 |
|
|
# Those other scripts may in turn exec other programs to perform any
|
59 |
|
|
# I/O operations that cannot easily be done at the Tcl level. For
|
60 |
|
|
# example performing low-level ethernet operations generally requires
|
61 |
|
|
# working at the C level or equivalent, so a separate executable would
|
62 |
|
|
# be appropriate. The ecosynth executable will transfer control to
|
63 |
|
|
# this script after the appinit() call, which should have performed
|
64 |
|
|
# certain initialization steps.
|
65 |
|
|
#
|
66 |
|
|
# 1) the Tcl interpreter will be fully initialized.
|
67 |
|
|
#
|
68 |
|
|
# 2) usually Tk will have been loaded and initialized as well. This
|
69 |
|
|
# can be suppressed using a command-line option -nw.
|
70 |
|
|
#
|
71 |
|
|
# 3) there will be a namespace synth:: for use by ecosynth.tcl
|
72 |
|
|
# Auxiliary scripts are expected to use their own namespace
|
73 |
|
|
# where possible.
|
74 |
|
|
#
|
75 |
|
|
# 4) there will be two channels synth::_channel_from_app and
|
76 |
|
|
# synth::_channel_to_app. These correspond to a pipe between
|
77 |
|
|
# the eCos application and the auxiliary. The application will
|
78 |
|
|
# send requests using this pipe and expect replies. I/O
|
79 |
|
|
# operations are always initiated by a request from the
|
80 |
|
|
# application, but the auxiliary can raise an interrupt via
|
81 |
|
|
# the SIGIO signal.
|
82 |
|
|
#
|
83 |
|
|
# Other standard channels stdin, stdout and stderr will have
|
84 |
|
|
# been inherited from the eCos application.
|
85 |
|
|
#
|
86 |
|
|
# 5) some Tcl commands implemented in C will have been added to the
|
87 |
|
|
# interpreter. The most notable is synth::_send_SIGIO, used to
|
88 |
|
|
# raise an interrupt within the application.
|
89 |
|
|
#
|
90 |
|
|
# 6) similarly some variables will have been added to the interpreter.
|
91 |
|
|
#
|
92 |
|
|
#
|
93 |
|
|
# Configuring everything can get somewhat complicated. It is the eCos
|
94 |
|
|
# application that knows what I/O facilities within the auxiliary it
|
95 |
|
|
# wants to access, but it may not know all the details. The eCos
|
96 |
|
|
# component architecture makes things a bit more complicated still,
|
97 |
|
|
# generic code such as this ecosynth.tcl script has no way of knowing
|
98 |
|
|
# what I/O facilities might be provided by some package or other.
|
99 |
|
|
#
|
100 |
|
|
# For example, a target-side ethernet driver may want to send outgoing
|
101 |
|
|
# ethernet packets to some script or program on the host and receive
|
102 |
|
|
# incoming ethernet packets. However it does not necessarily know what
|
103 |
|
|
# the host-side should do with those ethernet packets, e.g. use a
|
104 |
|
|
# spare Linux ethernet device, use the Linux kernel's ethertap
|
105 |
|
|
# facility, ... Although that kind of information could be handled by
|
106 |
|
|
# target-side configury, host-side configuration files will often be
|
107 |
|
|
# more appropriate. Specifically it would allow a single eCos
|
108 |
|
|
# synthetic target application to run in a variety of environments,
|
109 |
|
|
# using different ways to provide the I/O, with no need to do any
|
110 |
|
|
# reconfiguring or rebuilding of the target side.
|
111 |
|
|
#
|
112 |
|
|
#
|
113 |
|
|
# The basic approach that is taken is:
|
114 |
|
|
#
|
115 |
|
|
# 1) the eCos application tells the auxiliary what I/O facilities
|
116 |
|
|
# it is interested in. This should happen as a result
|
117 |
|
|
# of static constructors or of device driver initialization
|
118 |
|
|
# routines. The application has no control over the implementation
|
119 |
|
|
# of the I/O facilities, it just expects something on the other
|
120 |
|
|
# end to respond sensibly to requests.
|
121 |
|
|
#
|
122 |
|
|
# For example, a synthetic target ethernet driver will supply
|
123 |
|
|
# an initialization routine via its NETDEVTAB_ENTRY. This
|
124 |
|
|
# routine will send a request to the auxiliary asking for a
|
125 |
|
|
# device of type ethernet, id "eth0", provided by
|
126 |
|
|
# CYGPKG_DEVS_ETH_ECOSYNTH, version current. The auxiliary will
|
127 |
|
|
# now attempt to load a suitable Tcl script ethernet.tcl from a
|
128 |
|
|
# location determined using the package name and version.
|
129 |
|
|
#
|
130 |
|
|
# 2) there is a primary target definition file which can be
|
131 |
|
|
# specified as the final argument on the command line, with
|
132 |
|
|
# "default" being the default value. The code will look for
|
133 |
|
|
# this file with or without a .tdf extension, first in the
|
134 |
|
|
# current directory, then in ~/.ecos/synth/. This file is
|
135 |
|
|
# actually a Tcl script that gets executed in the current
|
136 |
|
|
# interpreter. However typically it will only contain
|
137 |
|
|
# entries such as:
|
138 |
|
|
#
|
139 |
|
|
# synth_device eth0 {
|
140 |
|
|
# ...
|
141 |
|
|
# }
|
142 |
|
|
#
|
143 |
|
|
# 3) There are additional optional configuration files
|
144 |
|
|
# ~/.ecos/synth/initrc.tcl and ~/.ecos/synth/mainrc.tcl which can
|
145 |
|
|
# be used for further customization. initrc.tcl will get run
|
146 |
|
|
# early on, mainrc.tcl will get run once initialization is
|
147 |
|
|
# complete. Specifically the target-side code will send an
|
148 |
|
|
# appropriate request after invoking all the static constructors.
|
149 |
|
|
# At this time the auxiliary will run mainrc.tcl, and in addition
|
150 |
|
|
# it may issue warnings about unused arguments etc.
|
151 |
|
|
#
|
152 |
|
|
# 4) there may also be separate configuration files for GUI
|
153 |
|
|
# preferences etc. These are distinct from the initrc and
|
154 |
|
|
# mainrc files in that they are generated rather than
|
155 |
|
|
# hand-written.
|
156 |
|
|
|
157 |
|
|
# }}}
|
158 |
|
|
# {{{ Basic initialization
|
159 |
|
|
|
160 |
|
|
# ----------------------------------------------------------------------------
|
161 |
|
|
# There should be two channels corresponding to the pipe between the
|
162 |
|
|
# eCos application and the auxiliary. These should be configured
|
163 |
|
|
# appropriately. If either channel does not exist then that is a
|
164 |
|
|
# very good indication that the system is not properly initialized,
|
165 |
|
|
# i.e. that this script is not being run by the ecosynth executable
|
166 |
|
|
# and by implication that no eCos application is involved.
|
167 |
|
|
|
168 |
|
|
if {([info exists synth::_channel_to_app] == 0) ||
|
169 |
|
|
([info exists synth::_channel_from_app] == 0) ||
|
170 |
|
|
([info exists synth::_ecosynth_version] == 0) ||
|
171 |
|
|
([info exists synth::_ppid] == 0) ||
|
172 |
|
|
([info exists synth::_ecosynth_repository] == 0) ||
|
173 |
|
|
([info exists synth::_ecosynth_libexecdir] == 0) ||
|
174 |
|
|
([info exists synth::_ecosynth_package_dir] == 0) ||
|
175 |
|
|
([info exists synth::_ecosynth_package_version] == 0) ||
|
176 |
|
|
([info exists synth::_ecosynth_package_install] == 0) ||
|
177 |
|
|
([info commands synth::_send_SIGIO] == "") ||
|
178 |
|
|
([info commands synth::_send_SIGKILL] == "") } {
|
179 |
|
|
|
180 |
|
|
puts stderr "ecosynth.tcl: the current interpreter has not been properly initialized."
|
181 |
|
|
puts stderr " This script should only be invoked by the ecosynth executable when"
|
182 |
|
|
puts stderr " an eCos synthetic target application is run."
|
183 |
|
|
exit 1
|
184 |
|
|
}
|
185 |
|
|
|
186 |
|
|
# Is the script currently being executed the most recent version?
|
187 |
|
|
# This check should only happen if an environment variable
|
188 |
|
|
# ECOSYNTH_DEVEL is set, because the installed tools may have come
|
189 |
|
|
# from somewhere other than the current repository.
|
190 |
|
|
if { [info exists ::env(ECOSYNTH_DEVEL)] } {
|
191 |
|
|
set _orig_name [file join $synth::_ecosynth_repository $synth::_ecosynth_package_dir $synth::_ecosynth_package_version \
|
192 |
|
|
"host" [file tail [info script]]]
|
193 |
|
|
if { [file exists $_orig_name] && [file readable $_orig_name] && ($_orig_name != [info script]) } {
|
194 |
|
|
if { [file mtime $_orig_name] >= [file mtime [info script]] } {
|
195 |
|
|
puts "$_orig_name is more recent than install: executing that."
|
196 |
|
|
source $_orig_name
|
197 |
|
|
return
|
198 |
|
|
}
|
199 |
|
|
}
|
200 |
|
|
unset _orig_name
|
201 |
|
|
}
|
202 |
|
|
|
203 |
|
|
fconfigure $synth::_channel_to_app -buffering none
|
204 |
|
|
fconfigure $synth::_channel_from_app -encoding binary
|
205 |
|
|
fconfigure $synth::_channel_to_app -encoding binary
|
206 |
|
|
fconfigure $synth::_channel_from_app -translation binary
|
207 |
|
|
fconfigure $synth::_channel_to_app -translation binary
|
208 |
|
|
|
209 |
|
|
# Define additional globals and procedures inside the synth:: namespace.
|
210 |
|
|
# Variables and functions that begin with an _ are considered internal
|
211 |
|
|
# and should not be used directly.
|
212 |
|
|
namespace eval synth {
|
213 |
|
|
|
214 |
|
|
# Unfortunately the name of the eCos executable is lost at this stage.
|
215 |
|
|
# Within the eCos application it was held in argv[0], but that has been
|
216 |
|
|
# overridden with the name of the auxiliary. However we have access to the
|
217 |
|
|
# parent process id so we can use /proc to get the required information.
|
218 |
|
|
variable ecos_appname ""
|
219 |
|
|
catch {
|
220 |
|
|
set synth::ecos_appname [file readlink "/proc/[set synth::_ppid]/exe"]
|
221 |
|
|
set synth::ecos_appname [file tail $synth::ecos_appname]
|
222 |
|
|
}
|
223 |
|
|
|
224 |
|
|
# The install location can be determined from the script name.
|
225 |
|
|
# This is used for e.g. loading bitmaps, even if ECOSYNTH_DEVEL
|
226 |
|
|
# is set, because some of the files may be generated.
|
227 |
|
|
# ECOSYNTH_DEVEL only affects Tcl scripts.
|
228 |
|
|
variable install_dir [file join $synth::_ecosynth_libexecdir "ecos" $synth::_ecosynth_package_install]
|
229 |
|
|
|
230 |
|
|
# Is the eCos application still running? This is worth keeping
|
231 |
|
|
# track of so that send_reply and raise_interrupt do not try to
|
232 |
|
|
# interact with a program that is no longer running.
|
233 |
|
|
variable ecos_running 1
|
234 |
|
|
|
235 |
|
|
# This variable is used to enter the event loop
|
236 |
|
|
variable _ecosynth_exit 0
|
237 |
|
|
|
238 |
|
|
# Is GUI mode enabled?
|
239 |
|
|
variable flag_gui [expr { "" != [info commands "tk"] } ]
|
240 |
|
|
|
241 |
|
|
# When running in GUI mode the GUI should stay up even after the application
|
242 |
|
|
# has exited, so that the user can take a good look around. When running in
|
243 |
|
|
# non-GUI mode this program should exit as soon it has finished cleaning up.
|
244 |
|
|
variable flag_immediate_exit [expr { 0 == $synth::flag_gui} ]
|
245 |
|
|
|
246 |
|
|
# Is the GUI ready to accept output?
|
247 |
|
|
variable flag_gui_ready 0
|
248 |
|
|
|
249 |
|
|
# Flags and variables related to command-line arguments
|
250 |
|
|
variable flag_help 0
|
251 |
|
|
variable flag_keep_going 0
|
252 |
|
|
variable flag_no_rc 0
|
253 |
|
|
variable flag_verbose 0
|
254 |
|
|
variable flag_debug 0
|
255 |
|
|
variable logfile ""
|
256 |
|
|
variable target_definition ""
|
257 |
|
|
variable geometry "<none>"
|
258 |
|
|
}
|
259 |
|
|
|
260 |
|
|
# }}}
|
261 |
|
|
# {{{ Hooks & atexit support
|
262 |
|
|
|
263 |
|
|
# ----------------------------------------------------------------------------
|
264 |
|
|
# A lot of the flexibility of ecosynth is provided by hooks. Device scripts
|
265 |
|
|
# and, more importantly, the per-user initrc and mainrc scripts can install
|
266 |
|
|
# hooks that get called when an event occurs, for example when the eCos
|
267 |
|
|
# applications attempts to transmit an ethernet packet.
|
268 |
|
|
#
|
269 |
|
|
# A special hook is used to implement atexit handling. This involves redefining
|
270 |
|
|
# the "exit" command so that it will invoke the appropriate hooks first.
|
271 |
|
|
|
272 |
|
|
namespace eval synth {
|
273 |
|
|
# All hooks are held in an array, indexed by the hook name, with each
|
274 |
|
|
# array entry being a list of functions to be invoked.
|
275 |
|
|
array set _hooks [list]
|
276 |
|
|
|
277 |
|
|
proc hook_define { name } {
|
278 |
|
|
if { [info exists synth::_hooks($name)] } {
|
279 |
|
|
synth::report_error "Attempt to create hook $name which already exists.\n"
|
280 |
|
|
} else {
|
281 |
|
|
set synth::_hooks($name) [list]
|
282 |
|
|
}
|
283 |
|
|
}
|
284 |
|
|
|
285 |
|
|
proc hook_defined { name } {
|
286 |
|
|
return [info exists synth::_hooks($name)]
|
287 |
|
|
}
|
288 |
|
|
|
289 |
|
|
proc hook_add { name function } {
|
290 |
|
|
if { ! [info exists synth::_hooks($name)] } {
|
291 |
|
|
synth::report_error "Attempt to attach a function to an unknown hook $name\n"
|
292 |
|
|
set synth::_hooks($name) [list]
|
293 |
|
|
}
|
294 |
|
|
lappend synth::_hooks($name) $function
|
295 |
|
|
}
|
296 |
|
|
|
297 |
|
|
proc hook_call { name args } {
|
298 |
|
|
if { ! [info exists synth::_hooks($name) ] } {
|
299 |
|
|
synth::report_error "Attempt to invoke unknown hook $name\n"
|
300 |
|
|
} else {
|
301 |
|
|
foreach function $synth::_hooks($name) {
|
302 |
|
|
$function $args
|
303 |
|
|
}
|
304 |
|
|
}
|
305 |
|
|
}
|
306 |
|
|
|
307 |
|
|
# Define an initial set of hooks
|
308 |
|
|
synth::hook_define "exit" ;# The auxiliary is exiting
|
309 |
|
|
synth::hook_define "ecos_exit" ;# The eCos application has exited
|
310 |
|
|
synth::hook_define "ecos_initialized" ;# eCos static constructors have run
|
311 |
|
|
synth::hook_define "help" ;# --help
|
312 |
|
|
}
|
313 |
|
|
|
314 |
|
|
# Rename the builtin exit command so that it can still be accessed.
|
315 |
|
|
rename exit _hook_real_exit
|
316 |
|
|
|
317 |
|
|
# And define a replacement for exit which will invoke the appropriate
|
318 |
|
|
# hook. Care has to be taken in case of recursive exit calls, each
|
319 |
|
|
# hook function is only called once.
|
320 |
|
|
|
321 |
|
|
proc exit { { code 0 } } {
|
322 |
|
|
while { [llength $synth::_hooks(exit)] > 0 } {
|
323 |
|
|
set handler [lindex $synth::_hooks(exit) end]
|
324 |
|
|
set synth::_hooks(exit) [lrange $synth::_hooks(exit) 0 end-1]
|
325 |
|
|
|
326 |
|
|
# For now assume no errors - it is not clear what could be done
|
327 |
|
|
# about them anyway.
|
328 |
|
|
catch { eval $handler [list]}
|
329 |
|
|
}
|
330 |
|
|
_hook_real_exit $code
|
331 |
|
|
}
|
332 |
|
|
|
333 |
|
|
# }}}
|
334 |
|
|
# {{{ Output
|
335 |
|
|
|
336 |
|
|
# ----------------------------------------------------------------------------
|
337 |
|
|
# The usual set of utilities for issuing warnings, errors, ...
|
338 |
|
|
#
|
339 |
|
|
# There are three possibilities to consider:
|
340 |
|
|
#
|
341 |
|
|
# 1) running in text-only mode. The output should just go to stdout
|
342 |
|
|
#
|
343 |
|
|
# 2) running in GUI mode and the text window exists. Just update the
|
344 |
|
|
# window
|
345 |
|
|
#
|
346 |
|
|
# 3) running in GUI mode but the text window is not yet ready. The
|
347 |
|
|
# output needs to be buffered for now, and will be flushed
|
348 |
|
|
# later.
|
349 |
|
|
#
|
350 |
|
|
# Also, if for some reason this program exits while there is output still
|
351 |
|
|
# buffered that output should also go to stdout.
|
352 |
|
|
#
|
353 |
|
|
# If any errors occur during initialization, e.g. an invalid device script
|
354 |
|
|
# or user initialization scripts, those get reported and an error count
|
355 |
|
|
# is maintained. When the eCos application reports that initialization is
|
356 |
|
|
# complete it will be sent back a status for the auxiliary, and will
|
357 |
|
|
# exit if things have not started up correctly. This tries to ensure that
|
358 |
|
|
# if there are multiple errors the user sees all of them.
|
359 |
|
|
|
360 |
|
|
namespace eval synth {
|
361 |
|
|
|
362 |
|
|
variable _pending_output [list]
|
363 |
|
|
variable _logfd ""
|
364 |
|
|
variable _error_count 0
|
365 |
|
|
|
366 |
|
|
proc logfile_open { } {
|
367 |
|
|
synth::report "Opening logfile $synth::logfile"
|
368 |
|
|
set msg ""
|
369 |
|
|
if { [catch { set synth::_logfd [open $synth::logfile "w"] } msg ] } {
|
370 |
|
|
synth::report_error "Unable to open logfile \"$synth::logfile\"\n $msg\n"
|
371 |
|
|
}
|
372 |
|
|
}
|
373 |
|
|
|
374 |
|
|
# A default implementation of output. This gets overwritten later when running
|
375 |
|
|
# in GUI mode, so if GUI mode is enabled then this proc must be called before
|
376 |
|
|
# the GUI is ready and the data must be queued.
|
377 |
|
|
proc output { msg filter } {
|
378 |
|
|
if { ! $synth::flag_gui } {
|
379 |
|
|
# If a logfile exists, output normally goes there rather than
|
380 |
|
|
# to standard output. The exception is for errors which
|
381 |
|
|
# always go to stderr, in addition to the logfile.
|
382 |
|
|
if { "" != $synth::_logfd } {
|
383 |
|
|
puts -nonewline $synth::_logfd $msg
|
384 |
|
|
if { "error" == $filter } {
|
385 |
|
|
puts -nonewline stderr $msg
|
386 |
|
|
}
|
387 |
|
|
} else {
|
388 |
|
|
if { "error" == $filter } {
|
389 |
|
|
puts -nonewline stderr $msg
|
390 |
|
|
} else {
|
391 |
|
|
puts -nonewline $msg
|
392 |
|
|
}
|
393 |
|
|
}
|
394 |
|
|
} else {
|
395 |
|
|
lappend synth::_pending_output [list $msg $filter]
|
396 |
|
|
}
|
397 |
|
|
}
|
398 |
|
|
|
399 |
|
|
# Invoked by the text window code once everything is ready
|
400 |
|
|
# and synth::output has been redefined.
|
401 |
|
|
proc _flush_output { } {
|
402 |
|
|
foreach msg $synth::_pending_output {
|
403 |
|
|
synth::output [lindex $msg 0] [lindex $msg 1]
|
404 |
|
|
}
|
405 |
|
|
set synth::_pending_output [list]
|
406 |
|
|
}
|
407 |
|
|
|
408 |
|
|
# Cope with early exits. This will only have an effect if
|
409 |
|
|
# _flush_output has not been called yet, and by implication
|
410 |
|
|
# if synth::output has not yet been redefined.
|
411 |
|
|
proc _exit_flush_output { arg_list } {
|
412 |
|
|
if { 0 != [llength $synth::_pending_output] } {
|
413 |
|
|
set synth::flag_gui 0
|
414 |
|
|
synth::_flush_output
|
415 |
|
|
}
|
416 |
|
|
}
|
417 |
|
|
synth::hook_add "exit" synth::_exit_flush_output
|
418 |
|
|
|
419 |
|
|
proc report { msg } {
|
420 |
|
|
synth::output $msg "report"
|
421 |
|
|
}
|
422 |
|
|
|
423 |
|
|
proc report_warning { msg } {
|
424 |
|
|
synth::output "Warning: $msg" "warning"
|
425 |
|
|
}
|
426 |
|
|
|
427 |
|
|
proc report_error { msg } {
|
428 |
|
|
incr synth::_error_count
|
429 |
|
|
synth::output "Error: $msg" "error"
|
430 |
|
|
}
|
431 |
|
|
|
432 |
|
|
# Internal errors indicate a serious problem within ecosynth or
|
433 |
|
|
# a device-specific script. For now this results in output to
|
434 |
|
|
# stderr, a backtrace, and termination of the auxiliary, which
|
435 |
|
|
# should also cause the eCos application to shut down.
|
436 |
|
|
#
|
437 |
|
|
# An alternative approach would involve calling ::error and
|
438 |
|
|
# benefitting from its backtrace generation, but there are various
|
439 |
|
|
# places where it makes to sense to catch problems and call
|
440 |
|
|
# synth::error rather than internal_error
|
441 |
|
|
proc internal_error { msg } {
|
442 |
|
|
puts stderr "ecosynth, an internal error has occurred:"
|
443 |
|
|
puts stderr " $msg"
|
444 |
|
|
puts stderr "---------- backtrace -------------------------------------------------"
|
445 |
|
|
for { set level [info level] } { $level > 0 } { incr level -1 } {
|
446 |
|
|
puts stderr [info level $level]
|
447 |
|
|
}
|
448 |
|
|
puts stderr "----------------------------------------------------------------------"
|
449 |
|
|
puts stderr "ecosynth, exiting."
|
450 |
|
|
exit 1
|
451 |
|
|
}
|
452 |
|
|
|
453 |
|
|
# Dummy implementations of the exported filter routines, in case a script
|
454 |
|
|
# tries to create a filter even when not running in graphical mode
|
455 |
|
|
variable _dummy_filters [list]
|
456 |
|
|
|
457 |
|
|
proc filter_exists { name } {
|
458 |
|
|
set result 0
|
459 |
|
|
if { -1 != [lsearch -exact $synth::_dummy_filters $name] } {
|
460 |
|
|
set result 1
|
461 |
|
|
}
|
462 |
|
|
return $result
|
463 |
|
|
}
|
464 |
|
|
|
465 |
|
|
proc filter_get_list { } {
|
466 |
|
|
return $synth::_dummy_filters
|
467 |
|
|
}
|
468 |
|
|
|
469 |
|
|
proc filter_add { name args } {
|
470 |
|
|
if { [synth::filter_exists $name] } {
|
471 |
|
|
synth::internal_error "attempt to install filter $name twice.\n"
|
472 |
|
|
}
|
473 |
|
|
lappend synth::_dummy_filters $name
|
474 |
|
|
}
|
475 |
|
|
}
|
476 |
|
|
|
477 |
|
|
# }}}
|
478 |
|
|
# {{{ Argument processing and global options
|
479 |
|
|
|
480 |
|
|
# ----------------------------------------------------------------------------
|
481 |
|
|
# Argument processing. The eCos application will usually just pass its
|
482 |
|
|
# command line arguments to the auxiliary. Four special arguments will
|
483 |
|
|
# have been examined already:
|
484 |
|
|
#
|
485 |
|
|
# -io, --io
|
486 |
|
|
# I/O facilities, i.e. the auxiliary should run
|
487 |
|
|
# -ni, -nio, --ni, --nio
|
488 |
|
|
# No I/O facilities, i.e. the auxiliary should not be run.
|
489 |
|
|
# -nw, --nw, --no-windows
|
490 |
|
|
# No windows, i.e. disable the GUI
|
491 |
|
|
# -w, --w, --windows
|
492 |
|
|
# Enable the GUI
|
493 |
|
|
#
|
494 |
|
|
# There are a number of additional flags available as standard:
|
495 |
|
|
#
|
496 |
|
|
# -v, --version
|
497 |
|
|
# The usual
|
498 |
|
|
# -h, --help
|
499 |
|
|
# Ditto
|
500 |
|
|
# -k, --k, --keep-going
|
501 |
|
|
# Ignore errors as much as possible
|
502 |
|
|
# -nr, --no-rc
|
503 |
|
|
# Skip the initrc and mainrc files
|
504 |
|
|
# -x, --exit
|
505 |
|
|
# The auxiliary should exit at the same time as the eCos application.
|
506 |
|
|
# -nx, --no-exit
|
507 |
|
|
# Inverse of the above
|
508 |
|
|
# -V, --verbose
|
509 |
|
|
# The usual
|
510 |
|
|
# --debug
|
511 |
|
|
# Not intended for end users
|
512 |
|
|
# -l <file>, -l=<file>, --logfile <file>, --logfile=<file>
|
513 |
|
|
# Send all output to the specified file. In GUI mode this is in addition
|
514 |
|
|
# to the main text window. In non-GUI mode this is instead of stdout.
|
515 |
|
|
# -t <file>, -t=<file>, --target <file>, --target=<file>
|
516 |
|
|
# Specify the target definition file.
|
517 |
|
|
#
|
518 |
|
|
# Many X applications accept a common set of options, e.g. -display,
|
519 |
|
|
# -geometry, etc. Although usually Tk will process these, there are
|
520 |
|
|
# some problems - see ecosynth.c, ecosynth_appinit() for details.
|
521 |
|
|
# Hence -geometry has to be processed here.
|
522 |
|
|
#
|
523 |
|
|
# -geometry <geom>
|
524 |
|
|
#
|
525 |
|
|
#
|
526 |
|
|
# Some device-specific scripts may want to support additional
|
527 |
|
|
# command line arguments. This is somewhat messy, since the core
|
528 |
|
|
# code has no way of knowing what devices might be available and
|
529 |
|
|
# hence what the actual valid arguments are. It would be possible
|
530 |
|
|
# to just ignore any arguments that are not used by any device,
|
531 |
|
|
# but that could really confuse a user who has made a typo. Instead
|
532 |
|
|
# the code below keeps track of which arguments have been "consumed",
|
533 |
|
|
# allowing it to issue a warning about unconsumed arguments after
|
534 |
|
|
# initialization.
|
535 |
|
|
#
|
536 |
|
|
# Arguments can take the following forms:
|
537 |
|
|
#
|
538 |
|
|
# 1) -flag or --flag.
|
539 |
|
|
# 2) -name=value or --name=value
|
540 |
|
|
# 3) -name value or --name value
|
541 |
|
|
#
|
542 |
|
|
# There is a possibility of confusion if any of the values begin with a hyphen.
|
543 |
|
|
# It is hard to do anything about this without advance knowledge of what all
|
544 |
|
|
# the valid arguments are. Instead the user can avoid problems by using
|
545 |
|
|
# the -name=value variant on the command line.
|
546 |
|
|
#
|
547 |
|
|
# There is also possible confusion if a single argument can occur multiple
|
548 |
|
|
# times. If that is permitted then things can get rather messy, and
|
549 |
|
|
# the current API does not really handle it.
|
550 |
|
|
|
551 |
|
|
namespace eval synth {
|
552 |
|
|
|
553 |
|
|
# Keep track of all arguments which have not yet been consumed.
|
554 |
|
|
array set _argv_unconsumed [list]
|
555 |
|
|
for { set i 0 } { $i < $::argc } { incr i } {
|
556 |
|
|
set synth::_argv_unconsumed($i) [lindex $::argv $i]
|
557 |
|
|
}
|
558 |
|
|
|
559 |
|
|
# Provide a list of just those arguments that have not yet
|
560 |
|
|
# been consumed.
|
561 |
|
|
proc argv_get_unconsumed { } {
|
562 |
|
|
set result [list]
|
563 |
|
|
for { set i 0 } { $i < $::argc } {incr i } {
|
564 |
|
|
if { [info exists synth::_argv_unconsumed($i)] } {
|
565 |
|
|
lappend result $synth::_argv_unconsumed($i)
|
566 |
|
|
}
|
567 |
|
|
}
|
568 |
|
|
return $result
|
569 |
|
|
}
|
570 |
|
|
|
571 |
|
|
proc _argv_consume { index } {
|
572 |
|
|
if { [info exists synth::_argv_unconsumed($index)] } {
|
573 |
|
|
unset synth::_argv_unconsumed($index)
|
574 |
|
|
}
|
575 |
|
|
}
|
576 |
|
|
|
577 |
|
|
# Internal routine. Given a string of the form "-flag" or "-name=",
|
578 |
|
|
# return an index within argv or -1 if not found. As a side effect
|
579 |
|
|
# this "consumes" the argument.
|
580 |
|
|
proc _argv_lookup { name } {
|
581 |
|
|
set result -1
|
582 |
|
|
if { "=" != [string index $name end] } {
|
583 |
|
|
for { set i 0 } { $i < $::argc } { incr i } {
|
584 |
|
|
set arg [lindex $::argv $i]
|
585 |
|
|
if { [string equal $arg $name] || [string equal $arg "-[set name]"] } {
|
586 |
|
|
set result $i
|
587 |
|
|
synth::_argv_consume $i
|
588 |
|
|
break
|
589 |
|
|
}
|
590 |
|
|
}
|
591 |
|
|
} else {
|
592 |
|
|
set name [string range $name 0 end-1]
|
593 |
|
|
set len [string length $name]
|
594 |
|
|
for { set i 0 } { $i < $::argc } { incr i } {
|
595 |
|
|
set arg [lindex $::argv $i]
|
596 |
|
|
if { [string equal -length $len $arg $name] } {
|
597 |
|
|
if { "=" == [string index $arg $len] } {
|
598 |
|
|
set result $i
|
599 |
|
|
synth::_argv_consume $i
|
600 |
|
|
break;
|
601 |
|
|
} elseif { ([string length $arg] == $len) && ($i < ($::argc - 1)) } {
|
602 |
|
|
set result $i
|
603 |
|
|
synth::_argv_consume $i
|
604 |
|
|
synth::_argv_consume [expr $i + 1]
|
605 |
|
|
break
|
606 |
|
|
}
|
607 |
|
|
} elseif { [string equal -length [expr $len + 1] $arg "-[set name]"] } {
|
608 |
|
|
if { "=" == [string index $arg [expr $len + 1]] } {
|
609 |
|
|
set result $i
|
610 |
|
|
synth::_argv_consume $i
|
611 |
|
|
break
|
612 |
|
|
} elseif { ([string length $arg] == [expr $len + 1]) && ($i < ($::argc - 1)) } {
|
613 |
|
|
set result $i
|
614 |
|
|
synth::_argv_consume $i
|
615 |
|
|
synth::_argv_consume [expr $i + 1]
|
616 |
|
|
break
|
617 |
|
|
}
|
618 |
|
|
}
|
619 |
|
|
}
|
620 |
|
|
}
|
621 |
|
|
return $result
|
622 |
|
|
}
|
623 |
|
|
|
624 |
|
|
# Look for a given argument on the command line.
|
625 |
|
|
proc argv_defined { name } {
|
626 |
|
|
set result 0
|
627 |
|
|
set index [synth::_argv_lookup $name]
|
628 |
|
|
if { -1 != $index } {
|
629 |
|
|
set result 1
|
630 |
|
|
}
|
631 |
|
|
return $result
|
632 |
|
|
}
|
633 |
|
|
|
634 |
|
|
# Return the value associated with a given argument, which must be present.
|
635 |
|
|
proc argv_get_value { name } {
|
636 |
|
|
if { "=" != [string index $name end] } {
|
637 |
|
|
synth::internal_error "attempt to get a value for a simple flag argument \"$name\".\n"
|
638 |
|
|
}
|
639 |
|
|
set result ""
|
640 |
|
|
set index [synth::_argv_lookup $name]
|
641 |
|
|
if { -1 == $index } {
|
642 |
|
|
synth::internal_error "attempt to get the value associated with a non-existent argument \"$name\".\n"
|
643 |
|
|
}
|
644 |
|
|
set arg [lindex $::argv $index]
|
645 |
|
|
set len [string length $name]
|
646 |
|
|
if { [string equal -length $len $arg $name] } {
|
647 |
|
|
set result [string range $arg $len end]
|
648 |
|
|
} elseif { [string equal -length [expr $len + 1] $arg "-[set name]"] } {
|
649 |
|
|
set result [string range $arg [expr $len + 1] end]
|
650 |
|
|
} else {
|
651 |
|
|
set result [lindex $::argv [expr $index + 1]]
|
652 |
|
|
}
|
653 |
|
|
return $result
|
654 |
|
|
}
|
655 |
|
|
|
656 |
|
|
# -ni/-nio are not relevant. If present then they would have been handled
|
657 |
|
|
# within the eCos application, the auxiliary would not have been spawned,
|
658 |
|
|
# and this script would not be running.
|
659 |
|
|
|
660 |
|
|
# -io will have been processed by the eCos application.
|
661 |
|
|
# -nw, -w, and related options have been processed by the C code.
|
662 |
|
|
# Look them up anyway to consume them.
|
663 |
|
|
synth::_argv_lookup "-io"
|
664 |
|
|
synth::_argv_lookup "-nw"
|
665 |
|
|
synth::_argv_lookup "-no-windows"
|
666 |
|
|
synth::_argv_lookup "-w"
|
667 |
|
|
synth::_argv_lookup "-windows"
|
668 |
|
|
|
669 |
|
|
# Now cope with the other standard flags
|
670 |
|
|
if { [synth::argv_defined "-v"] || [synth::argv_defined "--version"] } {
|
671 |
|
|
# Just output the version message and exit. The eCos application
|
672 |
|
|
# will do the same. The version is obtained from configure.in,
|
673 |
|
|
# and also from the install directory. The synthetic target
|
674 |
|
|
# startup code will exit quietly if the auxiliary exits at this
|
675 |
|
|
# stage. This output should go via puts rather than the
|
676 |
|
|
# synth:: output routines, since the GUI will never appear if
|
677 |
|
|
# --version is specified.
|
678 |
|
|
puts "ecosynth: version $synth::_ecosynth_version"
|
679 |
|
|
puts " : install location [file dirname [info script]]"
|
680 |
|
|
exit 0
|
681 |
|
|
}
|
682 |
|
|
|
683 |
|
|
if { [synth::argv_defined "-l="] } {
|
684 |
|
|
set synth::logfile [synth::argv_get_value "-l="]
|
685 |
|
|
} elseif { [synth::argv_defined "-logfile="] } {
|
686 |
|
|
set synth::logfile [synth::argv_get_value "-logfile="]
|
687 |
|
|
}
|
688 |
|
|
if { "" != $synth::logfile } {
|
689 |
|
|
synth::logfile_open
|
690 |
|
|
}
|
691 |
|
|
|
692 |
|
|
# -h/--help would normally also result in an immediate exit. However,
|
693 |
|
|
# the device-specific scripts have not yet been loaded so there
|
694 |
|
|
# is no way of reporting their options. Hence the usage information
|
695 |
|
|
# is delayed until later. Suppressing GUI mode as a side effect is
|
696 |
|
|
# probably a good idea as well, that way the output appears in the
|
697 |
|
|
# current console window.
|
698 |
|
|
if { [synth::argv_defined "-h"] || [synth::argv_defined "-help"] } {
|
699 |
|
|
set synth::flag_help 1
|
700 |
|
|
set synth::flag_gui 0
|
701 |
|
|
}
|
702 |
|
|
|
703 |
|
|
if { [synth::argv_defined "-debug"] } {
|
704 |
|
|
set synth::flag_debug 1
|
705 |
|
|
}
|
706 |
|
|
|
707 |
|
|
if { [synth::argv_defined "-k"] || [synth::argv_defined "-keep-going"] } {
|
708 |
|
|
set synth::flag_keep_going 1
|
709 |
|
|
}
|
710 |
|
|
|
711 |
|
|
if { [synth::argv_defined "-nr"] || [synth::argv_defined "-no-rc"]} {
|
712 |
|
|
set synth::flag_no_rc 1
|
713 |
|
|
}
|
714 |
|
|
|
715 |
|
|
if { [synth::argv_defined "-x"] || [synth::argv_defined "-exit"] } {
|
716 |
|
|
set synth::flag_immediate_exit 1
|
717 |
|
|
} elseif { [synth::argv_defined "-nx"] || [synth::argv_defined "-no-exit"] } {
|
718 |
|
|
set synth::flag_immediate_exit 0
|
719 |
|
|
}
|
720 |
|
|
|
721 |
|
|
if { [synth::argv_defined "-V"] || [synth::argv_defined "-verbose"] } {
|
722 |
|
|
set synth::flag_verbose 1
|
723 |
|
|
}
|
724 |
|
|
|
725 |
|
|
if { [synth::argv_defined "-t="] } {
|
726 |
|
|
set synth::target_definition [synth::argv_get_value "-t="]
|
727 |
|
|
} elseif { [synth::argv_defined "-target="] } {
|
728 |
|
|
set synth::target_definition [synth::argv_get_value "-target="]
|
729 |
|
|
}
|
730 |
|
|
|
731 |
|
|
# Arguably -geometry should only be checked when the GUI is enabled,
|
732 |
|
|
# but doing so at all times is harmless.
|
733 |
|
|
# Note that '-geometry ""' means that any value held in the
|
734 |
|
|
# preferences file should be ignored. Hence the regexp below
|
735 |
|
|
# accepts the empty string, and treats it separately from
|
736 |
|
|
# uninitialized.
|
737 |
|
|
if { [synth::argv_defined "-geometry="] } {
|
738 |
|
|
set synth::geometry [synth::argv_get_value "-geometry="]
|
739 |
|
|
|
740 |
|
|
if { ![regexp -- {^([0-9]+x[0-9]+)?([+-][0-9]+[+-][0-9]+)?$} $synth::geometry] } {
|
741 |
|
|
synth::report_warning "Invalid geometry string $synth::geometry\n"
|
742 |
|
|
set synth::geometry "<none>"
|
743 |
|
|
}
|
744 |
|
|
}
|
745 |
|
|
|
746 |
|
|
if { $synth::flag_debug } {
|
747 |
|
|
synth::report \
|
748 |
|
|
"Results of initial command-line parsing:\n \
|
749 |
|
|
--help $synth::flag_help\n \
|
750 |
|
|
--keep-going $synth::flag_keep_going\n \
|
751 |
|
|
--no-rc $synth::flag_no_rc\n \
|
752 |
|
|
--exit $synth::flag_immediate_exit\n \
|
753 |
|
|
--verbose $synth::flag_verbose\n \
|
754 |
|
|
logfile $synth::logfile\n \
|
755 |
|
|
target definition $synth::target_definition\n \
|
756 |
|
|
geometry $synth::geometry\n \
|
757 |
|
|
unconsumed [synth::get_unconsumed_args]\n"
|
758 |
|
|
}
|
759 |
|
|
}
|
760 |
|
|
|
761 |
|
|
# }}}
|
762 |
|
|
# {{{ Create and populate ~/.ecos/synth
|
763 |
|
|
|
764 |
|
|
# ----------------------------------------------------------------------------
|
765 |
|
|
# If the per-user configuration directories do not exist yet, create them.
|
766 |
|
|
# Also install default initrc.tcl and mainrc.tcl files which do nothing, but
|
767 |
|
|
# which can be edited. If problems occur then the user gets a warning
|
768 |
|
|
# but execution proceeds.
|
769 |
|
|
#
|
770 |
|
|
# Some people may object to this automatic creation of directories and
|
771 |
|
|
# configuration files. However there is plenty of precedent, and the
|
772 |
|
|
# files involved are small. Messages are generated so that the user
|
773 |
|
|
# knows what has happened.
|
774 |
|
|
#
|
775 |
|
|
# Currently the default target definition file is not copied from
|
776 |
|
|
# the install tree into the per-user tree. Although some users will
|
777 |
|
|
# be happy having this file in ~/.ecos/synth, others may prefer it
|
778 |
|
|
# to be more visible in the current directory.
|
779 |
|
|
|
780 |
|
|
if { ![file exists "~/.ecos"] } {
|
781 |
|
|
synth::report "Creating new directory ~/.ecos for eCos configuration files.\n"
|
782 |
|
|
if { 0 != [catch { file mkdir "~/.ecos" }] } {
|
783 |
|
|
synth::report_warning "failed to create directory ~/.ecos\n"
|
784 |
|
|
}
|
785 |
|
|
}
|
786 |
|
|
if { [file exists "~/.ecos"] && [file isdirectory "~/.ecos"] && ![file exists "~/.ecos/synth"] } {
|
787 |
|
|
synth::report "Creating new directory ~/.ecos/synth for synthetic target configuration files.\n"
|
788 |
|
|
if { 0 != [catch { file mkdir "~/.ecos/synth" } ] } {
|
789 |
|
|
synth::report_warning "failed to create directory ~/.ecos/synth\n"
|
790 |
|
|
} else {
|
791 |
|
|
# initrc and mainrc are only copied when the directory is first created,
|
792 |
|
|
# so that users can delete them if unwanted - even though the
|
793 |
|
|
# default versions do nothing.
|
794 |
|
|
synth::report "Installing default configuration files ~/.ecos/synth/initrc.tcl and ~/.ecos/synth/mainrc.tcl\n"
|
795 |
|
|
catch { file copy -- [file join $synth::install_dir "user_initrc.tcl"] "~/.ecos/synth/initrc.tcl"}
|
796 |
|
|
catch { file copy -- [file join $synth::install_dir "user_mainrc.tcl"] "~/.ecos/synth/mainrc.tcl"}
|
797 |
|
|
}
|
798 |
|
|
}
|
799 |
|
|
|
800 |
|
|
# }}}
|
801 |
|
|
# {{{ Read target definition file
|
802 |
|
|
|
803 |
|
|
# ----------------------------------------------------------------------------
|
804 |
|
|
# Once the GUI is up and running it is possible to start reading in some
|
805 |
|
|
# configuration files. The first of these is the target definition file.
|
806 |
|
|
# Typically this would be ~/.ecos/synth/default.tdf. An alternative
|
807 |
|
|
# definition file can be specified on the command line with the
|
808 |
|
|
# -t argument, and the code will look in the current directory,
|
809 |
|
|
# in ~/.ecos/synth, and in the install tree.
|
810 |
|
|
#
|
811 |
|
|
# The purpose of the target definition file is to specify exactly
|
812 |
|
|
# how I/O gets implemented. For example the eCos application may
|
813 |
|
|
# want to access a network device eth0, but that device could be
|
814 |
|
|
# implemented in a variety of ways (e.g. a real ethernet device
|
815 |
|
|
# on the Linux host, or a fake device provided by the ethertap
|
816 |
|
|
# facility). It is the target definition file that provides
|
817 |
|
|
# this information.
|
818 |
|
|
#
|
819 |
|
|
# The file is of course just another Tcl script, running in the
|
820 |
|
|
# current interpreter. There seems little point in using a safe
|
821 |
|
|
# interpreter given the considerable number of other Tcl scripts
|
822 |
|
|
# that are being used, some of which need the ability to e.g.
|
823 |
|
|
# run other programs. The main command is synth_device which
|
824 |
|
|
# takes two arguments, a device name and some options for that
|
825 |
|
|
# device, e.g.:
|
826 |
|
|
#
|
827 |
|
|
# synth_device eth0 {
|
828 |
|
|
# use eth1
|
829 |
|
|
# }
|
830 |
|
|
#
|
831 |
|
|
# synth_device eth1 {
|
832 |
|
|
# use tap0
|
833 |
|
|
# }
|
834 |
|
|
#
|
835 |
|
|
# When the eCos device driver looks up eth0 this will cause a
|
836 |
|
|
# device-specific Tcl script to be loaded, which can examine
|
837 |
|
|
# this data.
|
838 |
|
|
#
|
839 |
|
|
# This code has no way of knowing what constitutes valid or invalid
|
840 |
|
|
# contents for an eth0 device, especially since the Tcl script that
|
841 |
|
|
# could provide such knowledge has not been loaded. Instead it is
|
842 |
|
|
# assumed that the contents is another set of Tcl commands such as
|
843 |
|
|
# "physical", which will of course not be defined so the Tcl interpreter
|
844 |
|
|
# will invoke "unknown" which is temporarily redefined here. This makes
|
845 |
|
|
# it possible for the device-specific commands to have arbitrary number
|
846 |
|
|
# of arguments, or to define Tcl fragments for hooks, or whatever.
|
847 |
|
|
#
|
848 |
|
|
# As with argument processing, the code attempts to keep track of
|
849 |
|
|
# which devices and options have been "consumed" and can issue
|
850 |
|
|
# warnings about any unused devices or options. This helps to track
|
851 |
|
|
# down typos and similar problems. These warnings are only output
|
852 |
|
|
# when running at verbose mode, since it is fairly normal practice
|
853 |
|
|
# to have a single target definition file which supports
|
854 |
|
|
# a number of different eCos applications with different I/O
|
855 |
|
|
# requirements.
|
856 |
|
|
|
857 |
|
|
namespace eval synth {
|
858 |
|
|
# A list of all devices specified in the target definition file.
|
859 |
|
|
# For now assume that a given device will only be specified once.
|
860 |
|
|
variable _tdf_devices [list]
|
861 |
|
|
|
862 |
|
|
# An array with additional details of each device. This will have
|
863 |
|
|
# entries such as _tdf_device_options(eth0,4), where the second
|
864 |
|
|
# argument is a per-device index. The value of each entry is
|
865 |
|
|
# a list of the actual command and its arguments. This use of
|
866 |
|
|
# an index allows for multiple instances of a per-device
|
867 |
|
|
# option.
|
868 |
|
|
array set _tdf_device_options [list]
|
869 |
|
|
|
870 |
|
|
# While reading in the device details it is necessary to keep track
|
871 |
|
|
# of the current device, if any. Otherwise the implementation of
|
872 |
|
|
# "unknown" will not be able to update _tdf_device_options. An index
|
873 |
|
|
# is needed as well.
|
874 |
|
|
variable _tdf_current_device ""
|
875 |
|
|
variable _tdf_current_index 0
|
876 |
|
|
|
877 |
|
|
# Keep track of which devices and options have been consumed
|
878 |
|
|
variable _tdf_consumed_devices [list]
|
879 |
|
|
variable _tdf_consumed_options [list]
|
880 |
|
|
|
881 |
|
|
proc tdf_has_device { name } {
|
882 |
|
|
return [expr -1 != [lsearch -exact $synth::_tdf_devices $name]]
|
883 |
|
|
}
|
884 |
|
|
|
885 |
|
|
proc tdf_get_devices { } {
|
886 |
|
|
return $synth::_tdf_devices
|
887 |
|
|
}
|
888 |
|
|
|
889 |
|
|
proc _tdf_get_option_index { devname option } {
|
890 |
|
|
synth::_tdf_consume_device $devname
|
891 |
|
|
for { set i 0 } { [info exists synth::_tdf_device_options($devname,$i)] } { incr i } {
|
892 |
|
|
if { $option == [lindex $synth::_tdf_device_options($devname,$i) 0] } {
|
893 |
|
|
synth::_tdf_consume_option $devname $i
|
894 |
|
|
return $i
|
895 |
|
|
}
|
896 |
|
|
}
|
897 |
|
|
return -1
|
898 |
|
|
}
|
899 |
|
|
|
900 |
|
|
proc _tdf_get_option_indices { devname option } {
|
901 |
|
|
synth::_tdf_consume_device $devname
|
902 |
|
|
set result [list]
|
903 |
|
|
for { set i 0 } { [info exists synth::_tdf_device_options($devname,$i)] } { incr i } {
|
904 |
|
|
if { $option == [lindex $synth::_tdf_device_options($devname,$i) 0] } {
|
905 |
|
|
synth::_tdf_consume_option $devname $i
|
906 |
|
|
lappend result $i
|
907 |
|
|
}
|
908 |
|
|
}
|
909 |
|
|
return $result
|
910 |
|
|
}
|
911 |
|
|
|
912 |
|
|
proc tdf_has_option { devname option } {
|
913 |
|
|
return [expr -1 != [synth::_tdf_get_option_index $devname $option]]
|
914 |
|
|
}
|
915 |
|
|
|
916 |
|
|
proc tdf_get_option { devname option } {
|
917 |
|
|
set index [synth::_tdf_get_option_index $devname $option]
|
918 |
|
|
if { -1 != $index } {
|
919 |
|
|
lappend synth::_tdf_consumed_options "$devname,$index"
|
920 |
|
|
return [lrange $synth::_tdf_device_options($devname,$index) 1 end]
|
921 |
|
|
} else {
|
922 |
|
|
return [list]
|
923 |
|
|
}
|
924 |
|
|
}
|
925 |
|
|
|
926 |
|
|
proc tdf_get_options { devname option } {
|
927 |
|
|
set result [list]
|
928 |
|
|
set indices [synth::_tdf_get_option_indices $devname $option]
|
929 |
|
|
foreach index $indices {
|
930 |
|
|
lappend result [lrange $synth::_tdf_device_options($devname,$index) 1 end]
|
931 |
|
|
}
|
932 |
|
|
return $result
|
933 |
|
|
}
|
934 |
|
|
|
935 |
|
|
proc tdf_get_all_options { devname } {
|
936 |
|
|
set result [list]
|
937 |
|
|
for { set i 0 } { [info exists synth::_tdf_device_options($devname,$i)] } { incr i } {
|
938 |
|
|
lappend synth::_tdf_consumed_options "$devname,$index"
|
939 |
|
|
lappend result $synth::_tdf_device_options($devname,$i)
|
940 |
|
|
}
|
941 |
|
|
return $result
|
942 |
|
|
}
|
943 |
|
|
|
944 |
|
|
proc _tdf_consume_device { name } {
|
945 |
|
|
if { -1 == [lsearch -exact $synth::_tdf_consumed_devices $name] } {
|
946 |
|
|
lappend synth::_tdf_consumed_devices $name
|
947 |
|
|
}
|
948 |
|
|
}
|
949 |
|
|
|
950 |
|
|
proc _tdf_consume_option { devname index } {
|
951 |
|
|
if { -1 == [lsearch -exact $synth::_tdf_consumed_options "$devname,$index"] } {
|
952 |
|
|
lappend synth::_tdf_consumed_options "$devname,$index"
|
953 |
|
|
}
|
954 |
|
|
}
|
955 |
|
|
|
956 |
|
|
proc tdf_get_unconsumed_devices { } {
|
957 |
|
|
set result [list]
|
958 |
|
|
foreach devname $synth::_tdf_devices {
|
959 |
|
|
if { -1 == [lsearch -exact $synth::_tdf_consumed_devices $devname] } {
|
960 |
|
|
lappend result $devname
|
961 |
|
|
}
|
962 |
|
|
}
|
963 |
|
|
return $result
|
964 |
|
|
}
|
965 |
|
|
|
966 |
|
|
proc tdf_get_unconsumed_options { } {
|
967 |
|
|
set result [list]
|
968 |
|
|
foreach devname $synth::_tdf_devices {
|
969 |
|
|
if { -1 == [lsearch -exact $synth::_tdf_consumed_devices $devname] } {
|
970 |
|
|
# Do not report all the options for a device that has not been consumed at all
|
971 |
|
|
continue
|
972 |
|
|
}
|
973 |
|
|
for { set i 0 } { [info exists synth::_tdf_device_options($devname,$i)] } { incr i } {
|
974 |
|
|
if { -1 == [lsearch -exact $synth::_tdf_consumed_options "$devname,$i"] } {
|
975 |
|
|
lappend result [list $devname $synth::_tdf_device_options($devname,$i)]
|
976 |
|
|
}
|
977 |
|
|
}
|
978 |
|
|
}
|
979 |
|
|
return $result
|
980 |
|
|
}
|
981 |
|
|
}
|
982 |
|
|
|
983 |
|
|
# Look for the target definition file.
|
984 |
|
|
set _tdf $synth::target_definition
|
985 |
|
|
if { "" == $_tdf } {
|
986 |
|
|
set _tdf "default"
|
987 |
|
|
}
|
988 |
|
|
set _config_file ""
|
989 |
|
|
|
990 |
|
|
set _dirlist [list [pwd] "~/.ecos/synth" $synth::install_dir]
|
991 |
|
|
foreach _dir $_dirlist {
|
992 |
|
|
set _candidate "[file join $_dir $_tdf].tdf" ; # file join does the right thing for absolute paths
|
993 |
|
|
if { [file isfile $_candidate] } {
|
994 |
|
|
set _config_file $_candidate
|
995 |
|
|
break
|
996 |
|
|
} else {
|
997 |
|
|
set _candidate [file join $_dir $_tdf]
|
998 |
|
|
if { [file isfile $_candidate] } {
|
999 |
|
|
set _config_file $_candidate
|
1000 |
|
|
break
|
1001 |
|
|
}
|
1002 |
|
|
}
|
1003 |
|
|
}
|
1004 |
|
|
if { "" == $_config_file } {
|
1005 |
|
|
if { "" != $synth::target_definition } {
|
1006 |
|
|
# The user explicitly specified a file, so it must be read in.
|
1007 |
|
|
# If it cannot be found then that should be treated as an error.
|
1008 |
|
|
set msg "Unable to find target definition file $synth::target_definition\n"
|
1009 |
|
|
if { "absolute" != [file pathtype $synth::target_definition] } {
|
1010 |
|
|
append msg " Searched $_dirlist\n"
|
1011 |
|
|
}
|
1012 |
|
|
synth::report_error $msg
|
1013 |
|
|
exit 1
|
1014 |
|
|
} else {
|
1015 |
|
|
# This is a mild error, because default.tdf should be installed
|
1016 |
|
|
# below libexec. However the default file does not actually
|
1017 |
|
|
# define anything, it is just a set of comments, so there is
|
1018 |
|
|
# nothing to be gained by issuing a warning.
|
1019 |
|
|
}
|
1020 |
|
|
} else {
|
1021 |
|
|
|
1022 |
|
|
set synth::target_definition $_config_file
|
1023 |
|
|
|
1024 |
|
|
proc synth_device { name data } {
|
1025 |
|
|
if { "" != $synth::_tdf_current_device } {
|
1026 |
|
|
error "synth_device $name is nested inside $synth::_tdf_current_device\nNesting of synth_device entries is not allowed."
|
1027 |
|
|
}
|
1028 |
|
|
if { -1 != [lsearch -exact $synth::_tdf_devices $name] } {
|
1029 |
|
|
error "Duplicate entry for synth_device $name"
|
1030 |
|
|
}
|
1031 |
|
|
set synth::_tdf_current_device $name
|
1032 |
|
|
set synth::_tdf_current_index 0
|
1033 |
|
|
lappend synth::_tdf_devices $name
|
1034 |
|
|
eval $data
|
1035 |
|
|
# If the eval resulted in an error, propagate it immediately rather than attempt
|
1036 |
|
|
# any form of recovery. The downside is that only error per run will be
|
1037 |
|
|
# reported.
|
1038 |
|
|
set synth::_tdf_current_device ""
|
1039 |
|
|
}
|
1040 |
|
|
rename unknown _synth_unknown
|
1041 |
|
|
proc unknown { args } {
|
1042 |
|
|
if { "" == $synth::_tdf_current_device } {
|
1043 |
|
|
# An unknown command at the toplevel. Pass this to the
|
1044 |
|
|
# original "unknown" command, in the unlikely event that
|
1045 |
|
|
# the user really did want to autoload a library or do
|
1046 |
|
|
# something similar.
|
1047 |
|
|
eval _synth_unknown $arg
|
1048 |
|
|
return
|
1049 |
|
|
}
|
1050 |
|
|
|
1051 |
|
|
# Anything else is treated as an option within the synth_device
|
1052 |
|
|
set synth::_tdf_device_options($synth::_tdf_current_device,$synth::_tdf_current_index) $args
|
1053 |
|
|
incr synth::_tdf_current_index
|
1054 |
|
|
}
|
1055 |
|
|
|
1056 |
|
|
set _config_file_msg ""
|
1057 |
|
|
set _result [catch { source $_config_file } _config_file_msg ]
|
1058 |
|
|
|
1059 |
|
|
rename unknown ""
|
1060 |
|
|
rename synth_device ""
|
1061 |
|
|
rename _synth_unknown unknown
|
1062 |
|
|
|
1063 |
|
|
if { $_result } {
|
1064 |
|
|
# Any problems reading in the target definition file should be
|
1065 |
|
|
# treated as an error: I/O is unlikely to behave in the way
|
1066 |
|
|
# that the user expects.
|
1067 |
|
|
set msg "An error occurred while reading in the target definition file\n $_config_file\n $_config_file_msg\n"
|
1068 |
|
|
synth::report_error $msg
|
1069 |
|
|
exit 1
|
1070 |
|
|
}
|
1071 |
|
|
unset _result _config_file_msg
|
1072 |
|
|
}
|
1073 |
|
|
|
1074 |
|
|
unset _dirlist _tdf _config_file _candidate
|
1075 |
|
|
|
1076 |
|
|
# }}}
|
1077 |
|
|
|
1078 |
|
|
if { $synth::flag_gui } {
|
1079 |
|
|
# {{{ Main GUI code
|
1080 |
|
|
|
1081 |
|
|
# {{{ Session file
|
1082 |
|
|
|
1083 |
|
|
# ----------------------------------------------------------------------------
|
1084 |
|
|
# The tool manages a file ~/.ecos/synth/guisession, holding information
|
1085 |
|
|
# such as the size and position of the main window. The aim is to give
|
1086 |
|
|
# the user a fairly consistent interface between sessions. The information
|
1087 |
|
|
# is saved during exit handling, and also in response to the window
|
1088 |
|
|
# manager WM_SAVE_YOURSELF request. However note that the latter does
|
1089 |
|
|
# not extend to user session information - restarting the eCos application
|
1090 |
|
|
# the next time a user logs in is inappropriate for eCos, plus if
|
1091 |
|
|
# the application is being run inside gdb (a likely scenario) it is gdb
|
1092 |
|
|
# that should handle restarting the application.
|
1093 |
|
|
#
|
1094 |
|
|
# Using a single file has limitations. Specifically the user may be
|
1095 |
|
|
# switching between a number of different target definition files,
|
1096 |
|
|
# each resulting in a subtly different layout, and arguably there
|
1097 |
|
|
# should be separate session information for each one. However
|
1098 |
|
|
# distinguishing between per-target and global settings would be
|
1099 |
|
|
# very complicated.
|
1100 |
|
|
#
|
1101 |
|
|
# The most obvious implementation involves the options database.
|
1102 |
|
|
#
|
1103 |
|
|
# FIXME: implement properly
|
1104 |
|
|
|
1105 |
|
|
namespace eval synth {
|
1106 |
|
|
# Make sure we are using the right options from .Xdefaults etc.
|
1107 |
|
|
tk appname "ecosynth"
|
1108 |
|
|
|
1109 |
|
|
if { $synth::flag_debug } {
|
1110 |
|
|
# synth::report "Reading in session file ~/.ecos/synth/guisession\n"
|
1111 |
|
|
}
|
1112 |
|
|
|
1113 |
|
|
# synth::report_warning "Support for reading session file ~/.ecos/synth/guisession not yet implemented.\n"
|
1114 |
|
|
|
1115 |
|
|
if { [file exists "~/.ecos/synth/guisession"] } {
|
1116 |
|
|
if {0 != [catch { option readfile "~/.ecos/synth/guisession" userDefault} msg]} {
|
1117 |
|
|
# synth::report_error "Failed to read GUI session information from file ~/.ecos/synth/guisession\n $msg\n"
|
1118 |
|
|
}
|
1119 |
|
|
}
|
1120 |
|
|
|
1121 |
|
|
proc _update_session_file { arg_list } {
|
1122 |
|
|
# synth::report_warning "Support for updating session file ~/.ecos/synth/guisession not yet implemented.\n"
|
1123 |
|
|
}
|
1124 |
|
|
proc _handle_wm_save_yourself { } {
|
1125 |
|
|
# synth::report_warning "Support for WM_SAVE_YOURSELF not yet implemented\n"
|
1126 |
|
|
}
|
1127 |
|
|
|
1128 |
|
|
synth::hook_add "exit" synth::_update_session_file
|
1129 |
|
|
}
|
1130 |
|
|
|
1131 |
|
|
# }}}
|
1132 |
|
|
# {{{ Load images
|
1133 |
|
|
|
1134 |
|
|
# ----------------------------------------------------------------------------
|
1135 |
|
|
# Load various useful bitmaps etc. into memory, so that they can be accessed
|
1136 |
|
|
# by any code that needs them.
|
1137 |
|
|
#
|
1138 |
|
|
# Running1 is a coloured version of the eCos logo. running2 and running3 are
|
1139 |
|
|
# used by alternative implementations of the heartbeat: running2 has the
|
1140 |
|
|
# red and black reversed, and running3 is mostly a mirror image of the normal
|
1141 |
|
|
# logo.
|
1142 |
|
|
namespace eval synth {
|
1143 |
|
|
|
1144 |
|
|
proc load_image { image_name filename } {
|
1145 |
|
|
set result 0
|
1146 |
|
|
set type [file extension $filename]
|
1147 |
|
|
if { ! [file exists $filename] } {
|
1148 |
|
|
synth::report_error "Image $filename has not been installed.\n"
|
1149 |
|
|
} elseif { ! [file readable $filename] } {
|
1150 |
|
|
synth::report_error "Image $filename is not readable.\n"
|
1151 |
|
|
} elseif { (".xbm" == $type) } {
|
1152 |
|
|
if { 0 == [catch { set $image_name [image create bitmap -file $filename] }] } {
|
1153 |
|
|
set result 1
|
1154 |
|
|
} else {
|
1155 |
|
|
synth::report_error "Bitmap image $filename is invalid.\n"
|
1156 |
|
|
}
|
1157 |
|
|
} else {
|
1158 |
|
|
if { 0 == [catch { set $image_name [image create photo -file $filename] }] } {
|
1159 |
|
|
set result 1
|
1160 |
|
|
} else {
|
1161 |
|
|
synth::report_error "Image $filename is invalid.\n"
|
1162 |
|
|
}
|
1163 |
|
|
}
|
1164 |
|
|
return $result
|
1165 |
|
|
}
|
1166 |
|
|
|
1167 |
|
|
set _images [list "tick_yes.xbm" "tick_no.xbm" "save.xbm" "cut.xbm" "copy.xbm" "paste.xbm" \
|
1168 |
|
|
"help.xbm" "running1.ppm" "saveall.xbm" ]
|
1169 |
|
|
foreach _image $_images {
|
1170 |
|
|
variable image_[file rootname $_image]
|
1171 |
|
|
synth::load_image "synth::image_[file rootname $_image]" [file join $synth::install_dir $_image]
|
1172 |
|
|
}
|
1173 |
|
|
unset _images _image
|
1174 |
|
|
}
|
1175 |
|
|
|
1176 |
|
|
# }}}
|
1177 |
|
|
# {{{ Balloon help
|
1178 |
|
|
|
1179 |
|
|
namespace eval synth {
|
1180 |
|
|
|
1181 |
|
|
variable _balloon_messages
|
1182 |
|
|
variable _balloon_pending ""
|
1183 |
|
|
|
1184 |
|
|
toplevel .balloon
|
1185 |
|
|
label .balloon.info -borderwidth 2 -relief groove -background "light yellow"
|
1186 |
|
|
pack .balloon.info -side left -fill both -expand 1
|
1187 |
|
|
wm overrideredirect .balloon 1
|
1188 |
|
|
wm withdraw .balloon
|
1189 |
|
|
|
1190 |
|
|
proc register_balloon_help { widget message } {
|
1191 |
|
|
set synth::_balloon_messages($widget) $message
|
1192 |
|
|
bind $widget <Enter> { synth::_balloonhelp_pending %W }
|
1193 |
|
|
bind $widget <Leave> { synth::_balloonhelp_cancel }
|
1194 |
|
|
}
|
1195 |
|
|
|
1196 |
|
|
proc _balloonhelp_pending { widget } {
|
1197 |
|
|
synth::_balloonhelp_cancel
|
1198 |
|
|
set synth::_balloon_pending [after 1200 [list synth::_balloonhelp_show $widget]]
|
1199 |
|
|
}
|
1200 |
|
|
|
1201 |
|
|
proc _balloonhelp_cancel { } {
|
1202 |
|
|
if { "" != $synth::_balloon_pending } {
|
1203 |
|
|
after cancel $synth::_balloon_pending
|
1204 |
|
|
set synth::_balloon_pending ""
|
1205 |
|
|
} else {
|
1206 |
|
|
wm withdraw .balloon
|
1207 |
|
|
}
|
1208 |
|
|
}
|
1209 |
|
|
|
1210 |
|
|
proc _balloonhelp_show { widget } {
|
1211 |
|
|
.balloon.info configure -text $synth::_balloon_messages($widget)
|
1212 |
|
|
set x [expr [winfo rootx $widget] + 2]
|
1213 |
|
|
set y [expr [winfo rooty $widget] + [winfo height $widget] + 2]
|
1214 |
|
|
wm geometry .balloon +$x+$y
|
1215 |
|
|
wm deiconify .balloon
|
1216 |
|
|
raise .balloon
|
1217 |
|
|
set synth::_balloon_pending ""
|
1218 |
|
|
}
|
1219 |
|
|
}
|
1220 |
|
|
|
1221 |
|
|
# }}}
|
1222 |
|
|
# {{{ Window manager settings
|
1223 |
|
|
|
1224 |
|
|
# ----------------------------------------------------------------------------
|
1225 |
|
|
# Set up the current program name in the title bar etc.
|
1226 |
|
|
|
1227 |
|
|
namespace eval synth {
|
1228 |
|
|
|
1229 |
|
|
if { $synth::flag_debug } {
|
1230 |
|
|
synth::report "Performing required interactions with window manager\n"
|
1231 |
|
|
}
|
1232 |
|
|
|
1233 |
|
|
# The toplevel is withdrawn during startup. It is possible that
|
1234 |
|
|
# some of the windows and other objects created initially will end
|
1235 |
|
|
# up being deleted again before the system is fully up and running,
|
1236 |
|
|
# and the event loop is entered before then to accept requests from
|
1237 |
|
|
# the eCos application. This could cause confusing changes. The
|
1238 |
|
|
# toplevel is displayed in response to the constructors-done request.
|
1239 |
|
|
wm withdraw .
|
1240 |
|
|
|
1241 |
|
|
# For now disable all attempts to use the "send" command. Leaving it
|
1242 |
|
|
# enabled would introduce security problems.
|
1243 |
|
|
rename "::send" {}
|
1244 |
|
|
|
1245 |
|
|
variable title "eCos synthetic target"
|
1246 |
|
|
if { "" != $synth::ecos_appname} {
|
1247 |
|
|
append synth::title ": $synth::ecos_appname ($synth::_ppid)"
|
1248 |
|
|
}
|
1249 |
|
|
wm title . $synth::title
|
1250 |
|
|
|
1251 |
|
|
# Use the specified geometry, or that from the last session.
|
1252 |
|
|
# Obviously how well this works depends very much on the
|
1253 |
|
|
# window manager being used.
|
1254 |
|
|
set _geometry ""
|
1255 |
|
|
if { "" == $synth::geometry} {
|
1256 |
|
|
# Command line request to suppress the preferences. Revert
|
1257 |
|
|
# to a default size.
|
1258 |
|
|
set _geometry "640x480"
|
1259 |
|
|
} elseif { "<none>" == $synth::geometry } {
|
1260 |
|
|
# No command line option, use the value from the preferences file
|
1261 |
|
|
# FIXME: implement
|
1262 |
|
|
set _geometry "640x480"
|
1263 |
|
|
} else {
|
1264 |
|
|
# There was an explicit -geometry option on the command line. Use it.
|
1265 |
|
|
set synth::_geometry $synth::geometry
|
1266 |
|
|
if { [regexp -- {^([0-9]+x[0-9]+).*$} $synth::_geometry] } {
|
1267 |
|
|
wm sizefrom . "user"
|
1268 |
|
|
}
|
1269 |
|
|
if { [regexp -- {^.*([+-][0-9]+[+-][0-9]+)$} $synth::_geometry] } {
|
1270 |
|
|
wm positionfrom . "user"
|
1271 |
|
|
}
|
1272 |
|
|
}
|
1273 |
|
|
wm geometry . $synth::_geometry
|
1274 |
|
|
unset synth::_geometry
|
1275 |
|
|
|
1276 |
|
|
set _file [file join $synth::install_dir "ecosicon.xbm"]
|
1277 |
|
|
if { [file readable $synth::_file] } {
|
1278 |
|
|
wm iconbitmap . "@$synth::_file"
|
1279 |
|
|
}
|
1280 |
|
|
set _file [file join $synth::install_dir "ecosiconmask.xbm"]
|
1281 |
|
|
if { [file readable $synth::_file] } {
|
1282 |
|
|
wm iconmask . "@$synth::_file"
|
1283 |
|
|
}
|
1284 |
|
|
unset synth::_file
|
1285 |
|
|
|
1286 |
|
|
if { "" != $synth::ecos_appname } {
|
1287 |
|
|
wm iconname . "ecosynth: $synth::ecos_appname"
|
1288 |
|
|
} else {
|
1289 |
|
|
wm iconname . "ecosynth"
|
1290 |
|
|
}
|
1291 |
|
|
|
1292 |
|
|
wm protocol . "WM_DELETE_WINDOW" synth::_handle_exit_request
|
1293 |
|
|
wm protocol . "WM_SAVE_YOURSELF" synth::_handle_wm_save_yourself
|
1294 |
|
|
}
|
1295 |
|
|
|
1296 |
|
|
# }}}
|
1297 |
|
|
# {{{ Exit and kill handling
|
1298 |
|
|
|
1299 |
|
|
# ----------------------------------------------------------------------------
|
1300 |
|
|
# Exit handling. The user may request program termination using various
|
1301 |
|
|
# different ways:
|
1302 |
|
|
# 1) File->Exit
|
1303 |
|
|
# 2) ctrl-Q, the shortcut for the above
|
1304 |
|
|
# 3) the Window Manager's delete-window request
|
1305 |
|
|
#
|
1306 |
|
|
# If eCos has already exited then the request can be handled straightaway.
|
1307 |
|
|
# The invocation of exit will go via the exit hooks so appropriate
|
1308 |
|
|
# clean-ups will take place.
|
1309 |
|
|
#
|
1310 |
|
|
# If eCos has not already exited then it is assumed that the user wants
|
1311 |
|
|
# the eCos application to terminate as well as the GUI. This can be achieved
|
1312 |
|
|
# via the interrupt subsystem. However, there is a risk that the application
|
1313 |
|
|
# has crashed, or is blocked in gdb, or has interrupts permanently disabled,
|
1314 |
|
|
# in which case it is not going to respond to the SIGIO. To allow for this
|
1315 |
|
|
# a number of retries are attempted, and after five seconds of this the
|
1316 |
|
|
# application is killed off forcibly.
|
1317 |
|
|
|
1318 |
|
|
namespace eval synth {
|
1319 |
|
|
|
1320 |
|
|
variable _handle_exit_retries 0
|
1321 |
|
|
variable _handle_exit_after ""
|
1322 |
|
|
|
1323 |
|
|
proc _handle_exit_request { } {
|
1324 |
|
|
|
1325 |
|
|
if { !$synth::ecos_running } {
|
1326 |
|
|
exit 0
|
1327 |
|
|
}
|
1328 |
|
|
# Setting this flag causes ecosynth to exit immediately once
|
1329 |
|
|
# the application terminates.
|
1330 |
|
|
set synth::flag_immediate_exit 1
|
1331 |
|
|
|
1332 |
|
|
# Now ask the application to exit
|
1333 |
|
|
synth::request_application_exit
|
1334 |
|
|
|
1335 |
|
|
# Set up a timer to retry this
|
1336 |
|
|
if { "" == $synth::_handle_exit_after } {
|
1337 |
|
|
set synth::_handle_exit_after [after 1000 synth::_handle_exit_timer]
|
1338 |
|
|
|
1339 |
|
|
# And output something so the user knows the request has been received
|
1340 |
|
|
synth::report "Waiting for the eCos application to exit.\n"
|
1341 |
|
|
}
|
1342 |
|
|
}
|
1343 |
|
|
|
1344 |
|
|
# This routine requests termination of eCos, but not of
|
1345 |
|
|
# ecosynth
|
1346 |
|
|
proc _handle_kill_request { } {
|
1347 |
|
|
if { $synth::ecos_running } {
|
1348 |
|
|
synth::request_application_exit
|
1349 |
|
|
if { "" == $synth::_handle_exit_after } {
|
1350 |
|
|
set synth::_handle_exit_after [after 1000 synth::_handle_exit_timer]
|
1351 |
|
|
}
|
1352 |
|
|
}
|
1353 |
|
|
}
|
1354 |
|
|
|
1355 |
|
|
proc _handle_exit_timer { } {
|
1356 |
|
|
if { $synth::ecos_running } {
|
1357 |
|
|
incr synth::_handle_exit_retries
|
1358 |
|
|
if { $synth::_handle_exit_retries < 5 } {
|
1359 |
|
|
synth::request_application_exit
|
1360 |
|
|
synth::report "Still waiting for the eCos application to exit.\n"
|
1361 |
|
|
} else {
|
1362 |
|
|
synth::_send_SIGKILL
|
1363 |
|
|
}
|
1364 |
|
|
set synth::_handle_exit_after [after 1000 synth::_handle_exit_timer]
|
1365 |
|
|
}
|
1366 |
|
|
}
|
1367 |
|
|
}
|
1368 |
|
|
|
1369 |
|
|
# }}}
|
1370 |
|
|
# {{{ Main window layout
|
1371 |
|
|
|
1372 |
|
|
# ----------------------------------------------------------------------------
|
1373 |
|
|
# The window layout is as follows:
|
1374 |
|
|
# 1) a menu bar at the top (surprise surprise). Many of the menus will be
|
1375 |
|
|
# empty or nearly so, but device-specific scripts may want to extend
|
1376 |
|
|
# the menus.
|
1377 |
|
|
# 2) a toolbar. This is primarily for use by device-specific scripts
|
1378 |
|
|
# 3) a central grid.
|
1379 |
|
|
# 4) a status line at the bottom.
|
1380 |
|
|
#
|
1381 |
|
|
# The central grid is organised as a 3x3 set of frames. The centre frame holds
|
1382 |
|
|
# the main text display, plus associated scrollbars, and is the frame that
|
1383 |
|
|
# will expand or shrink as the toplevel is resized. The remaining eight frames
|
1384 |
|
|
# (nw, n, ne, e, se, s, sw, w) are available for use by device-specific
|
1385 |
|
|
# scripts, typically under control of settings in the target definition file.
|
1386 |
|
|
# It is very possible that some or all of these eight frames will be empty,
|
1387 |
|
|
# and if an entire row or column is empty then Tk will make them invisible.
|
1388 |
|
|
#
|
1389 |
|
|
# Possible enhancements:
|
1390 |
|
|
# 1) implement some sort of paning/resizing around the central text window.
|
1391 |
|
|
# That would provide some way of letting the user control the space
|
1392 |
|
|
# taken by device-specific subwindows. This would be implemented
|
1393 |
|
|
# by modifying the weights assigned to different rows/columns.
|
1394 |
|
|
# 2) it would be very useful if the main text window could be split,
|
1395 |
|
|
# like emacs. This would require multiple text widgets, with output
|
1396 |
|
|
# being pasted in each one.
|
1397 |
|
|
# 3) possibly the text window should not be hard-wired to the centre frame,
|
1398 |
|
|
# instead its frame could be selected by preferences somehow.
|
1399 |
|
|
|
1400 |
|
|
if { $synth::flag_debug } {
|
1401 |
|
|
synth::report "Creating main window layout\n"
|
1402 |
|
|
}
|
1403 |
|
|
|
1404 |
|
|
# The various frames are generally accessed via variables
|
1405 |
|
|
|
1406 |
|
|
menu .menubar -borderwidth 1
|
1407 |
|
|
menu .menubar.file
|
1408 |
|
|
menu .menubar.edit
|
1409 |
|
|
menu .menubar.view
|
1410 |
|
|
menu .menubar.windows
|
1411 |
|
|
menu .menubar.help
|
1412 |
|
|
|
1413 |
|
|
. configure -menu .menubar
|
1414 |
|
|
.menubar add cascade -label "File" -underline 0 -menu .menubar.file
|
1415 |
|
|
.menubar add cascade -label "Edit" -underline 0 -menu .menubar.edit
|
1416 |
|
|
.menubar add cascade -label "View" -underline 0 -menu .menubar.view
|
1417 |
|
|
.menubar add cascade -label "Windows" -underline 0 -menu .menubar.windows
|
1418 |
|
|
.menubar add cascade -label "Help" -underline 0 -menu .menubar.help
|
1419 |
|
|
|
1420 |
|
|
.menubar.file add command -label "Save" -underline 0 -accelerator "Ctrl-S" -command [list synth::_handle_file_save]
|
1421 |
|
|
.menubar.file add command -label "Save As..." -underline 5 -command [list synth::_handle_file_save_as]
|
1422 |
|
|
.menubar.file add command -label "Save All..." -underline 6 -command [list synth::_handle_file_save_all]
|
1423 |
|
|
.menubar.file add command -label "Kill eCos" -underline 0 -command [list synth::_handle_kill_request]
|
1424 |
|
|
.menubar.file add command -label "Exit" -underline 1 -accelerator "Ctrl-Q" -command [list synth::_handle_exit_request]
|
1425 |
|
|
bind . <Control-KeyPress-q> [list synth::_handle_exit_request]
|
1426 |
|
|
bind . <Control-KeyPress-s> [list synth::_handle_file_save]
|
1427 |
|
|
|
1428 |
|
|
# Once eCos has exited, the kill option should be disabled
|
1429 |
|
|
namespace eval synth {
|
1430 |
|
|
proc _menubar_ecos_exit_clean { arg_list } {
|
1431 |
|
|
.menubar.file entryconfigure "Kill eCos" -state disabled
|
1432 |
|
|
}
|
1433 |
|
|
synth::hook_add "ecos_exit" synth::_menubar_ecos_exit_clean
|
1434 |
|
|
}
|
1435 |
|
|
|
1436 |
|
|
frame .toolbar -borderwidth 1 -relief groove
|
1437 |
|
|
if { "" != $synth::image_save } {
|
1438 |
|
|
button .toolbar.save -image $synth::image_save -borderwidth 0 -command [list synth::_handle_file_save]
|
1439 |
|
|
pack .toolbar.save -side left -padx 2
|
1440 |
|
|
synth::register_balloon_help .toolbar.save "Save visible output"
|
1441 |
|
|
}
|
1442 |
|
|
if { "" != $synth::image_saveall } {
|
1443 |
|
|
button .toolbar.saveall -image $synth::image_saveall -borderwidth 0 -command [list synth::_handle_file_save_all]
|
1444 |
|
|
pack .toolbar.saveall -side left -padx 2
|
1445 |
|
|
synth::register_balloon_help .toolbar.saveall "Save all output"
|
1446 |
|
|
}
|
1447 |
|
|
if { "" != $synth::image_cut } {
|
1448 |
|
|
button .toolbar.cut -image $synth::image_cut -borderwidth 0 -state disabled -command [list synth::_handle_edit_cut]
|
1449 |
|
|
pack .toolbar.cut -side left -padx 2
|
1450 |
|
|
synth::register_balloon_help .toolbar.cut "Cut"
|
1451 |
|
|
}
|
1452 |
|
|
if { "" != $synth::image_copy } {
|
1453 |
|
|
|
1454 |
|
|
button .toolbar.copy -image $synth::image_copy -borderwidth 0 -command [list synth::_handle_edit_copy]
|
1455 |
|
|
pack .toolbar.copy -side left -padx 2
|
1456 |
|
|
synth::register_balloon_help .toolbar.copy "Copy"
|
1457 |
|
|
}
|
1458 |
|
|
if { "" != $synth::image_paste } {
|
1459 |
|
|
button .toolbar.paste -image $synth::image_paste -borderwidth 0 -state disabled -command [list synth::_handle_edit_paste]
|
1460 |
|
|
pack .toolbar.paste -side left -padx 2
|
1461 |
|
|
synth::register_balloon_help .toolbar.paste "Paste"
|
1462 |
|
|
}
|
1463 |
|
|
pack .toolbar -side top -fill x
|
1464 |
|
|
|
1465 |
|
|
frame .main
|
1466 |
|
|
frame .main.nw -borderwidth 0
|
1467 |
|
|
frame .main.n -borderwidth 0
|
1468 |
|
|
frame .main.ne -borderwidth 0
|
1469 |
|
|
frame .main.e -borderwidth 0
|
1470 |
|
|
frame .main.se -borderwidth 0
|
1471 |
|
|
frame .main.s -borderwidth 0
|
1472 |
|
|
frame .main.sw -borderwidth 0
|
1473 |
|
|
frame .main.w -borderwidth 0
|
1474 |
|
|
|
1475 |
|
|
frame .main.centre
|
1476 |
|
|
|
1477 |
|
|
frame .main.border_nw_n -width 2 -background black -borderwidth 0
|
1478 |
|
|
frame .main.border_n_ne -width 2 -background black -borderwidth 0
|
1479 |
|
|
frame .main.border_w_centre -width 2 -background black -borderwidth 0
|
1480 |
|
|
frame .main.border_centre_e -width 2 -background black -borderwidth 0
|
1481 |
|
|
frame .main.border_sw_s -width 2 -background black -borderwidth 0
|
1482 |
|
|
frame .main.border_s_se -width 2 -background black -borderwidth 0
|
1483 |
|
|
frame .main.border_nw_w -height 2 -background black -borderwidth 0
|
1484 |
|
|
frame .main.border_n_centre -height 2 -background black -borderwidth 0
|
1485 |
|
|
frame .main.border_ne_e -height 2 -background black -borderwidth 0
|
1486 |
|
|
frame .main.border_w_sw -height 2 -background black -borderwidth 0
|
1487 |
|
|
frame .main.border_centre_s -height 2 -background black -borderwidth 0
|
1488 |
|
|
frame .main.border_e_se -height 2 -background black -borderwidth 0
|
1489 |
|
|
|
1490 |
|
|
text .main.centre.text -xscrollcommand [list .main.centre.horizontal set] -yscrollcommand [list .main.centre.vertical set]
|
1491 |
|
|
scrollbar .main.centre.horizontal -orient horizontal -command [list .main.centre.text xview]
|
1492 |
|
|
scrollbar .main.centre.vertical -orient vertical -command [list .main.centre.text yview]
|
1493 |
|
|
grid configure .main.centre.text -row 0 -column 0 -sticky news
|
1494 |
|
|
grid configure .main.centre.vertical -row 0 -column 1 -sticky ns
|
1495 |
|
|
grid configure .main.centre.horizontal -row 1 -column 0 -sticky ew
|
1496 |
|
|
# Is there anything useful to be done in 1,1? e.g. a >> button to
|
1497 |
|
|
# go directly to perform ".main.centre.text see end"
|
1498 |
|
|
|
1499 |
|
|
# Make sure that row 0 column 0, i.e. the text widget rather than the
|
1500 |
|
|
# scrollbars, grows to fit all available space.
|
1501 |
|
|
grid rowconfigure .main.centre 0 -weight 1
|
1502 |
|
|
grid rowconfigure .main.centre 1 -weight 0
|
1503 |
|
|
grid columnconfigure .main.centre 0 -weight 1
|
1504 |
|
|
grid columnconfigure .main.centre 1 -weight 0
|
1505 |
|
|
|
1506 |
|
|
grid configure .main.nw -row 0 -column 0 -sticky news
|
1507 |
|
|
grid configure .main.border_nw_n -row 0 -column 1 -sticky ns
|
1508 |
|
|
grid configure .main.n -row 0 -column 2 -sticky news
|
1509 |
|
|
grid configure .main.border_n_ne -row 0 -column 3 -sticky ns
|
1510 |
|
|
grid configure .main.ne -row 0 -column 4 -sticky news
|
1511 |
|
|
grid configure .main.border_nw_w -row 1 -column 0 -sticky ew
|
1512 |
|
|
grid configure .main.border_n_centre -row 1 -column 1 -columnspan 3 -sticky ew
|
1513 |
|
|
grid configure .main.border_ne_e -row 1 -column 4 -sticky ew
|
1514 |
|
|
grid configure .main.w -row 2 -column 0 -sticky news
|
1515 |
|
|
grid configure .main.border_w_centre -row 2 -column 1 -sticky ns
|
1516 |
|
|
grid configure .main.centre -row 2 -column 2 -sticky news
|
1517 |
|
|
grid configure .main.border_centre_e -row 2 -column 3 -sticky ns
|
1518 |
|
|
grid configure .main.e -row 2 -column 4 -sticky news
|
1519 |
|
|
grid configure .main.border_w_sw -row 3 -column 0 -sticky ew
|
1520 |
|
|
grid configure .main.border_centre_s -row 3 -column 1 -columnspan 3 -sticky ew
|
1521 |
|
|
grid configure .main.border_e_se -row 3 -column 4 -sticky ew
|
1522 |
|
|
grid configure .main.sw -row 4 -column 0 -sticky news
|
1523 |
|
|
grid configure .main.border_sw_s -row 4 -column 1 -sticky ns
|
1524 |
|
|
grid configure .main.s -row 4 -column 2 -sticky news
|
1525 |
|
|
grid configure .main.border_s_se -row 4 -column 3 -sticky ns
|
1526 |
|
|
grid configure .main.se -row 4 -column 4 -sticky news
|
1527 |
|
|
grid columnconfigure .main 0 -weight 0
|
1528 |
|
|
grid columnconfigure .main 1 -weight 0
|
1529 |
|
|
grid columnconfigure .main 2 -weight 1
|
1530 |
|
|
grid columnconfigure .main 3 -weight 0
|
1531 |
|
|
grid columnconfigure .main 4 -weight 0
|
1532 |
|
|
grid rowconfigure .main 0 -weight 0
|
1533 |
|
|
grid rowconfigure .main 1 -weight 0
|
1534 |
|
|
grid rowconfigure .main 2 -weight 1
|
1535 |
|
|
grid rowconfigure .main 3 -weight 0
|
1536 |
|
|
grid rowconfigure .main 4 -weight 0
|
1537 |
|
|
|
1538 |
|
|
# The .main frame should not be packed into the main window yet.
|
1539 |
|
|
# Until all devices have been instantiated the various subwindows
|
1540 |
|
|
# are not yet known, so the desired size of .main is not known
|
1541 |
|
|
# either. Packing it too early and then adding more windows
|
1542 |
|
|
# causes confusion.
|
1543 |
|
|
|
1544 |
|
|
# }}}
|
1545 |
|
|
# {{{ Help
|
1546 |
|
|
|
1547 |
|
|
# ----------------------------------------------------------------------------
|
1548 |
|
|
# Two main sources of documentation are of interest to the synthetic
|
1549 |
|
|
# target. The first is the toplevel eCos documentation. The second
|
1550 |
|
|
# is the documentation specific to the generic target. Device-specific
|
1551 |
|
|
# scripts may want to add menu entries for their own documentation.
|
1552 |
|
|
#
|
1553 |
|
|
# The problems are:
|
1554 |
|
|
# 1) where to find the documentation
|
1555 |
|
|
# 2) how to view it?
|
1556 |
|
|
#
|
1557 |
|
|
# The documentation should be in the component repository. If there is
|
1558 |
|
|
# a variable ECOS_REPOSITORY then that gives the appropriate information.
|
1559 |
|
|
# Otherwise things get messy because the repository being used for
|
1560 |
|
|
# eCos may not match the repository used when building the host-side
|
1561 |
|
|
# support - the versions should match but the repository may have
|
1562 |
|
|
# moved. Never the less that is the best we can do.
|
1563 |
|
|
# NOTE: sources.redhat.com might provide another alternative, but the
|
1564 |
|
|
# documentation is not organized in the same way as the repository.
|
1565 |
|
|
#
|
1566 |
|
|
# As for how to view the documentation, this is up to user preferences
|
1567 |
|
|
# but ecosynth has built-in knowledge of three different viewers.
|
1568 |
|
|
|
1569 |
|
|
namespace eval synth {
|
1570 |
|
|
|
1571 |
|
|
if { $synth::flag_debug } {
|
1572 |
|
|
synth::report "Setting up help menu\n"
|
1573 |
|
|
}
|
1574 |
|
|
|
1575 |
|
|
variable _browser1 "mozilla -remote openURL(%s)"
|
1576 |
|
|
variable _browser2 "mozilla %s"
|
1577 |
|
|
variable _browser3 "gnome-help-browser %s"
|
1578 |
|
|
variable _main_help ""
|
1579 |
|
|
variable _synth_help ""
|
1580 |
|
|
set _repo ""
|
1581 |
|
|
|
1582 |
|
|
if { [info exists env(ECOS_REPOSITORY)] } {
|
1583 |
|
|
set _repo $env(ECOS_REPOSITORY)
|
1584 |
|
|
} else {
|
1585 |
|
|
set _repo $synth::_ecos_repository
|
1586 |
|
|
}
|
1587 |
|
|
if { ![file exists [file join $_repo "ecos.db"]] } {
|
1588 |
|
|
synth::report_warning "Failed to locate eCos component repository.\n \
|
1589 |
|
|
Please define an environment variable ECOS_REPOSITORY.\n"
|
1590 |
|
|
} else {
|
1591 |
|
|
# FIXME: this needs attention once the documentation is more sorted
|
1592 |
|
|
set synth::_main_help [file join $_repo "index.html"]
|
1593 |
|
|
if { ![file exists $synth::_main_help] } {
|
1594 |
|
|
if { 0 } {
|
1595 |
|
|
synth::report_warning "Failed to locate toplevel documentation file $synth::_main_help\n \
|
1596 |
|
|
Help->eCos menu option disabled.\n"
|
1597 |
|
|
}
|
1598 |
|
|
set synth::_main_help ""
|
1599 |
|
|
} else {
|
1600 |
|
|
set synth::_main_help "file://$_main_help"
|
1601 |
|
|
}
|
1602 |
|
|
|
1603 |
|
|
set synth::_synth_help [file join $_repo $synth::_ecosynth_package_dir $synth::_ecosynth_package_version "doc/hal-synth-arch.html"]
|
1604 |
|
|
if { ![file exists $synth::_synth_help] } {
|
1605 |
|
|
synth::report_warning "Failed to locate synthetic target documentation $synth::_synth_help\n \
|
1606 |
|
|
Help->Synthetic target menu option disabled.\n"
|
1607 |
|
|
set synth::_synth_help ""
|
1608 |
|
|
} else {
|
1609 |
|
|
set synth::_synth_help "file://$_synth_help"
|
1610 |
|
|
}
|
1611 |
|
|
}
|
1612 |
|
|
|
1613 |
|
|
if { "" != $_main_help } {
|
1614 |
|
|
.menubar.help add command -label "eCos" -command [list synth::_menu_help $synth::handle_help]
|
1615 |
|
|
} else {
|
1616 |
|
|
.menubar.help add command -label "eCos" -state disabled
|
1617 |
|
|
}
|
1618 |
|
|
if { "" != $_synth_help } {
|
1619 |
|
|
.menubar.help add command -label "Synthetic target" -command [list synth::handle_help "$synth::_synth_help"]
|
1620 |
|
|
} else {
|
1621 |
|
|
.menubar.help add command -label "Synthetic target" -state disabled
|
1622 |
|
|
}
|
1623 |
|
|
|
1624 |
|
|
unset _repo
|
1625 |
|
|
|
1626 |
|
|
proc handle_help { which } {
|
1627 |
|
|
set command [format $synth::_browser1 $which]
|
1628 |
|
|
if { 0 != [catch { eval exec -- "$command > /dev/null" } & ] } {
|
1629 |
|
|
set command [format $synth::_browser2 $which]
|
1630 |
|
|
if { 0 != [catch { eval exec -- "$command > /dev/null &" } ] } {
|
1631 |
|
|
set command [format $synth::_browser3 $which]
|
1632 |
|
|
if { 0 != [catch { eval exec -- "$command > /dev/null &"} ] } {
|
1633 |
|
|
synth::report_warning "Unable to start a help browser.\n Please check the settings in Edit->Preferences.\n"
|
1634 |
|
|
}
|
1635 |
|
|
}
|
1636 |
|
|
}
|
1637 |
|
|
}
|
1638 |
|
|
|
1639 |
|
|
# FIXME: add an about box as well.
|
1640 |
|
|
}
|
1641 |
|
|
|
1642 |
|
|
# }}}
|
1643 |
|
|
# {{{ Filters
|
1644 |
|
|
|
1645 |
|
|
# ----------------------------------------------------------------------------
|
1646 |
|
|
# The central text window is expected to provide the bulk of the information
|
1647 |
|
|
# to the user. This information can be voluminous, so filtering is desirable.
|
1648 |
|
|
#
|
1649 |
|
|
# There are two types of filters. System filters are provided by ecosynth
|
1650 |
|
|
# itself and by device-specific scripts. For example ecosynth has a filter
|
1651 |
|
|
# for warnings, and the console device has a filter for eCos trace messages.
|
1652 |
|
|
# In addition users can specify their own filters using regular expressions,
|
1653 |
|
|
# and those filters take priority. Note that there is an assumption that
|
1654 |
|
|
# output is predominantly line-based: if partial lines get output then
|
1655 |
|
|
# some confusion is possible.
|
1656 |
|
|
#
|
1657 |
|
|
# With tk the implementation is relatively straightforward: the text widget's
|
1658 |
|
|
# tag facility does all the hard work of controlling how text gets displayed.
|
1659 |
|
|
# It is possible to show or hide text using -elide, colours can be controlled
|
1660 |
|
|
# using -background and -foreground, ... Not all of this functionality
|
1661 |
|
|
# is made available to the user just yet.
|
1662 |
|
|
|
1663 |
|
|
namespace eval synth {
|
1664 |
|
|
# The bulk of the information is held in arrays, indexed by the name of
|
1665 |
|
|
# the filter. Lists are used to keep track of all valid names.
|
1666 |
|
|
variable _system_filter_list [list]
|
1667 |
|
|
variable _system_filter_settings
|
1668 |
|
|
variable _user_filter_list [list]
|
1669 |
|
|
variable _user_filter_settings
|
1670 |
|
|
|
1671 |
|
|
# Does a given system filter already exist?
|
1672 |
|
|
proc filter_exists { name } {
|
1673 |
|
|
set result 0
|
1674 |
|
|
if { -1 != [lsearch -exact $synth::_system_filter_list $name] } {
|
1675 |
|
|
set result 1
|
1676 |
|
|
}
|
1677 |
|
|
return $result
|
1678 |
|
|
}
|
1679 |
|
|
|
1680 |
|
|
proc filter_get_list { } {
|
1681 |
|
|
return $synth::_system_filter_list
|
1682 |
|
|
}
|
1683 |
|
|
|
1684 |
|
|
# Parsing support. All filters take a number of standard options:
|
1685 |
|
|
#
|
1686 |
|
|
# -text "message" - how to identify this filter to the user
|
1687 |
|
|
# -hide [0|1] - whether or not this text should be hidden by default
|
1688 |
|
|
# -foreground <colour>
|
1689 |
|
|
# -background <colour>
|
1690 |
|
|
#
|
1691 |
|
|
# The details of the currently supported options are known only to
|
1692 |
|
|
# filter_parse_options and filter_add, allowing new options such
|
1693 |
|
|
# as font manipulation to be added in future.
|
1694 |
|
|
#
|
1695 |
|
|
# There are two ways of adding a filter. filter_add is typically used
|
1696 |
|
|
# inside ecosynth.tcl with known good data. filter_add_parsed is
|
1697 |
|
|
# used with user-provided data, e.g. from the target definition file,
|
1698 |
|
|
# after a call to filter_validate.
|
1699 |
|
|
proc filter_parse_options { arg_list result_ref message_ref } {
|
1700 |
|
|
upvar 1 $result_ref result
|
1701 |
|
|
upvar 1 $message_ref message
|
1702 |
|
|
set message ""
|
1703 |
|
|
|
1704 |
|
|
set text_set 0
|
1705 |
|
|
set hide_set 0
|
1706 |
|
|
set foreground_set 0
|
1707 |
|
|
set background_set 0
|
1708 |
|
|
|
1709 |
|
|
set len [llength $arg_list]
|
1710 |
|
|
for { set i 0 } { $i < $len } { incr i } {
|
1711 |
|
|
set arg [lindex $arg_list $i]
|
1712 |
|
|
if { ( "-text" == $arg) ||
|
1713 |
|
|
( "-hide" == $arg) ||
|
1714 |
|
|
( "-foreground" == $arg) || ( "-fg" == $arg) ||
|
1715 |
|
|
( "-background" == $arg) || ( "-bg" == $arg) } {
|
1716 |
|
|
|
1717 |
|
|
incr i
|
1718 |
|
|
if { $i >= $len } {
|
1719 |
|
|
append message " Missing data after argument $arg\n"
|
1720 |
|
|
} else {
|
1721 |
|
|
set data [lindex $arg_list $i]
|
1722 |
|
|
if { "-text" == $arg } {
|
1723 |
|
|
if { $text_set } {
|
1724 |
|
|
append message " Attempt to set -text option twice.\n"
|
1725 |
|
|
} else {
|
1726 |
|
|
set text_set 1
|
1727 |
|
|
set result("-text") $data
|
1728 |
|
|
}
|
1729 |
|
|
} elseif { "-hide" == $arg } {
|
1730 |
|
|
if { $hide_set } {
|
1731 |
|
|
append message " Attempt to set -hide option twice.\n"
|
1732 |
|
|
} else {
|
1733 |
|
|
set hide_set 1
|
1734 |
|
|
if { ! [string is boolean -strict $data] } {
|
1735 |
|
|
append message " -hide should be given a boolean value, not \"$data\"\n"
|
1736 |
|
|
} else {
|
1737 |
|
|
set result("-hide") [expr $data ? 1 : 0]
|
1738 |
|
|
}
|
1739 |
|
|
}
|
1740 |
|
|
} elseif { ( "-foreground" == $arg) || ( "-fg" == $arg ) } {
|
1741 |
|
|
if { $foreground_set } {
|
1742 |
|
|
append message " Attempt to set -foreground twice.\n"
|
1743 |
|
|
} else {
|
1744 |
|
|
set foreground_set 1
|
1745 |
|
|
# FIXME: is there some way of validating this color?
|
1746 |
|
|
set result("-foreground") $data
|
1747 |
|
|
}
|
1748 |
|
|
} elseif { ( "-background" == $arg) || ( "-bg" == $arg ) } {
|
1749 |
|
|
if { $background_set } {
|
1750 |
|
|
append message " Attempt to set -background twice.\n"
|
1751 |
|
|
} else {
|
1752 |
|
|
set background_set 1
|
1753 |
|
|
# FIXME: is there some way of validating this color?
|
1754 |
|
|
set result("-background") $data
|
1755 |
|
|
}
|
1756 |
|
|
}
|
1757 |
|
|
}
|
1758 |
|
|
} else {
|
1759 |
|
|
append message " Unknown option \"$arg\".\n"
|
1760 |
|
|
}
|
1761 |
|
|
}
|
1762 |
|
|
|
1763 |
|
|
if { "" == $message } {
|
1764 |
|
|
return 1
|
1765 |
|
|
} else {
|
1766 |
|
|
return 0
|
1767 |
|
|
}
|
1768 |
|
|
}
|
1769 |
|
|
|
1770 |
|
|
# Add a new system filter, after the options have been parsed
|
1771 |
|
|
proc filter_add_parsed { name data_ref } {
|
1772 |
|
|
upvar 1 $data_ref data
|
1773 |
|
|
|
1774 |
|
|
set text $name
|
1775 |
|
|
set hide 0
|
1776 |
|
|
set foreground "<default>"
|
1777 |
|
|
set background "<default>"
|
1778 |
|
|
if { [info exists data("-text")] } {
|
1779 |
|
|
set text $data("-text")
|
1780 |
|
|
}
|
1781 |
|
|
if { [info exists data("-hide")] } {
|
1782 |
|
|
set hide $data("-hide")
|
1783 |
|
|
}
|
1784 |
|
|
if { [info exists data("-foreground")] } {
|
1785 |
|
|
set foreground $data("-foreground")
|
1786 |
|
|
}
|
1787 |
|
|
if { [info exists data("-background")] } {
|
1788 |
|
|
set background $data("-background")
|
1789 |
|
|
}
|
1790 |
|
|
|
1791 |
|
|
if { $hide } {
|
1792 |
|
|
.main.centre.text tag configure $name -elide 1
|
1793 |
|
|
} else {
|
1794 |
|
|
.main.centre.text tag configure $name -elide 0
|
1795 |
|
|
}
|
1796 |
|
|
if { "<default>" == $foreground } {
|
1797 |
|
|
.main.centre.text tag configure $name -foreground [.main.centre.text cget -foreground]
|
1798 |
|
|
} else {
|
1799 |
|
|
set msg ""
|
1800 |
|
|
if [catch { .main.centre.text tag configure $name -foreground $foreground } msg ] {
|
1801 |
|
|
synth::report_warning "Unable to configure color \"$foreground\"\n $msg\n"
|
1802 |
|
|
set foreground "<default>"
|
1803 |
|
|
.main.centre.text tag configure $name -foreground [.main.centre.text cget -foreground]
|
1804 |
|
|
}
|
1805 |
|
|
}
|
1806 |
|
|
if { "<default>" == $background } {
|
1807 |
|
|
.main.centre.text tag configure $name -background [.main.centre.text cget -background]
|
1808 |
|
|
} else {
|
1809 |
|
|
set msg ""
|
1810 |
|
|
if [catch { .main.centre.text tag configure $name -background $background } msg ] {
|
1811 |
|
|
synth::report_warning "Unable to configure color \"$background\"\n $msg\n"
|
1812 |
|
|
set background "<default>"
|
1813 |
|
|
.main.centre.text tag configure $name -background [.main.centre.text cget -background]
|
1814 |
|
|
}
|
1815 |
|
|
}
|
1816 |
|
|
|
1817 |
|
|
lappend synth::_system_filter_list $name
|
1818 |
|
|
set synth::_system_filter_settings($name,text) $text
|
1819 |
|
|
set synth::_system_filter_settings($name,hide) $hide
|
1820 |
|
|
set synth::_system_filter_settings($name,foreground) $foreground
|
1821 |
|
|
set synth::_system_filter_settings($name,background) $background
|
1822 |
|
|
|
1823 |
|
|
# System tags should only get added during initialization. Hence the
|
1824 |
|
|
# first time the system filters window is brought up all filters
|
1825 |
|
|
# should be defined. However, just in case a new filter is added
|
1826 |
|
|
# in the middle of a run...
|
1827 |
|
|
if { [winfo exists .system_filters] } {
|
1828 |
|
|
destroy .system_filters
|
1829 |
|
|
}
|
1830 |
|
|
}
|
1831 |
|
|
|
1832 |
|
|
# Add a new system filter, performing the appropriate parsing.
|
1833 |
|
|
proc filter_add { name args } {
|
1834 |
|
|
|
1835 |
|
|
if { [synth::filter_exists $name] } {
|
1836 |
|
|
synth::internal_error "attempt to install filter $name twice.\n"
|
1837 |
|
|
}
|
1838 |
|
|
array set data [list]
|
1839 |
|
|
set msg ""
|
1840 |
|
|
|
1841 |
|
|
if { ![synth::filter_parse_options $args data msg] } {
|
1842 |
|
|
# Any dubious arguments to the internal filter_add are treated as fatal.
|
1843 |
|
|
synth::internal_error "unable to create new filter $name.\n$msg"
|
1844 |
|
|
} else {
|
1845 |
|
|
filter_add_parsed $name data
|
1846 |
|
|
}
|
1847 |
|
|
}
|
1848 |
|
|
|
1849 |
|
|
filter_add "report" -text "ecosynth messages"
|
1850 |
|
|
filter_add "error" -text "ecosynth errors" -foreground red
|
1851 |
|
|
# amber is not a standard colour. Amber leds emit light in the range
|
1852 |
|
|
# 595-605 nm, corresponding to rgb values of approx. FF4200.
|
1853 |
|
|
# OrangeRed is close enough at FF4500
|
1854 |
|
|
filter_add "warning" -text "ecosynth warnings" -foreground OrangeRed
|
1855 |
|
|
|
1856 |
|
|
# Bring up the system filters window, creating it if necessary.
|
1857 |
|
|
# Keeping the toplevel around but iconified/withdrawn when
|
1858 |
|
|
# unwanted means that properties such as size and position will
|
1859 |
|
|
# tend to be preserved.
|
1860 |
|
|
variable _system_filter_new_settings
|
1861 |
|
|
variable _system_filter_widgets
|
1862 |
|
|
|
1863 |
|
|
proc _menu_view_system_filters { } {
|
1864 |
|
|
if { [winfo exists .system_filters] } {
|
1865 |
|
|
if { "normal" == [wm state .system_filters] } {
|
1866 |
|
|
raise .system_filters
|
1867 |
|
|
} else {
|
1868 |
|
|
wm deiconify .system_filters
|
1869 |
|
|
}
|
1870 |
|
|
return
|
1871 |
|
|
}
|
1872 |
|
|
toplevel .system_filters
|
1873 |
|
|
wm title .system_filters "ecosynth system filters"
|
1874 |
|
|
wm protocol .system_filters "WM_DELETE_WINDOW" [list synth::_menu_view_system_filters_cancel]
|
1875 |
|
|
wm group .system_filters .
|
1876 |
|
|
|
1877 |
|
|
frame .system_filters.main
|
1878 |
|
|
label .system_filters.main.header1 -text "Filter"
|
1879 |
|
|
label .system_filters.main.header2 -text "Hide"
|
1880 |
|
|
label .system_filters.main.header3 -text "Foreground"
|
1881 |
|
|
label .system_filters.main.header4 -text "Background"
|
1882 |
|
|
set text_fg [.system_filters.main.header1 cget -foreground]
|
1883 |
|
|
frame .system_filters.main.row0 -height 1 -background $text_fg
|
1884 |
|
|
frame .system_filters.main.row2 -height 1 -background $text_fg
|
1885 |
|
|
frame .system_filters.main.col0 -width 1 -background $text_fg
|
1886 |
|
|
frame .system_filters.main.col2 -width 1 -background $text_fg
|
1887 |
|
|
frame .system_filters.main.col4 -width 1 -background $text_fg
|
1888 |
|
|
frame .system_filters.main.col6 -width 1 -background $text_fg
|
1889 |
|
|
frame .system_filters.main.col8 -width 1 -background $text_fg
|
1890 |
|
|
grid .system_filters.main.row0 -row 0 -column 0 -columnspan 9 -sticky ew
|
1891 |
|
|
grid .system_filters.main.header1 -row 1 -column 1 -sticky news
|
1892 |
|
|
grid .system_filters.main.header2 -row 1 -column 3 -sticky news
|
1893 |
|
|
grid .system_filters.main.header3 -row 1 -column 5 -sticky news
|
1894 |
|
|
grid .system_filters.main.header4 -row 1 -column 7 -sticky news
|
1895 |
|
|
grid .system_filters.main.row2 -row 2 -column 0 -columnspan 9 -sticky ew
|
1896 |
|
|
|
1897 |
|
|
set row 3
|
1898 |
|
|
foreach filter $synth::_system_filter_list {
|
1899 |
|
|
set synth::_system_filter_new_settings($filter,hide) $synth::_system_filter_settings($filter,hide)
|
1900 |
|
|
set synth::_system_filter_new_settings($filter,foreground) $synth::_system_filter_settings($filter,foreground)
|
1901 |
|
|
set synth::_system_filter_new_settings($filter,background) $synth::_system_filter_settings($filter,background)
|
1902 |
|
|
|
1903 |
|
|
set synth::_system_filter_widgets($filter,label) \
|
1904 |
|
|
[label .system_filters.main.filter_name_$row -text $synth::_system_filter_settings($filter,text)]
|
1905 |
|
|
set synth::_system_filter_widgets($filter,hide) \
|
1906 |
|
|
[checkbutton .system_filters.main.filter_hide_$row -borderwidth 2 -indicatoron false -selectcolor "" \
|
1907 |
|
|
-image $synth::image_tick_no -selectimage $synth::image_tick_yes -variable synth::_system_filter_new_settings($filter,hide)]
|
1908 |
|
|
set synth::_system_filter_widgets($filter,foreground) [button .system_filters.main.filter_foreground_$row -borderwidth 2 \
|
1909 |
|
|
-command [list synth::_menu_view_system_filters_choose_foreground $filter]]
|
1910 |
|
|
set synth::_system_filter_widgets($filter,background) [button .system_filters.main.filter_background_$row -borderwidth 2 \
|
1911 |
|
|
-command [list synth::_menu_view_system_filters_choose_background $filter]]
|
1912 |
|
|
|
1913 |
|
|
grid .system_filters.main.filter_name_$row -row $row -column 1 -sticky news
|
1914 |
|
|
grid .system_filters.main.filter_hide_$row -row $row -column 3 -sticky news
|
1915 |
|
|
grid .system_filters.main.filter_foreground_$row -row $row -column 5 -sticky news
|
1916 |
|
|
grid .system_filters.main.filter_background_$row -row $row -column 7 -sticky news
|
1917 |
|
|
|
1918 |
|
|
incr row
|
1919 |
|
|
frame .system_filters.main.row$row -height 1 -background $text_fg
|
1920 |
|
|
grid .system_filters.main.row$row -row $row -column 0 -columnspan 9 -sticky ew
|
1921 |
|
|
incr row
|
1922 |
|
|
}
|
1923 |
|
|
grid .system_filters.main.col0 -row 0 -column 0 -rowspan $row -sticky ns
|
1924 |
|
|
grid .system_filters.main.col2 -row 0 -column 2 -rowspan $row -sticky ns
|
1925 |
|
|
grid .system_filters.main.col4 -row 0 -column 4 -rowspan $row -sticky ns
|
1926 |
|
|
grid .system_filters.main.col6 -row 0 -column 6 -rowspan $row -sticky ns
|
1927 |
|
|
grid .system_filters.main.col8 -row 0 -column 8 -rowspan $row -sticky ns
|
1928 |
|
|
|
1929 |
|
|
for { set i 0 } { $i < $row } { incr i 2 } {
|
1930 |
|
|
grid rowconfigure .system_filters.main $i -weight 0
|
1931 |
|
|
}
|
1932 |
|
|
for { set i 1 } { $i < $row } { incr i 2 } {
|
1933 |
|
|
grid rowconfigure .system_filters.main $i -weight 1
|
1934 |
|
|
}
|
1935 |
|
|
for { set i 0 } { $i < 9 } { incr i 2 } {
|
1936 |
|
|
grid columnconfigure .system_filters.main $i -weight 0
|
1937 |
|
|
}
|
1938 |
|
|
for { set i 1 } { $i < 9 } { incr i 2 } {
|
1939 |
|
|
grid columnconfigure .system_filters.main $i -weight 1
|
1940 |
|
|
}
|
1941 |
|
|
|
1942 |
|
|
pack .system_filters.main -side top -fill both -expand 1 -pady 4 -padx 4
|
1943 |
|
|
|
1944 |
|
|
# FIXME: add try and revert buttons
|
1945 |
|
|
frame .system_filters.buttons
|
1946 |
|
|
button .system_filters.buttons.ok -text "OK" -command [list synth::_menu_view_system_filters_ok]
|
1947 |
|
|
button .system_filters.buttons.cancel -text "Cancel" -command [list synth::_menu_view_system_filters_cancel]
|
1948 |
|
|
pack .system_filters.buttons.ok .system_filters.buttons.cancel -side left -expand 1
|
1949 |
|
|
pack .system_filters.buttons -side bottom -fill x -pady 4
|
1950 |
|
|
|
1951 |
|
|
frame .system_filters.separator -height 2 -borderwidth 1 -relief sunken
|
1952 |
|
|
pack .system_filters.separator -side bottom -fill x -pady 4
|
1953 |
|
|
|
1954 |
|
|
bind .system_filters <KeyPress-Return> [list synth::_menu_view_system_filters_ok]
|
1955 |
|
|
bind .system_filters <KeyPress-Escape> [list synth::_menu_view_system_filters_cancel]
|
1956 |
|
|
|
1957 |
|
|
synth::_menu_view_system_filters_reset
|
1958 |
|
|
}
|
1959 |
|
|
|
1960 |
|
|
proc _menu_view_system_filters_reset { } {
|
1961 |
|
|
foreach filter $synth::_system_filter_list {
|
1962 |
|
|
set synth::_system_filter_new_settings($filter,hide) $synth::_system_filter_settings($filter,hide)
|
1963 |
|
|
set synth::_system_filter_new_settings($filter,foreground) $synth::_system_filter_settings($filter,foreground)
|
1964 |
|
|
set synth::_system_filter_new_settings($filter,background) $synth::_system_filter_settings($filter,background)
|
1965 |
|
|
|
1966 |
|
|
set colour $synth::_system_filter_new_settings($filter,foreground)
|
1967 |
|
|
if { "<default>" == $colour } {
|
1968 |
|
|
set colour [.system_filters.main.header1 cget -foreground]
|
1969 |
|
|
}
|
1970 |
|
|
$synth::_system_filter_widgets($filter,label) configure -foreground $colour
|
1971 |
|
|
$synth::_system_filter_widgets($filter,foreground) configure -background $colour -activebackground $colour
|
1972 |
|
|
|
1973 |
|
|
set colour $synth::_system_filter_new_settings($filter,background)
|
1974 |
|
|
if { "<default>" == $colour } {
|
1975 |
|
|
set colour [.system_filters.main.header1 cget -background]
|
1976 |
|
|
}
|
1977 |
|
|
$synth::_system_filter_widgets($filter,label) configure -background $colour
|
1978 |
|
|
$synth::_system_filter_widgets($filter,background) configure -background $colour -activebackground $colour
|
1979 |
|
|
}
|
1980 |
|
|
}
|
1981 |
|
|
|
1982 |
|
|
# Change a colour. For now this involves calling Tk's chooseColor utility.
|
1983 |
|
|
# This is simple but not quite right: it would be much better to allow
|
1984 |
|
|
# the foreground and background to be modified in the same dialog, providing
|
1985 |
|
|
# immediate feedback on how the text will actually appear; it should also
|
1986 |
|
|
# provide some simple way of reverting to the default.
|
1987 |
|
|
proc _menu_view_system_filters_choose_foreground { filter } {
|
1988 |
|
|
set current_colour $synth::_system_filter_new_settings($filter,foreground)
|
1989 |
|
|
if { "<default>" == $current_colour } {
|
1990 |
|
|
set current_colour [.system_filters.main.header1 cget -foreground]
|
1991 |
|
|
}
|
1992 |
|
|
set new_colour [tk_chooseColor -parent .system_filters -title "$synth::_system_filter_settings($filter,text) foreground" \
|
1993 |
|
|
-initialcolor $current_colour]
|
1994 |
|
|
if { "" != $new_colour } {
|
1995 |
|
|
set synth::_system_filter_new_settings($filter,foreground) $new_colour
|
1996 |
|
|
$synth::_system_filter_widgets($filter,label) configure -foreground $new_colour
|
1997 |
|
|
$synth::_system_filter_widgets($filter,foreground) configure -background $new_colour -activebackground $new_colour
|
1998 |
|
|
}
|
1999 |
|
|
}
|
2000 |
|
|
|
2001 |
|
|
proc _menu_view_system_filters_choose_background { filter } {
|
2002 |
|
|
set current_colour $synth::_system_filter_new_settings($filter,background)
|
2003 |
|
|
if { "<default>" == $current_colour } {
|
2004 |
|
|
set current_colour [.system_filters.main.header1 cget -background]
|
2005 |
|
|
}
|
2006 |
|
|
set new_colour [tk_chooseColor -parent .system_filters -title "$synth::_system_filter_settings($filter,text) background" \
|
2007 |
|
|
-initialcolor $current_colour]
|
2008 |
|
|
if { "" != $new_colour } {
|
2009 |
|
|
set synth::_system_filter_new_settings($filter,background) $new_colour
|
2010 |
|
|
$synth::_system_filter_widgets($filter,label) configure -background $new_colour
|
2011 |
|
|
$synth::_system_filter_widgets($filter,background) configure -background $new_colour -activebackground $new_colour
|
2012 |
|
|
}
|
2013 |
|
|
}
|
2014 |
|
|
|
2015 |
|
|
proc _menu_view_system_filters_ok { } {
|
2016 |
|
|
wm withdraw .system_filters
|
2017 |
|
|
foreach filter $synth::_system_filter_list {
|
2018 |
|
|
if { $synth::_system_filter_settings($filter,hide) != $synth::_system_filter_new_settings($filter,hide) } {
|
2019 |
|
|
set hide $synth::_system_filter_new_settings($filter,hide)
|
2020 |
|
|
set synth::_system_filter_settings($filter,hide) $hide
|
2021 |
|
|
if { $hide } {
|
2022 |
|
|
.main.centre.text tag configure $filter -elide 1
|
2023 |
|
|
} else {
|
2024 |
|
|
.main.centre.text tag configure $filter -elide 0
|
2025 |
|
|
}
|
2026 |
|
|
}
|
2027 |
|
|
if { $synth::_system_filter_settings($filter,foreground) != $synth::_system_filter_new_settings($filter,foreground) } {
|
2028 |
|
|
set foreground $synth::_system_filter_new_settings($filter,foreground)
|
2029 |
|
|
set synth::_system_filter_settings($filter,foreground) $foreground
|
2030 |
|
|
.main.centre.text tag configure $filter -foreground $foreground
|
2031 |
|
|
}
|
2032 |
|
|
if { $synth::_system_filter_settings($filter,background) != $synth::_system_filter_new_settings($filter,background) } {
|
2033 |
|
|
set background $synth::_system_filter_new_settings($filter,background)
|
2034 |
|
|
set synth::_system_filter_settings($filter,background) $background
|
2035 |
|
|
.main.centre.text tag configure $filter -background $background
|
2036 |
|
|
}
|
2037 |
|
|
}
|
2038 |
|
|
}
|
2039 |
|
|
|
2040 |
|
|
proc _menu_view_system_filters_cancel { } {
|
2041 |
|
|
wm withdraw .system_filters
|
2042 |
|
|
synth::_menu_view_system_filters_reset
|
2043 |
|
|
}
|
2044 |
|
|
|
2045 |
|
|
# Now add a suitable entry to the View menu.
|
2046 |
|
|
.menubar.view add command -label "System filters..." -command [list synth::_menu_view_system_filters]
|
2047 |
|
|
|
2048 |
|
|
# User filters.
|
2049 |
|
|
# FIXME: implement
|
2050 |
|
|
# .menubar.view add command -label "User filters..." -command [list synth::_menu_view_filters] -state disabled
|
2051 |
|
|
}
|
2052 |
|
|
|
2053 |
|
|
# }}}
|
2054 |
|
|
# {{{ Text window
|
2055 |
|
|
|
2056 |
|
|
# ----------------------------------------------------------------------------
|
2057 |
|
|
# The central text window is expected to provide the bulk of the information
|
2058 |
|
|
# to the user. Various filtering mechanisms are desirable. For example the
|
2059 |
|
|
# user should be able to control whether or not eCos trace messages are
|
2060 |
|
|
# currently visible, not to mention other characteristics such as font
|
2061 |
|
|
# and colours. The text widget's tag mechanism makes this relatively simple.
|
2062 |
|
|
|
2063 |
|
|
namespace eval synth {
|
2064 |
|
|
|
2065 |
|
|
# Should the user be able to edit the text window, e.g. to add annotations?
|
2066 |
|
|
# This is disabled by default but can be enabled.
|
2067 |
|
|
variable flag_read_only 1
|
2068 |
|
|
|
2069 |
|
|
# By default disable wrapping. Possibly it should be possible to
|
2070 |
|
|
# enable this on a per-tag basis.
|
2071 |
|
|
.main.centre.text configure -wrap "none"
|
2072 |
|
|
|
2073 |
|
|
# Give the text widget the focus by default. That way operations
|
2074 |
|
|
# like page-up work immediately.
|
2075 |
|
|
focus .main.centre.text
|
2076 |
|
|
|
2077 |
|
|
# If editing is currently disallowed, do not accept any input.
|
2078 |
|
|
# The code below is probably not quite sufficient, it is
|
2079 |
|
|
# ASCII-centric. A separate binding for Alt- sequences ensures
|
2080 |
|
|
# that the top-level menu processing continues to work.
|
2081 |
|
|
# Similarly a separate binding for Control- sequences ensures
|
2082 |
|
|
# that the shortcuts continue to work.
|
2083 |
|
|
bind .main.centre.text <Alt-KeyPress> {
|
2084 |
|
|
continue
|
2085 |
|
|
}
|
2086 |
|
|
bind .main.centre.text <Control-KeyPress> {
|
2087 |
|
|
continue
|
2088 |
|
|
}
|
2089 |
|
|
bind .main.centre.text <KeyPress> {
|
2090 |
|
|
if { !$synth::flag_read_only } {
|
2091 |
|
|
continue
|
2092 |
|
|
} elseif { 0 != [llength %A] } {
|
2093 |
|
|
break
|
2094 |
|
|
} elseif { ("Return" == "%K") || ("Tab" == "%K") || ("space" == "%K") } {
|
2095 |
|
|
break
|
2096 |
|
|
} else {
|
2097 |
|
|
continue
|
2098 |
|
|
}
|
2099 |
|
|
}
|
2100 |
|
|
# There are a few special control- bindings built in to the Tk text
|
2101 |
|
|
# widget which perform editing operations
|
2102 |
|
|
bind .main.centre.text <Control-KeyPress-h> {
|
2103 |
|
|
if { !$synth::flag_read_only } {
|
2104 |
|
|
continue
|
2105 |
|
|
} else {
|
2106 |
|
|
break
|
2107 |
|
|
}
|
2108 |
|
|
}
|
2109 |
|
|
bind .main.centre.text <Control-KeyPress-d> {
|
2110 |
|
|
if { !$synth::flag_read_only } {
|
2111 |
|
|
continue
|
2112 |
|
|
} else {
|
2113 |
|
|
break
|
2114 |
|
|
}
|
2115 |
|
|
}
|
2116 |
|
|
bind .main.centre.text <Control-KeyPress-k> {
|
2117 |
|
|
if { !$synth::flag_read_only } {
|
2118 |
|
|
continue
|
2119 |
|
|
} else {
|
2120 |
|
|
break
|
2121 |
|
|
}
|
2122 |
|
|
}
|
2123 |
|
|
bind .main.centre.text <Control-KeyPress-o> {
|
2124 |
|
|
if { !$synth::flag_read_only } {
|
2125 |
|
|
continue
|
2126 |
|
|
} else {
|
2127 |
|
|
break
|
2128 |
|
|
}
|
2129 |
|
|
}
|
2130 |
|
|
bind .main.centre.text <Control-KeyPress-t> {
|
2131 |
|
|
if { !$synth::flag_read_only } {
|
2132 |
|
|
continue
|
2133 |
|
|
} else {
|
2134 |
|
|
break
|
2135 |
|
|
}
|
2136 |
|
|
}
|
2137 |
|
|
|
2138 |
|
|
# Implement support for the normal edit menu operations.
|
2139 |
|
|
# FIXME: add a search facility
|
2140 |
|
|
.menubar.edit insert end command -label "Cut" -command [list synth::_handle_edit_cut] -underline 2 -accelerator "Ctrl-X" -state disabled
|
2141 |
|
|
.menubar.edit insert end command -label "Copy" -command [list synth::_handle_edit_copy] -underline 0 -accelerator "Ctrl-C"
|
2142 |
|
|
.menubar.edit insert end command -label "Paste" -command [list synth::_handle_edit_paste] -underline 0 -accelerator "Ctrl-V" -state disabled
|
2143 |
|
|
.menubar.edit insert end command -label "Clear" -command [list synth::_handle_edit_clear] -underline 3 -accelerator "Del" -state disabled
|
2144 |
|
|
.menubar.edit insert end command -label "Select All" -command [list synth::_handle_edit_select_all] -underline 9 -accelerator "Ctrl-A"
|
2145 |
|
|
.menubar.edit insert end checkbutton -label "Read Only" -variable synth::flag_read_only
|
2146 |
|
|
.menubar.edit insert end separator
|
2147 |
|
|
proc _trace_read_only { name1 name2 op } {
|
2148 |
|
|
if { !$synth::flag_read_only } {
|
2149 |
|
|
.menubar.edit entryconfigure "Cut" -state normal
|
2150 |
|
|
.menubar.edit entryconfigure "Paste" -state normal
|
2151 |
|
|
.menubar.edit entryconfigure "Clear" -state normal
|
2152 |
|
|
.toolbar.cut configure -state normal
|
2153 |
|
|
.toolbar.paste configure -state normal
|
2154 |
|
|
} else {
|
2155 |
|
|
.menubar.edit entryconfigure "Cut" -state disabled
|
2156 |
|
|
.menubar.edit entryconfigure "Paste" -state disabled
|
2157 |
|
|
.menubar.edit entryconfigure "Clear" -state disabled
|
2158 |
|
|
.toolbar.cut configure -state disabled
|
2159 |
|
|
.toolbar.paste configure -state disabled
|
2160 |
|
|
}
|
2161 |
|
|
}
|
2162 |
|
|
trace variable synth::flag_read_only "w" synth::_trace_read_only
|
2163 |
|
|
|
2164 |
|
|
# Support for cut'n'paste etc. The widget does most of the hard
|
2165 |
|
|
# work, but this code has to distinguish between read-only and
|
2166 |
|
|
# read-write windows.
|
2167 |
|
|
#
|
2168 |
|
|
# Some operations such as clear may operate on everything in the
|
2169 |
|
|
# selection, including hidden text that happens to be in the
|
2170 |
|
|
# range. That may or may not be the right thing to do. It is right
|
2171 |
|
|
# if the intent is to get rid of all events during a period of
|
2172 |
|
|
# time, but wrong if the user wants to get rid of specific text.
|
2173 |
|
|
bind . <Control-KeyPress-x> [list synth::_handle_edit_cut]
|
2174 |
|
|
bind . <Control-KeyPress-c> [list synth::_handle_edit_copy]
|
2175 |
|
|
bind . <Control-KeyPress-v> [list synth::_handle_edit_paste]
|
2176 |
|
|
bind . <KeyPress-Delete> [list synth::_handle_edit_clear]
|
2177 |
|
|
bind . <Control-KeyPress-a> [list synth::_handle_edit_select_all]
|
2178 |
|
|
|
2179 |
|
|
bind .main.centre.text <<Paste>> {
|
2180 |
|
|
if { !$synth::flag_read_only } {
|
2181 |
|
|
continue
|
2182 |
|
|
} else {
|
2183 |
|
|
break
|
2184 |
|
|
}
|
2185 |
|
|
}
|
2186 |
|
|
bind .main.centre.text <<Cut>> {
|
2187 |
|
|
if { !$synth::flag_read_only } {
|
2188 |
|
|
continue
|
2189 |
|
|
} else {
|
2190 |
|
|
break
|
2191 |
|
|
}
|
2192 |
|
|
}
|
2193 |
|
|
bind .main.centre.text <<Clear>> {
|
2194 |
|
|
if { !$synth::flag_read_only } {
|
2195 |
|
|
continue
|
2196 |
|
|
} else {
|
2197 |
|
|
break
|
2198 |
|
|
}
|
2199 |
|
|
}
|
2200 |
|
|
|
2201 |
|
|
proc _handle_edit_cut { } {
|
2202 |
|
|
event generate .main.centre.text "<<Cut>>"
|
2203 |
|
|
}
|
2204 |
|
|
|
2205 |
|
|
proc _handle_edit_copy { } {
|
2206 |
|
|
event generate .main.centre.text "<<Copy>>"
|
2207 |
|
|
}
|
2208 |
|
|
|
2209 |
|
|
proc _handle_edit_paste { } {
|
2210 |
|
|
event generate .main.centre.text "<<Paste>>"
|
2211 |
|
|
}
|
2212 |
|
|
|
2213 |
|
|
proc _handle_edit_clear { } {
|
2214 |
|
|
event generate .main.centre.text "<<Clear>>"
|
2215 |
|
|
}
|
2216 |
|
|
|
2217 |
|
|
proc _handle_edit_select_all { } {
|
2218 |
|
|
.main.centre.text tag add sel 1.0 "end - 1 chars"
|
2219 |
|
|
}
|
2220 |
|
|
|
2221 |
|
|
# Most output to the text window goes through this routine. It inserts
|
2222 |
|
|
# some text with an appropriate tag. In addition it will ensure that
|
2223 |
|
|
# the new text is visible if appropriate, and if a logfile has been
|
2224 |
|
|
# specified then that will be updated as well.
|
2225 |
|
|
proc output { msg tag } {
|
2226 |
|
|
set ytail [lindex [.main.centre.text yview] 1]
|
2227 |
|
|
set xhead [lindex [.main.centre.text xview] 0]
|
2228 |
|
|
.main.centre.text insert end $msg $tag
|
2229 |
|
|
if { (1.0 == $ytail) && (0.0 == $xhead) } {
|
2230 |
|
|
.main.centre.text see end
|
2231 |
|
|
}
|
2232 |
|
|
if { "" != $synth::_logfd } {
|
2233 |
|
|
puts -nonewline $synth::_logfd $msg
|
2234 |
|
|
}
|
2235 |
|
|
}
|
2236 |
|
|
|
2237 |
|
|
# Text output is now possible, so flush anything that is still buffered.
|
2238 |
|
|
# xview and yview may not give the right results until the window
|
2239 |
|
|
# is mapped, so always make the last text visible.
|
2240 |
|
|
set synth::flag_gui_ready 1
|
2241 |
|
|
synth::_flush_output
|
2242 |
|
|
.main.centre.text see end
|
2243 |
|
|
|
2244 |
|
|
|
2245 |
|
|
# Support for saving the current document. Save applies only to
|
2246 |
|
|
# the currently visible text. SaveAll gives the hidden text as
|
2247 |
|
|
# well.
|
2248 |
|
|
variable _savefile ""
|
2249 |
|
|
proc _handle_file_save { } {
|
2250 |
|
|
if { "" == $synth::_savefile } {
|
2251 |
|
|
set synth::_savefile [tk_getSaveFile -parent .]
|
2252 |
|
|
if { "" == $synth::_savefile } {
|
2253 |
|
|
return
|
2254 |
|
|
}
|
2255 |
|
|
}
|
2256 |
|
|
set msg ""
|
2257 |
|
|
if { 0 != [catch { set fd [open $synth::_savefile "w"] } msg] } {
|
2258 |
|
|
synth::report_error "$msg\n"
|
2259 |
|
|
if { $synth::_system_filter_settings(error,hide) } {
|
2260 |
|
|
tk_messageBox -type "ok" -icon "error" -parent . -message "$msg\n"
|
2261 |
|
|
}
|
2262 |
|
|
return
|
2263 |
|
|
}
|
2264 |
|
|
set number_lines [expr int([.main.centre.text index end])]
|
2265 |
|
|
for { set i 1 } { $i < $number_lines } { incr i } {
|
2266 |
|
|
set tags [.main.centre.text tag names "[set i].0"]
|
2267 |
|
|
if {[llength $tags] > 0 } {
|
2268 |
|
|
set tag [lindex $tags 0]
|
2269 |
|
|
if { [info exists synth::_system_filter_settings($tag,hide)] &&
|
2270 |
|
|
$synth::_system_filter_settings($tag,hide) } {
|
2271 |
|
|
continue
|
2272 |
|
|
}
|
2273 |
|
|
}
|
2274 |
|
|
puts $fd [.main.centre.text get "[set i].0" "[set i].end"]
|
2275 |
|
|
}
|
2276 |
|
|
close $fd
|
2277 |
|
|
}
|
2278 |
|
|
|
2279 |
|
|
proc _handle_file_save_as { } {
|
2280 |
|
|
set new_savefile [tk_getSaveFile -parent .]
|
2281 |
|
|
if { "" == $new_savefile } {
|
2282 |
|
|
return
|
2283 |
|
|
}
|
2284 |
|
|
set synth::_savefile $new_savefile
|
2285 |
|
|
synth::_handle_file_save
|
2286 |
|
|
}
|
2287 |
|
|
|
2288 |
|
|
proc _handle_file_save_all { } {
|
2289 |
|
|
set new_savefile [tk_getSaveFile -parent .]
|
2290 |
|
|
if { "" == $new_savefile } {
|
2291 |
|
|
return
|
2292 |
|
|
}
|
2293 |
|
|
set msg ""
|
2294 |
|
|
if { 0 != [catch { set fd [open $new_savefile "w"] } msg] } {
|
2295 |
|
|
synth::report_error "$msg\n"
|
2296 |
|
|
if { $synth::_system_filter_settings(error,hide) } {
|
2297 |
|
|
tk_messageBox -type "ok" -icon "error" -parent . -message "$msg\n"
|
2298 |
|
|
}
|
2299 |
|
|
return
|
2300 |
|
|
}
|
2301 |
|
|
puts -nonewline $fd [.main.centre.text get 1.0 end]
|
2302 |
|
|
close $fd
|
2303 |
|
|
}
|
2304 |
|
|
}
|
2305 |
|
|
|
2306 |
|
|
# }}}
|
2307 |
|
|
# {{{ Heartbeat and status
|
2308 |
|
|
|
2309 |
|
|
# ----------------------------------------------------------------------------
|
2310 |
|
|
# This code manages a status line at the bottom of the main window.
|
2311 |
|
|
# This involves a little heartbeat window, a label with the
|
2312 |
|
|
# text Running or Exited, some padding, and an additional status
|
2313 |
|
|
# line for use by other code.
|
2314 |
|
|
#
|
2315 |
|
|
# A variety of heartbeats have been attempted. The current one is
|
2316 |
|
|
# still not very good, but will do for now. Others are if 0'd out.
|
2317 |
|
|
# Note that these others may require additional images to be
|
2318 |
|
|
# preloaded.
|
2319 |
|
|
|
2320 |
|
|
namespace eval synth {
|
2321 |
|
|
frame .status -borderwidth 1 -relief groove
|
2322 |
|
|
|
2323 |
|
|
if { 1 } {
|
2324 |
|
|
# The eCos logo, bouncing horizontally
|
2325 |
|
|
variable _heartbeat_image_width [image width $synth::image_running1]
|
2326 |
|
|
variable _heartbeat_offset 0
|
2327 |
|
|
variable _heartbeat_ltor 1
|
2328 |
|
|
|
2329 |
|
|
frame .status.heartbeat -width $synth::_heartbeat_image_width -height [image height $synth::image_running1]
|
2330 |
|
|
pack .status.heartbeat -side left
|
2331 |
|
|
label .status.heartbeat.image -image $synth::image_running1 -anchor w -borderwidth 0
|
2332 |
|
|
place .status.heartbeat.image -x $synth::_heartbeat_offset -y 0
|
2333 |
|
|
|
2334 |
|
|
proc _heartbeat_update { } {
|
2335 |
|
|
if { ! $synth::ecos_running } {
|
2336 |
|
|
place configure .status.heartbeat.image -x 0 -y 0
|
2337 |
|
|
} else {
|
2338 |
|
|
if { $synth::_heartbeat_ltor } {
|
2339 |
|
|
incr synth::_heartbeat_offset 4
|
2340 |
|
|
} else {
|
2341 |
|
|
incr synth::_heartbeat_offset -4
|
2342 |
|
|
}
|
2343 |
|
|
place configure .status.heartbeat.image -x $synth::_heartbeat_offset
|
2344 |
|
|
|
2345 |
|
|
if { $synth::_heartbeat_offset < (5 - $synth::_heartbeat_image_width) } {
|
2346 |
|
|
set synth::_heartbeat_ltor 1
|
2347 |
|
|
} elseif { $synth::_heartbeat_offset > ( $synth::_heartbeat_image_width -5) } {
|
2348 |
|
|
set synth::_heartbeat_ltor 0
|
2349 |
|
|
}
|
2350 |
|
|
after 100 synth::_heartbeat_update
|
2351 |
|
|
}
|
2352 |
|
|
}
|
2353 |
|
|
after 100 synth::_heartbeat_update
|
2354 |
|
|
|
2355 |
|
|
} elseif { 0 } {
|
2356 |
|
|
# The eCos logo, alternating between a normal and an inverse version
|
2357 |
|
|
variable _heartbeat_image_width [image width $synth::image_running1]
|
2358 |
|
|
variable _heartbeat_inverse ""
|
2359 |
|
|
variable _heartbeat_normal ""
|
2360 |
|
|
variable _heartbeat_inverse_width 1
|
2361 |
|
|
variable _heartbeat_normal_width 1
|
2362 |
|
|
|
2363 |
|
|
canvas .status.heartbeat_canvas -width [image width $synth::image_running1] -height [image height $synth::image_running1]
|
2364 |
|
|
pack .status.heartbeat_canvas -side left
|
2365 |
|
|
label .status.heartbeat_canvas.background -image $synth::image_running1 -anchor w -borderwidth 0
|
2366 |
|
|
label .status.heartbeat_canvas.inverse -image $synth::image_running2 -anchor w -borderwidth 0
|
2367 |
|
|
label .status.heartbeat_canvas.normal -image $synth::image_running1 -anchor w -borderwidth 0
|
2368 |
|
|
.status.heartbeat_canvas create window 0 0 -anchor nw -window .status.heartbeat_canvas.background
|
2369 |
|
|
set synth::_heartbeat_inverse [.status.heartbeat_canvas create window 0 0 -anchor nw -window .status.heartbeat_canvas.inverse]
|
2370 |
|
|
raise .status.heartbeat_canvas.inverse .status.heartbeat_canvas.background
|
2371 |
|
|
set synth::_heartbeat_normal [.status.heartbeat_canvas create window 0 0 -anchor nw -window .status.heartbeat_canvas.normal]
|
2372 |
|
|
raise .status.heartbeat_canvas.normal .status.heartbeat_canvas.inverse
|
2373 |
|
|
|
2374 |
|
|
.status.heartbeat_canvas itemconfigure $synth::_heartbeat_inverse -width $synth::_heartbeat_inverse_width
|
2375 |
|
|
.status.heartbeat_canvas itemconfigure $synth::_heartbeat_normal -width $synth::_heartbeat_normal_width
|
2376 |
|
|
|
2377 |
|
|
proc _heartbeat_update { } {
|
2378 |
|
|
if { ! $synth::ecos_running } {
|
2379 |
|
|
.status.heartbeat_canvas delete $synth::_heartbeat_inverse
|
2380 |
|
|
.status.heartbeat_canvas delete $synth::_heartbeat_normal
|
2381 |
|
|
} else {
|
2382 |
|
|
if { $synth::_heartbeat_inverse_width < $synth::_heartbeat_image_width } {
|
2383 |
|
|
incr synth::_heartbeat_inverse_width 2
|
2384 |
|
|
.status.heartbeat_canvas itemconfigure $synth::_heartbeat_inverse -width $synth::_heartbeat_inverse_width
|
2385 |
|
|
} elseif { $synth::_heartbeat_normal_width < $synth::_heartbeat_image_width } {
|
2386 |
|
|
incr synth::_heartbeat_normal_width 2
|
2387 |
|
|
.status.heartbeat_canvas itemconfigure $synth::_heartbeat_normal -width $synth::_heartbeat_normal_width
|
2388 |
|
|
} else {
|
2389 |
|
|
set synth::_heartbeat_inverse_width 1
|
2390 |
|
|
set synth::_heartbeat_normal_width 1
|
2391 |
|
|
.status.heartbeat_canvas itemconfigure $synth::_heartbeat_inverse -width $synth::_heartbeat_inverse_width
|
2392 |
|
|
.status.heartbeat_canvas itemconfigure $synth::_heartbeat_normal -width $synth::_heartbeat_normal_width
|
2393 |
|
|
}
|
2394 |
|
|
after 100 synth::_heartbeat_update
|
2395 |
|
|
}
|
2396 |
|
|
}
|
2397 |
|
|
after 100 synth::_heartbeat_update
|
2398 |
|
|
|
2399 |
|
|
} elseif { 0 } {
|
2400 |
|
|
# The eCos logo moving left to right, then replaced by a slightly smaller
|
2401 |
|
|
# mirror version moving right to left, sort of as if rotating around a torus
|
2402 |
|
|
variable _heartbeat_image_width [image width $synth::image_running1]
|
2403 |
|
|
variable _heartbeat_offset [expr -1 * [image width $synth::image_running1]]
|
2404 |
|
|
variable _heartbeat_ltor 1
|
2405 |
|
|
|
2406 |
|
|
frame .status.heartbeat -width $synth::_heartbeat_image_width -height [image height $synth::image_running1]
|
2407 |
|
|
pack .status.heartbeat -side left
|
2408 |
|
|
label .status.heartbeat.label -image $synth::image_running1 -anchor w -borderwidth 0
|
2409 |
|
|
|
2410 |
|
|
place .status.heartbeat.label -x $synth::_heartbeat_offset -y 0
|
2411 |
|
|
|
2412 |
|
|
proc _heartbeat_update { } {
|
2413 |
|
|
if { ! $synth::ecos_running } {
|
2414 |
|
|
.status.heartbeat.label configure -image $synth::image_running1
|
2415 |
|
|
place configure .status.heartbeat.label -x 0
|
2416 |
|
|
} else {
|
2417 |
|
|
if { $synth::_heartbeat_ltor } {
|
2418 |
|
|
incr synth::_heartbeat_offset 4
|
2419 |
|
|
} else {
|
2420 |
|
|
incr synth::_heartbeat_offset -4
|
2421 |
|
|
}
|
2422 |
|
|
place configure .status.heartbeat.label -x $synth::_heartbeat_offset
|
2423 |
|
|
if { $synth::_heartbeat_offset < (0 - $synth::_heartbeat_image_width) } {
|
2424 |
|
|
.status.heartbeat.label configure -image $synth::image_running1
|
2425 |
|
|
set synth::_heartbeat_ltor 1
|
2426 |
|
|
} elseif { $synth::_heartbeat_offset > $synth::_heartbeat_image_width } {
|
2427 |
|
|
.status.heartbeat.label configure -image $synth::image_running3
|
2428 |
|
|
set synth::_heartbeat_ltor 0
|
2429 |
|
|
}
|
2430 |
|
|
after 100 synth::_heartbeat_update
|
2431 |
|
|
}
|
2432 |
|
|
}
|
2433 |
|
|
after 100 synth::_heartbeat_update
|
2434 |
|
|
}
|
2435 |
|
|
|
2436 |
|
|
label .status.running -width 10 -text "Running" -anchor w
|
2437 |
|
|
pack .status.running -side left
|
2438 |
|
|
proc _heartbeat_exit_hook { arg_list } {
|
2439 |
|
|
.status.running configure -text "Exited"
|
2440 |
|
|
}
|
2441 |
|
|
synth::hook_add "ecos_exit" synth::_heartbeat_exit_hook
|
2442 |
|
|
|
2443 |
|
|
label .status.text -text "" -anchor w
|
2444 |
|
|
pack .status.text -side left -fill x -expand 1
|
2445 |
|
|
pack .status -side bottom -fill x
|
2446 |
|
|
}
|
2447 |
|
|
|
2448 |
|
|
# }}}
|
2449 |
|
|
# {{{ Preferences
|
2450 |
|
|
|
2451 |
|
|
namespace eval synth {
|
2452 |
|
|
|
2453 |
|
|
if { $synth::flag_debug } {
|
2454 |
|
|
synth::report "Setting up preferences window.\n"
|
2455 |
|
|
}
|
2456 |
|
|
|
2457 |
|
|
variable _pref_browser1 ""
|
2458 |
|
|
variable _pref_browser2 ""
|
2459 |
|
|
variable _pref_browser3 ""
|
2460 |
|
|
|
2461 |
|
|
toplevel .preferences
|
2462 |
|
|
wm title .preferences "ecosynth preferences"
|
2463 |
|
|
wm withdraw .preferences
|
2464 |
|
|
wm protocol .preferences "WM_DELETE_WINDOW" [list synth::_menu_edit_preferences_cancel]
|
2465 |
|
|
wm group .preferences .
|
2466 |
|
|
|
2467 |
|
|
# NOTE: the fixed-size padx/pady arguments should probably be determined
|
2468 |
|
|
# using a font calculation. The fixed width for the column 0 entries is also
|
2469 |
|
|
# a cheat.
|
2470 |
|
|
set _pref_col0_width 24
|
2471 |
|
|
|
2472 |
|
|
frame .preferences.help
|
2473 |
|
|
frame .preferences.help.frame -borderwidth 2 -relief groove
|
2474 |
|
|
pack .preferences.help.frame -fill both -expand 1 -pady 10
|
2475 |
|
|
frame .preferences.help.frame.blank -height 10
|
2476 |
|
|
label .preferences.help.frame.label1 -text "Preferred browser" -width $synth::_pref_col0_width -anchor w
|
2477 |
|
|
label .preferences.help.frame.label2 -text "First alternative" -width $synth::_pref_col0_width -anchor w
|
2478 |
|
|
label .preferences.help.frame.label3 -text "Second alternative" -width $synth::_pref_col0_width -anchor w
|
2479 |
|
|
entry .preferences.help.frame.entry1 -width 40 -relief sunken -textvariable synth::_pref_browser1
|
2480 |
|
|
entry .preferences.help.frame.entry2 -width 40 -relief sunken -textvariable synth::_pref_browser2
|
2481 |
|
|
entry .preferences.help.frame.entry3 -width 40 -relief sunken -textvariable synth::_pref_browser3
|
2482 |
|
|
grid .preferences.help.frame.blank -row 0 -column 0
|
2483 |
|
|
grid .preferences.help.frame.label1 -row 1 -column 0 -sticky w
|
2484 |
|
|
grid .preferences.help.frame.label2 -row 2 -column 0 -sticky w
|
2485 |
|
|
grid .preferences.help.frame.label3 -row 3 -column 0 -sticky w
|
2486 |
|
|
grid .preferences.help.frame.entry1 -row 1 -column 1 -sticky ew
|
2487 |
|
|
grid .preferences.help.frame.entry2 -row 2 -column 1 -sticky ew
|
2488 |
|
|
grid .preferences.help.frame.entry3 -row 3 -column 1 -sticky ew
|
2489 |
|
|
grid columnconfigure .preferences.help.frame 0 -weight 0
|
2490 |
|
|
grid columnconfigure .preferences.help.frame 1 -weight 1
|
2491 |
|
|
|
2492 |
|
|
label .preferences.help.title -text "Help"
|
2493 |
|
|
place .preferences.help.title -in .preferences.help.frame -relx .1 -x -5 -y -10 -bordermode outside
|
2494 |
|
|
pack .preferences.help -fill both -expand 1 -padx 10
|
2495 |
|
|
|
2496 |
|
|
frame .preferences.buttons
|
2497 |
|
|
button .preferences.buttons.ok -text "OK" -command [list synth::_menu_edit_preferences_ok]
|
2498 |
|
|
button .preferences.buttons.cancel -text "Cancel" -command [list synth::_menu_edit_preferences_cancel]
|
2499 |
|
|
pack .preferences.buttons.ok .preferences.buttons.cancel -side left -expand 1
|
2500 |
|
|
pack .preferences.buttons -side bottom -fill x -pady 4
|
2501 |
|
|
|
2502 |
|
|
frame .preferences.separator -height 2 -borderwidth 1 -relief sunken
|
2503 |
|
|
pack .preferences.separator -side bottom -fill x -pady 4
|
2504 |
|
|
|
2505 |
|
|
bind .preferences <KeyPress-Return> [list synth::_menu_edit_preferences_ok]
|
2506 |
|
|
bind .preferences <KeyPress-Escape> [list synth::_menu_edit_preferences_cancel]
|
2507 |
|
|
|
2508 |
|
|
variable _saved_focus ""
|
2509 |
|
|
proc _menu_edit_preferences { } {
|
2510 |
|
|
set synth::_saved_focus [focus]
|
2511 |
|
|
set synth::_pref_browser1 $synth::_browser1
|
2512 |
|
|
set synth::_pref_browser2 $synth::_browser2
|
2513 |
|
|
set synth::_pref_browser3 $synth::_browser3
|
2514 |
|
|
if { "normal" == [wm state .preferences] } {
|
2515 |
|
|
raise .preferences
|
2516 |
|
|
} else {
|
2517 |
|
|
wm deiconify .preferences
|
2518 |
|
|
}
|
2519 |
|
|
focus .preferences.help.frame.entry1
|
2520 |
|
|
}
|
2521 |
|
|
|
2522 |
|
|
proc _menu_edit_preferences_ok { } {
|
2523 |
|
|
if { $synth::_browser1 != $synth::_pref_browser1 } {
|
2524 |
|
|
set synth::_browser1 $synth::_pref_browser1
|
2525 |
|
|
}
|
2526 |
|
|
if { $synth::_browser2 != $synth::_pref_browser2 } {
|
2527 |
|
|
set synth::_browser2 $synth::_pref_browser2
|
2528 |
|
|
}
|
2529 |
|
|
if { $synth::_browser3 != $synth::_pref_browser3 } {
|
2530 |
|
|
set synth::_browser3 $synth::_pref_browser3
|
2531 |
|
|
}
|
2532 |
|
|
|
2533 |
|
|
wm withdraw .preferences
|
2534 |
|
|
catch { focus $synth::_saved_focus }
|
2535 |
|
|
}
|
2536 |
|
|
|
2537 |
|
|
proc _menu_edit_preferences_cancel { } {
|
2538 |
|
|
wm withdraw .preferences
|
2539 |
|
|
catch { focus $synth::_saved_focus }
|
2540 |
|
|
}
|
2541 |
|
|
|
2542 |
|
|
.menubar.edit add command -label "Preferences..." -command [list synth::_menu_edit_preferences]
|
2543 |
|
|
}
|
2544 |
|
|
|
2545 |
|
|
# }}}
|
2546 |
|
|
# {{{ Clean-up
|
2547 |
|
|
|
2548 |
|
|
# ----------------------------------------------------------------------------
|
2549 |
|
|
# GUI clean-up.
|
2550 |
|
|
#
|
2551 |
|
|
# Once all the device-specific scripts have been loaded and initialized, it
|
2552 |
|
|
# is time to go through the various components of the GUI and clean up
|
2553 |
|
|
# anything that is not actually required.
|
2554 |
|
|
namespace eval synth {
|
2555 |
|
|
|
2556 |
|
|
proc _cleanup_gui { } {
|
2557 |
|
|
|
2558 |
|
|
if { $synth::flag_debug } {
|
2559 |
|
|
synth::report "Cleaning up unused GUI items.\n"
|
2560 |
|
|
}
|
2561 |
|
|
|
2562 |
|
|
# File, Edit, View and Help should always have contents, unless
|
2563 |
|
|
# the user has deleted entries via the mainrc file. The Windows
|
2564 |
|
|
# menu will be empty unless contents have been added. There is
|
2565 |
|
|
# always a global binding for ctrl-Q, and the window manager
|
2566 |
|
|
# should always provide a way of killing off the application,
|
2567 |
|
|
# so there is no need to treat File specially.
|
2568 |
|
|
if { 0 == [.menubar.file index end] } {
|
2569 |
|
|
.menubar delete "File"
|
2570 |
|
|
}
|
2571 |
|
|
if { 0 == [.menubar.edit index end] } {
|
2572 |
|
|
.menubar delete "Edit"
|
2573 |
|
|
}
|
2574 |
|
|
if { 0 == [.menubar.view index end] } {
|
2575 |
|
|
.menubar delete "View"
|
2576 |
|
|
}
|
2577 |
|
|
if { 0 == [.menubar.windows index end] } {
|
2578 |
|
|
.menubar delete "Windows"
|
2579 |
|
|
}
|
2580 |
|
|
if { 0 == [.menubar.help index end] } {
|
2581 |
|
|
.menubar delete "Help"
|
2582 |
|
|
}
|
2583 |
|
|
|
2584 |
|
|
# If the toolbar is empty get rid of it.
|
2585 |
|
|
if { 0 == [llength [winfo children .toolbar]] } {
|
2586 |
|
|
pack forget .toolbar
|
2587 |
|
|
destroy .toolbar
|
2588 |
|
|
}
|
2589 |
|
|
|
2590 |
|
|
set can_destroy [list]
|
2591 |
|
|
# Remove some or all of the top, left hand, right hand or bottom
|
2592 |
|
|
# sets of frames, if nobody is using them.
|
2593 |
|
|
if { (0 == [llength [pack slaves .main.nw]]) &&
|
2594 |
|
|
(0 == [llength [pack slaves .main.n]]) &&
|
2595 |
|
|
(0 == [llength [pack slaves .main.ne]]) } {
|
2596 |
|
|
lappend can_destroy .main.nw .main.border_nw_n .main.n .main.border_n_ne .main.ne
|
2597 |
|
|
lappend can_destroy .main.border_nw_w .main.border_n_centre .main.border_ne_e
|
2598 |
|
|
}
|
2599 |
|
|
if { (0 == [llength [pack slaves .main.nw]]) &&
|
2600 |
|
|
(0 == [llength [pack slaves .main.w]]) &&
|
2601 |
|
|
(0 == [llength [pack slaves .main.sw]]) } {
|
2602 |
|
|
lappend can_destroy .main.nw .main.border_nw_w .main.w .main.border_w_sw .main.sw
|
2603 |
|
|
lappend can_destroy .main.border_nw_n .main.border_w_centre .main.border_w_sw
|
2604 |
|
|
}
|
2605 |
|
|
if { (0 == [llength [pack slaves .main.ne]]) &&
|
2606 |
|
|
(0 == [llength [pack slaves .main.e]]) &&
|
2607 |
|
|
(0 == [llength [pack slaves .main.se]]) } {
|
2608 |
|
|
lappend can_destroy .main.ne .main.border_ne_e .main.e .main.border_e_se .main.se
|
2609 |
|
|
lappend can_destroy .main.border_n_ne .main.border_centre_e .main.border_s_se
|
2610 |
|
|
}
|
2611 |
|
|
if { (0 == [llength [pack slaves .main.sw]]) &&
|
2612 |
|
|
(0 == [llength [pack slaves .main.s]]) &&
|
2613 |
|
|
(0 == [llength [pack slaves .main.se]]) } {
|
2614 |
|
|
lappend can_destroy .main.sw .main.border_sw_s .main.s .main.border_s_se .main.se
|
2615 |
|
|
lappend can_destroy .main.border_w_sw .main.border_centre_s .main.border_e_se
|
2616 |
|
|
}
|
2617 |
|
|
|
2618 |
|
|
foreach frame [lsort -unique $can_destroy] {
|
2619 |
|
|
grid forget $frame
|
2620 |
|
|
}
|
2621 |
|
|
foreach frame [lsort -unique $can_destroy] {
|
2622 |
|
|
destroy $frame
|
2623 |
|
|
}
|
2624 |
|
|
|
2625 |
|
|
# Now that the full window layout is known the .main frame can be
|
2626 |
|
|
# packed. Doing this before now could cause problems because the
|
2627 |
|
|
# desired sizes of the subwindows are not known.
|
2628 |
|
|
pack .main -expand 1 -fill both
|
2629 |
|
|
}
|
2630 |
|
|
}
|
2631 |
|
|
|
2632 |
|
|
# }}}
|
2633 |
|
|
# {{{ Screen dump support
|
2634 |
|
|
|
2635 |
|
|
# Create screen dumps for the main window or for various subwindows.
|
2636 |
|
|
# Normally disabled, but useful when generating documentation.
|
2637 |
|
|
# FIXME: there seem to be problems getting the desired info about
|
2638 |
|
|
# transient windows, e.g. sizes. Hence the generated dumps still
|
2639 |
|
|
# require a lot of hand editing for now.
|
2640 |
|
|
if { 0 } {
|
2641 |
|
|
|
2642 |
|
|
bind . <Alt-w> {
|
2643 |
|
|
exec xwd -out main.xwd -frame -id [winfo id .]
|
2644 |
|
|
}
|
2645 |
|
|
|
2646 |
|
|
bind . <Alt-f> {
|
2647 |
|
|
.menubar invoke "File"
|
2648 |
|
|
after 100 exec xwd -out menu_file.xwd -frame -id [winfo id .]
|
2649 |
|
|
}
|
2650 |
|
|
|
2651 |
|
|
bind . <Alt-e> {
|
2652 |
|
|
.menubar invoke "Edit"
|
2653 |
|
|
after 100 exec xwd -out menu_edit.xwd -frame -id [winfo id .]
|
2654 |
|
|
}
|
2655 |
|
|
|
2656 |
|
|
bind . <Alt-v> {
|
2657 |
|
|
.menubar invoke "View"
|
2658 |
|
|
after 100 exec xwd -out menu_view.xwd -frame -id [winfo id .]
|
2659 |
|
|
}
|
2660 |
|
|
|
2661 |
|
|
# The Help menu will extend beyond the window boundaries
|
2662 |
|
|
bind . <Alt-h> {
|
2663 |
|
|
.menubar invoke "Help"
|
2664 |
|
|
after 100 exec xwd -out menu_help.xwd -root
|
2665 |
|
|
}
|
2666 |
|
|
}
|
2667 |
|
|
|
2668 |
|
|
# }}}
|
2669 |
|
|
|
2670 |
|
|
# }}}
|
2671 |
|
|
}
|
2672 |
|
|
|
2673 |
|
|
# {{{ Device instantiation
|
2674 |
|
|
|
2675 |
|
|
# ----------------------------------------------------------------------------
|
2676 |
|
|
# This code handles the loading of device-specific scripts in response
|
2677 |
|
|
# to requests from the eCos application, and the instantiation of devices.
|
2678 |
|
|
# The application's request provides four pieces of information, held in
|
2679 |
|
|
# null-terminated strings in the request buffer:
|
2680 |
|
|
#
|
2681 |
|
|
# package name e.g. hal/synth/arch
|
2682 |
|
|
# package version e.g. current
|
2683 |
|
|
# device type e.g. console or ethernet
|
2684 |
|
|
# device instance e.g. eth0, or an empty string
|
2685 |
|
|
# device data e.g. 1024x768 for frame buffer resolution
|
2686 |
|
|
#
|
2687 |
|
|
# The first two pieces of information can be concatenated to give a
|
2688 |
|
|
# path to the install location. The third identifies a suitable
|
2689 |
|
|
# tcl script, e.g. console.tcl. This is sufficient to locate and load
|
2690 |
|
|
# the tcl script. It should return an instantiation procedure which will
|
2691 |
|
|
# be invoked with the instance name (or an empty string if there will only
|
2692 |
|
|
# ever be one instance of this device type). The instantiation procedure
|
2693 |
|
|
# will then be called with a number and the device instance string, and
|
2694 |
|
|
# should return a handler for all requests intended for that device.
|
2695 |
|
|
#
|
2696 |
|
|
# If the package name and version are empty strings then an application-specific
|
2697 |
|
|
# device is being initialized, and the code will search in the current
|
2698 |
|
|
# directory and in ~/.ecos/synth
|
2699 |
|
|
|
2700 |
|
|
namespace eval synth {
|
2701 |
|
|
# Map package/version/type on to an instantiation procedure
|
2702 |
|
|
array set _instantiation_procs [list]
|
2703 |
|
|
|
2704 |
|
|
# Map device instances on to handlers.
|
2705 |
|
|
array set _device_handlers [list]
|
2706 |
|
|
array set _device_names [list]
|
2707 |
|
|
variable _next_device_id 1
|
2708 |
|
|
|
2709 |
|
|
# Let scripts know their install location and their source dir
|
2710 |
|
|
variable device_install_dir ""
|
2711 |
|
|
variable device_src_dir ""
|
2712 |
|
|
|
2713 |
|
|
# One handler is predefined.
|
2714 |
|
|
set synth::_device_handlers(0) synth::_handle_ecosynth_requests
|
2715 |
|
|
set synth::_device_names(0) "ecosynth I/O auxiliary"
|
2716 |
|
|
|
2717 |
|
|
proc _handle_INSTANTIATE { data } {
|
2718 |
|
|
|
2719 |
|
|
set list [split $data \0]
|
2720 |
|
|
if { [llength $list] < 5 } {
|
2721 |
|
|
synth::send_reply -1 0 ""
|
2722 |
|
|
return
|
2723 |
|
|
}
|
2724 |
|
|
set package_dir [lindex $list 0]
|
2725 |
|
|
set package_version [lindex $list 1]
|
2726 |
|
|
set device_type [lindex $list 2]
|
2727 |
|
|
set device_instance [lindex $list 3]
|
2728 |
|
|
set device_data [lindex $list 4]
|
2729 |
|
|
|
2730 |
|
|
if { ![info exists synth::_instantiation_procs($package_dir,$package_version,$device_type)] } {
|
2731 |
|
|
# The required script has not yet been loaded.
|
2732 |
|
|
if { "" != $package_dir } {
|
2733 |
|
|
# The device is provided by a package
|
2734 |
|
|
set synth::device_install_dir [file join $synth::_ecosynth_libexecdir "ecos" $package_dir $package_version]
|
2735 |
|
|
set synth::device_src_dir [file join $synth::_ecosynth_repository $package_dir $package_version]
|
2736 |
|
|
|
2737 |
|
|
set script [file join $::synth::device_install_dir "[set device_type].tcl"]
|
2738 |
|
|
if { ![file exists $script] } {
|
2739 |
|
|
synth::report_error "Unable to initialize device $device_type\n Script \"$script\" not found.\n"
|
2740 |
|
|
synth::send_reply -1 0 ""
|
2741 |
|
|
return
|
2742 |
|
|
} elseif { ![file readable $script] } {
|
2743 |
|
|
synth::report_error "Unable to initialize device $device_type\n Script \"$script\" not readable.\n"
|
2744 |
|
|
synth::send_reply -1 0 ""
|
2745 |
|
|
return
|
2746 |
|
|
}
|
2747 |
|
|
|
2748 |
|
|
# Is there a more recent version in the repository
|
2749 |
|
|
if { [info exists ::env(ECOSYNTH_DEVEL)] } {
|
2750 |
|
|
set _orig_name [file join $synth::device_src_dir "host" "[set device_type].tcl"]
|
2751 |
|
|
if { [file exists $_orig_name] && [file readable $_orig_name] } {
|
2752 |
|
|
if { [file mtime $_orig_name] >= [file mtime $script] } {
|
2753 |
|
|
puts "$_orig_name is more recent than install: executing that."
|
2754 |
|
|
set script $_orig_name
|
2755 |
|
|
}
|
2756 |
|
|
}
|
2757 |
|
|
}
|
2758 |
|
|
} else {
|
2759 |
|
|
# The device is application-specific
|
2760 |
|
|
set script [file join [pwd] "[set device_type].tcl"]
|
2761 |
|
|
if { ![file exists $script] || ![file readable $script] } {
|
2762 |
|
|
set script [file join "~/.ecos/synth" "[set device_type].tcl"]
|
2763 |
|
|
if { ![file exists $script] || ![file readable $script] } {
|
2764 |
|
|
synth::report_error "Unable to initialize device $device_type\n Script $device_type.tcl not found in [pwd] or ~/.ecos/synth\n"
|
2765 |
|
|
synth::send_reply -1 0 ""
|
2766 |
|
|
return
|
2767 |
|
|
}
|
2768 |
|
|
}
|
2769 |
|
|
}
|
2770 |
|
|
|
2771 |
|
|
# The uplevel ensures that the device script operates at the global
|
2772 |
|
|
# level, so any namespaces it creates are also at global level
|
2773 |
|
|
# and not nested inside synth. This avoids having to add
|
2774 |
|
|
# synth:: to lots of variable accesses and generally avoids confusion
|
2775 |
|
|
set result [catch { uplevel #0 source $script } instantiator]
|
2776 |
|
|
if { 0 != $result } {
|
2777 |
|
|
synth::report_error "Unable to initialize device $device_type\n Error loading script \"$script\"\n $instantiator\n"
|
2778 |
|
|
synth::send_reply -1 0 ""
|
2779 |
|
|
return
|
2780 |
|
|
}
|
2781 |
|
|
|
2782 |
|
|
set synth::_instantiation_procs($package_dir,$package_version,$device_type) $instantiator
|
2783 |
|
|
}
|
2784 |
|
|
|
2785 |
|
|
set handler [$synth::_instantiation_procs($package_dir,$package_version,$device_type) \
|
2786 |
|
|
$synth::_next_device_id $device_instance $device_data]
|
2787 |
|
|
if { "" == $handler } {
|
2788 |
|
|
synth::send_reply -1 0 ""
|
2789 |
|
|
} else {
|
2790 |
|
|
set result $synth::_next_device_id
|
2791 |
|
|
incr synth::_next_device_id
|
2792 |
|
|
|
2793 |
|
|
set synth::_device_handlers($result) $handler
|
2794 |
|
|
if { "" != $device_instance } {
|
2795 |
|
|
set synth::_device_names($result) $device_instance
|
2796 |
|
|
} else {
|
2797 |
|
|
set synth::_device_names($result) $device_type
|
2798 |
|
|
}
|
2799 |
|
|
synth::send_reply $result 0 ""
|
2800 |
|
|
}
|
2801 |
|
|
}
|
2802 |
|
|
}
|
2803 |
|
|
|
2804 |
|
|
# }}}
|
2805 |
|
|
# {{{ Interrupt handling
|
2806 |
|
|
|
2807 |
|
|
# ----------------------------------------------------------------------------
|
2808 |
|
|
# Interrupt handling. Device handlers can request an interrupt number
|
2809 |
|
|
# using allocate_interrupt, and typically they will transmit this
|
2810 |
|
|
# number to the eCos device driver during initialization. Device handlers
|
2811 |
|
|
# can at any time call raise_interrupt with that number, which typically
|
2812 |
|
|
# will result in SIGIO being sent to the eCos application. The latter will
|
2813 |
|
|
# send a request to retrieve a mask of current pending interrupts.
|
2814 |
|
|
#
|
2815 |
|
|
# Exit handling, in the sense of the user selecting File->Exit, is also
|
2816 |
|
|
# handled here. Such an exit request also involves raising SIGIO and
|
2817 |
|
|
# then sending a specially format response to the get-pending request.
|
2818 |
|
|
|
2819 |
|
|
namespace eval synth {
|
2820 |
|
|
|
2821 |
|
|
# The next interrupt number to be allocated. Interrupt source 0 is reserved
|
2822 |
|
|
# for the timer, which is handled within eCos itself via SIGALRM
|
2823 |
|
|
# rather than by the I/O auxiliary.
|
2824 |
|
|
variable _interrupt_next 1
|
2825 |
|
|
|
2826 |
|
|
# Keep track of which interrupts belong to which devices, for display and
|
2827 |
|
|
# diagnostic purposes.
|
2828 |
|
|
array set _interrupt_names [list]
|
2829 |
|
|
set _interrupt_names(0) "system clock"
|
2830 |
|
|
|
2831 |
|
|
# A mask of current pending interrupts
|
2832 |
|
|
variable _interrupt_pending 0
|
2833 |
|
|
|
2834 |
|
|
# Is an exit request pending?
|
2835 |
|
|
variable _interrupt_exit_pending 0
|
2836 |
|
|
|
2837 |
|
|
# Allow other code to hook into the interrupt system, e.g. to display
|
2838 |
|
|
# pending interrupts.
|
2839 |
|
|
synth::hook_define "interrupt"
|
2840 |
|
|
|
2841 |
|
|
# For now interrupts are always allocated dynamically, which effectively
|
2842 |
|
|
# means in the order of C++ static constructors. This means that interrupt
|
2843 |
|
|
# allocation depends on the application, and may even change as the application
|
2844 |
|
|
# is relinked.
|
2845 |
|
|
#
|
2846 |
|
|
# An alternative approach would allow device scripts to request specific
|
2847 |
|
|
# interrupt numbers, making the system a bit more deterministic, but
|
2848 |
|
|
# introducing complications such as shared interrupt numbers. On the other
|
2849 |
|
|
# hand that would make it easier to test chained interrupt support and
|
2850 |
|
|
# the like.
|
2851 |
|
|
# FIXME: add support for allocating specific interrupt numbers
|
2852 |
|
|
proc interrupt_allocate { name } {
|
2853 |
|
|
if { $synth::_interrupt_next == 32 } {
|
2854 |
|
|
synth::report_error "Unable to allocate an interrupt vector for $name\nAll 32 interrupt vectors are already in use.\n"
|
2855 |
|
|
return -1
|
2856 |
|
|
}
|
2857 |
|
|
set result $synth::_interrupt_next
|
2858 |
|
|
set synth::_interrupt_names($result) $name
|
2859 |
|
|
incr synth::_interrupt_next
|
2860 |
|
|
return $result
|
2861 |
|
|
}
|
2862 |
|
|
|
2863 |
|
|
# Allow information about the device->interrupt mappings to be retrieved
|
2864 |
|
|
proc interrupt_get_max { } {
|
2865 |
|
|
return [expr $synth::_interrupt_next - 1]
|
2866 |
|
|
}
|
2867 |
|
|
proc interrupt_get_devicename { number } {
|
2868 |
|
|
if { [info exists synth::_interrupt_names($number) ] } {
|
2869 |
|
|
return $synth::_interrupt_names($number)
|
2870 |
|
|
} else {
|
2871 |
|
|
return ""
|
2872 |
|
|
}
|
2873 |
|
|
}
|
2874 |
|
|
|
2875 |
|
|
# Raise a specific interrupt. If the interrupt is already pending
|
2876 |
|
|
# this has no effect because a SIGIO will have been sent to the
|
2877 |
|
|
# eCos application already. Otherwise SIGIO needs to be raised.
|
2878 |
|
|
proc interrupt_raise { number } {
|
2879 |
|
|
if { $number >= $synth::_interrupt_next } {
|
2880 |
|
|
error "Attempt to raise invalid interrupt $number."
|
2881 |
|
|
}
|
2882 |
|
|
if { !$synth::ecos_running } {
|
2883 |
|
|
return
|
2884 |
|
|
}
|
2885 |
|
|
set or_mask [expr 0x01 << $number]
|
2886 |
|
|
if { 0 == ($or_mask & $synth::_interrupt_pending) } {
|
2887 |
|
|
# This interrupt was not previously pending, so action is needed.
|
2888 |
|
|
set synth::_interrupt_pending [expr $synth::_interrupt_pending | $or_mask]
|
2889 |
|
|
synth::hook_call "interrupt" $number
|
2890 |
|
|
synth::_send_SIGIO
|
2891 |
|
|
}
|
2892 |
|
|
}
|
2893 |
|
|
|
2894 |
|
|
# Request application exit. This is typically called in response to
|
2895 |
|
|
# File->Exit.
|
2896 |
|
|
proc request_application_exit { } {
|
2897 |
|
|
set synth::_interrupt_exit_pending 1
|
2898 |
|
|
synth::_send_SIGIO
|
2899 |
|
|
}
|
2900 |
|
|
|
2901 |
|
|
# The eCos application wants to know about pending interrupts. It maintains
|
2902 |
|
|
# its own set of pending interrupts, so once the information has been
|
2903 |
|
|
# transferred there are no pending interrupts left in the I/O auxiliary,
|
2904 |
|
|
# only in the eCos app. A pending exit is indicated by non-empty data,
|
2905 |
|
|
# the actual data does not matter.
|
2906 |
|
|
proc _handle_GET_IRQ_PENDING { } {
|
2907 |
|
|
if { $synth::_interrupt_exit_pending } {
|
2908 |
|
|
synth::send_reply $synth::_interrupt_pending 1 "x"
|
2909 |
|
|
} else {
|
2910 |
|
|
synth::send_reply $synth::_interrupt_pending 0 ""
|
2911 |
|
|
}
|
2912 |
|
|
set synth::_interrupt_pending 0
|
2913 |
|
|
}
|
2914 |
|
|
}
|
2915 |
|
|
|
2916 |
|
|
# }}}
|
2917 |
|
|
# {{{ Initialization complete
|
2918 |
|
|
|
2919 |
|
|
# ----------------------------------------------------------------------------
|
2920 |
|
|
# This is called once all static constructors have been run, i.e. when all
|
2921 |
|
|
# eCos devices should be initialized. It does the following:
|
2922 |
|
|
#
|
2923 |
|
|
# 1) invoke any "initialized" hooks set up by device scripts.
|
2924 |
|
|
#
|
2925 |
|
|
# 2) run the per-user mainrc.tcl script, if it exists, so that users can
|
2926 |
|
|
# install hooks, modify the GUI display, etc.
|
2927 |
|
|
#
|
2928 |
|
|
# 3) warn about any unused command line arguments
|
2929 |
|
|
#
|
2930 |
|
|
# 4) optionally warn about any unused entries in the target definition file
|
2931 |
|
|
#
|
2932 |
|
|
# 5) clean up the GUI, e.g. remove unwanted windows and borders, and display it.
|
2933 |
|
|
#
|
2934 |
|
|
# However if the user specified --help then, instead of all the above,
|
2935 |
|
|
# a help message is displayed and the auxiliary exits, hopefully taking the
|
2936 |
|
|
# eCos application with it.
|
2937 |
|
|
|
2938 |
|
|
namespace eval synth {
|
2939 |
|
|
|
2940 |
|
|
proc _handle_CONSTRUCTORS_DONE { } {
|
2941 |
|
|
|
2942 |
|
|
if { $synth::flag_help } {
|
2943 |
|
|
puts "Usage : <eCos application> <options>"
|
2944 |
|
|
puts " Options are passed to the I/O auxiliary, and are not"
|
2945 |
|
|
puts " accessible to the eCos application."
|
2946 |
|
|
puts "Standard options:"
|
2947 |
|
|
puts " -io : run with I/O facilities."
|
2948 |
|
|
puts " -nio : run with no I/O facilities at all."
|
2949 |
|
|
puts " -nw, --no-windows : run in console mode."
|
2950 |
|
|
puts " -w, --windows : run in GUI mode (default)."
|
2951 |
|
|
puts " -v, --version : display the version of the I/O auxiliary."
|
2952 |
|
|
puts " -h, --help : show this help text."
|
2953 |
|
|
puts " -k, --keep-going : ignore errors in init scripts or the"
|
2954 |
|
|
puts " target definition file."
|
2955 |
|
|
puts " -nr, --no-rc : do not run the user's init scripts."
|
2956 |
|
|
puts " -x, --exit : terminate I/O auxiliary as soon as the eCos"
|
2957 |
|
|
puts " application exits (default in console mode)."
|
2958 |
|
|
puts " -nx, --no-exit : I/O auxiliary keeps running even after eCos"
|
2959 |
|
|
puts " application has exited (default in GUI mode)."
|
2960 |
|
|
puts " -V, --verbose : provide additional output during the run."
|
2961 |
|
|
puts " -l <file>, --logfile <file> : send all output to the specified file. In"
|
2962 |
|
|
puts " GUI mode this in addition to the main text"
|
2963 |
|
|
puts " window. In console mode this is instead of"
|
2964 |
|
|
puts " stdout."
|
2965 |
|
|
puts " -t <file>, --target <file> : use the specified .tdf file as the target"
|
2966 |
|
|
puts " definition. The auxiliary will look for this"
|
2967 |
|
|
puts " file in the current directory, ~/.ecos, and"
|
2968 |
|
|
puts " finally the install location."
|
2969 |
|
|
puts " -geometry <geometry> : size and position for the main window."
|
2970 |
|
|
synth::hook_call "help"
|
2971 |
|
|
exit 1
|
2972 |
|
|
}
|
2973 |
|
|
|
2974 |
|
|
synth::hook_call "ecos_initialized"
|
2975 |
|
|
|
2976 |
|
|
# ----------------------------------------------------------------------------
|
2977 |
|
|
if { !$synth::flag_no_rc } {
|
2978 |
|
|
set _config_file [file join "~/.ecos/synth" "mainrc.tcl"]
|
2979 |
|
|
if { [file exists $_config_file] } {
|
2980 |
|
|
if { [file readable $_config_file] } {
|
2981 |
|
|
if { [catch { source $_config_file } msg ] } {
|
2982 |
|
|
set error "Failed to execute user initialization file \"$_config_file\"\n"
|
2983 |
|
|
append error " $msg\n"
|
2984 |
|
|
if { $synth::flag_verbose } {
|
2985 |
|
|
append error "------- backtrace ------------------------------------------\n"
|
2986 |
|
|
append error $::errorInfo
|
2987 |
|
|
append error "\n------- backtrace ends -------------------------------------\n"
|
2988 |
|
|
}
|
2989 |
|
|
synth::report_error $error
|
2990 |
|
|
}
|
2991 |
|
|
} else {
|
2992 |
|
|
synth::report_error "No read access to user initialization file \"$_config_file\"\n"
|
2993 |
|
|
}
|
2994 |
|
|
}
|
2995 |
|
|
unset _config_file
|
2996 |
|
|
}
|
2997 |
|
|
|
2998 |
|
|
# ----------------------------------------------------------------------------
|
2999 |
|
|
# Report any arguments that have not been used up by the auxiliary itself
|
3000 |
|
|
# or by any device handlers
|
3001 |
|
|
set unconsumed_args [synth::argv_get_unconsumed]
|
3002 |
|
|
foreach arg $unconsumed_args {
|
3003 |
|
|
synth::report_warning "Unrecognised command line option \"$arg\", ignored.\n"
|
3004 |
|
|
}
|
3005 |
|
|
|
3006 |
|
|
# ----------------------------------------------------------------------------
|
3007 |
|
|
if { $synth::flag_verbose } {
|
3008 |
|
|
set unconsumed_devices [synth::tdf_get_unconsumed_devices]
|
3009 |
|
|
set unconsumed_options [synth::tdf_get_unconsumed_options]
|
3010 |
|
|
if { (0 != [llength $unconsumed_devices]) || (0 != [llength $unconsumed_options]) } {
|
3011 |
|
|
set msg "Target definition file $synth::target_definition\n"
|
3012 |
|
|
foreach dev $unconsumed_devices {
|
3013 |
|
|
append msg " synth_device \"$dev\" not recognised.\n"
|
3014 |
|
|
}
|
3015 |
|
|
foreach option $unconsumed_options {
|
3016 |
|
|
set dev [lindex $option 0]
|
3017 |
|
|
set opt [lindex $option 1]
|
3018 |
|
|
append msg " synth_device \"$dev\", option \"$opt\" not recognised.\n"
|
3019 |
|
|
}
|
3020 |
|
|
synth::report_warning $msg
|
3021 |
|
|
}
|
3022 |
|
|
}
|
3023 |
|
|
|
3024 |
|
|
# ----------------------------------------------------------------------------
|
3025 |
|
|
if { $synth::flag_gui } {
|
3026 |
|
|
synth::_cleanup_gui
|
3027 |
|
|
wm deiconify .
|
3028 |
|
|
}
|
3029 |
|
|
|
3030 |
|
|
# ----------------------------------------------------------------------------
|
3031 |
|
|
# Finally send a reply back to the application so it can really
|
3032 |
|
|
# start running. Alternatively, if any errors occurred during
|
3033 |
|
|
# initialization and the user did not specify --keep-going then
|
3034 |
|
|
# send back an error code, causing the eCos application to terminate.
|
3035 |
|
|
if { (0 == $synth::_error_count) || $synth::flag_keep_going } {
|
3036 |
|
|
synth::send_reply 1 0 ""
|
3037 |
|
|
} else {
|
3038 |
|
|
synth::send_reply 0 0 ""
|
3039 |
|
|
}
|
3040 |
|
|
}
|
3041 |
|
|
}
|
3042 |
|
|
|
3043 |
|
|
# }}}
|
3044 |
|
|
# {{{ Requests for the I/O auxiliary itself
|
3045 |
|
|
|
3046 |
|
|
# ----------------------------------------------------------------------------
|
3047 |
|
|
# There are three requests which can be aimed at the I/O auxiliary itself,
|
3048 |
|
|
# rather than at device-specific scripts. These are: INSTANTIATE to instantiate
|
3049 |
|
|
# a device; CONSTRUCTORS_DONE to indicate when initialization is complete;
|
3050 |
|
|
# and GET_IRQ_PENDING which deals with interrupts.
|
3051 |
|
|
|
3052 |
|
|
namespace eval synth {
|
3053 |
|
|
|
3054 |
|
|
proc _handle_ecosynth_requests { devid request arg1 arg2 request_data request_len reply_len } {
|
3055 |
|
|
if { 0x01 == $request } {
|
3056 |
|
|
synth::_handle_INSTANTIATE $request_data
|
3057 |
|
|
} elseif { 0x02 == $request } {
|
3058 |
|
|
synth::_handle_CONSTRUCTORS_DONE
|
3059 |
|
|
} elseif { 0x03 == $request } {
|
3060 |
|
|
synth::_handle_GET_IRQ_PENDING
|
3061 |
|
|
} elseif { 0x04 == $request } {
|
3062 |
|
|
synth::_handle_GET_VERSION
|
3063 |
|
|
} else {
|
3064 |
|
|
error "The eCos application has sent an invalid request sent to the I/O auxiliary"
|
3065 |
|
|
}
|
3066 |
|
|
}
|
3067 |
|
|
|
3068 |
|
|
variable _SYNTH_AUXILIARY_PROTOCOL_VERSION 0x01
|
3069 |
|
|
proc _handle_GET_VERSION { } {
|
3070 |
|
|
synth::send_reply $synth::_SYNTH_AUXILIARY_PROTOCOL_VERSION 0 ""
|
3071 |
|
|
}
|
3072 |
|
|
}
|
3073 |
|
|
|
3074 |
|
|
# }}}
|
3075 |
|
|
# {{{ Application exit
|
3076 |
|
|
|
3077 |
|
|
# ----------------------------------------------------------------------------
|
3078 |
|
|
# The application has exited. This is detected by an EOF event on the pipe
|
3079 |
|
|
# from the eCos application.
|
3080 |
|
|
#
|
3081 |
|
|
# First the rest of the system is informed about the event using the
|
3082 |
|
|
# appropriate hook. This should ensure that the various device-specific
|
3083 |
|
|
# scripts do the right thing, e.g shut down sub-processes. Next, if
|
3084 |
|
|
# the immediate exit flag is set then that is obeyed. This flag is set by
|
3085 |
|
|
# default when in command-line mode because there is no point in continuing
|
3086 |
|
|
# to run if there is neither an application nor a GUI for the user to interact
|
3087 |
|
|
# with. It also gets set if the user has explicitly requested an exit.
|
3088 |
|
|
#
|
3089 |
|
|
# The exit call will invoke the appropriate hooks.
|
3090 |
|
|
namespace eval synth {
|
3091 |
|
|
|
3092 |
|
|
proc _application_has_exited { } {
|
3093 |
|
|
|
3094 |
|
|
set synth::ecos_running 0
|
3095 |
|
|
synth::hook_call "ecos_exit"
|
3096 |
|
|
|
3097 |
|
|
# Depending on command-line arguments and whether or not the GUI is present,
|
3098 |
|
|
# the auxiliary should now exit
|
3099 |
|
|
if { $synth::flag_immediate_exit } {
|
3100 |
|
|
exit 0
|
3101 |
|
|
} elseif { !$synth::flag_gui } {
|
3102 |
|
|
synth::report "eCos application has exited: I/O auxiliary still running in the background.\n"
|
3103 |
|
|
}
|
3104 |
|
|
}
|
3105 |
|
|
}
|
3106 |
|
|
|
3107 |
|
|
# }}}
|
3108 |
|
|
# {{{ Communication with the eCos application
|
3109 |
|
|
|
3110 |
|
|
namespace eval synth {
|
3111 |
|
|
|
3112 |
|
|
# ----------------------------------------------------------------------------
|
3113 |
|
|
# The basic communication routines between the auxiliary and the
|
3114 |
|
|
# eCos application. _read_request is invoked whenever there is
|
3115 |
|
|
# a pending event on the pipe from the eCos application, either
|
3116 |
|
|
# a request or an EOF. It
|
3117 |
|
|
|
3118 |
|
|
# Keep track of a couple of things to detect protocol mismatches.
|
3119 |
|
|
variable _reply_expected 0
|
3120 |
|
|
variable _expected_rxlen 0
|
3121 |
|
|
|
3122 |
|
|
# Receive a single request from the eCos application and invoke the
|
3123 |
|
|
# appropriate handler.
|
3124 |
|
|
proc _read_request { } {
|
3125 |
|
|
# Read a single request from the application, or possibly EOF
|
3126 |
|
|
set devid 0
|
3127 |
|
|
set reqcode 0
|
3128 |
|
|
set arg1 0
|
3129 |
|
|
set arg2 0
|
3130 |
|
|
set txlen 0
|
3131 |
|
|
set txdata ""
|
3132 |
|
|
set rxlen 0
|
3133 |
|
|
set request [read $synth::_channel_from_app 24]
|
3134 |
|
|
|
3135 |
|
|
if { [eof $synth::_channel_from_app] } {
|
3136 |
|
|
fileevent $synth::_channel_from_app readable ""
|
3137 |
|
|
synth::_application_has_exited
|
3138 |
|
|
return
|
3139 |
|
|
}
|
3140 |
|
|
|
3141 |
|
|
# If a real request is sent then currently the application should
|
3142 |
|
|
# not be expecting a reply
|
3143 |
|
|
if { $synth::_reply_expected } {
|
3144 |
|
|
error "The eCos application should not be sending a request when there is still a reply pending"
|
3145 |
|
|
}
|
3146 |
|
|
|
3147 |
|
|
set binary_result [binary scan $request "iiiiii" devid reqcode arg1 arg2 txlen rxlen]
|
3148 |
|
|
if { 6 != $binary_result } {
|
3149 |
|
|
error "Internal error decoding request from eCos application"
|
3150 |
|
|
}
|
3151 |
|
|
|
3152 |
|
|
# If running on a 64-bit platform then the above numbers will have been sign-extended,
|
3153 |
|
|
# which could lead to confusing results
|
3154 |
|
|
set devid [expr $devid & 0x0FFFFFFFF]
|
3155 |
|
|
set reqcode [expr $reqcode & 0x0FFFFFFFF]
|
3156 |
|
|
set arg1 [expr $arg1 & 0x0FFFFFFFF]
|
3157 |
|
|
set arg2 [expr $arg2 & 0x0FFFFFFFF]
|
3158 |
|
|
set txlen [expr $txlen & 0x0FFFFFFFF]
|
3159 |
|
|
set rxlen [expr $rxlen & 0x0FFFFFFFF]
|
3160 |
|
|
|
3161 |
|
|
# The top bit of rxlen is special and indicates whether or not a reply is expected.
|
3162 |
|
|
set synth::_reply_expected [expr 0 != ($rxlen & 0x080000000)]
|
3163 |
|
|
set synth::_expected_rxlen [expr $rxlen & 0x07FFFFFFF]
|
3164 |
|
|
|
3165 |
|
|
# Is there additional data to be read
|
3166 |
|
|
if { $txlen > 0 } {
|
3167 |
|
|
set txdata [read $synth::_channel_from_app $txlen]
|
3168 |
|
|
if { [eof $synth::_channel_from_app] } {
|
3169 |
|
|
fileevent $synth::_channel_from_app readable ""
|
3170 |
|
|
synth::_application_has_exited
|
3171 |
|
|
return
|
3172 |
|
|
}
|
3173 |
|
|
}
|
3174 |
|
|
|
3175 |
|
|
# The devid can be used to get hold of a handler function, and that will do
|
3176 |
|
|
# the hard work.
|
3177 |
|
|
if { ![info exists synth::_device_handlers($devid)] } {
|
3178 |
|
|
error "A request has been received for an unknown device $devid"
|
3179 |
|
|
}
|
3180 |
|
|
|
3181 |
|
|
$synth::_device_handlers($devid) $devid $reqcode $arg1 $arg2 $txdata $txlen $synth::_expected_rxlen
|
3182 |
|
|
}
|
3183 |
|
|
|
3184 |
|
|
# Register _read_request as the handler for file events on the pipe from
|
3185 |
|
|
# the application.
|
3186 |
|
|
fileevent $synth::_channel_from_app readable synth::_read_request
|
3187 |
|
|
|
3188 |
|
|
# Send a reply back to eCos. This consists of a two-word structure,
|
3189 |
|
|
# result and length, followed by the data if any. Currently this
|
3190 |
|
|
# raises an error if there is a mismatch between the specified and
|
3191 |
|
|
# actual length of the data. Possibly the code should cope with
|
3192 |
|
|
# data strings that exceed the specified length, extracting a
|
3193 |
|
|
# suitable substring.
|
3194 |
|
|
proc send_reply { result { length 0 } { data "" } } {
|
3195 |
|
|
# Make sure that a reply is actually expected.
|
3196 |
|
|
if { !$synth::_reply_expected } {
|
3197 |
|
|
error "Attempt to send reply to application when no request has been sent"
|
3198 |
|
|
} else {
|
3199 |
|
|
set synth::_reply_expected 0
|
3200 |
|
|
}
|
3201 |
|
|
|
3202 |
|
|
if { $length > $synth::_expected_rxlen } {
|
3203 |
|
|
error "Reply contains more data than the application expects: $length bytes instead of $synth::_expected_rxlen"
|
3204 |
|
|
}
|
3205 |
|
|
if { ($length > 0) && ([string length $data] != $length) } {
|
3206 |
|
|
error "Mismatch between specified and actual data length: $length [string length $data]"
|
3207 |
|
|
}
|
3208 |
|
|
if { !$synth::ecos_running } {
|
3209 |
|
|
return
|
3210 |
|
|
}
|
3211 |
|
|
|
3212 |
|
|
set struct [binary format "ii" $result $length]
|
3213 |
|
|
# Ignore any errors when writing down the pipe. The only likely error is
|
3214 |
|
|
# when the application has exited, causing a SIGPIPE which Tcl
|
3215 |
|
|
# will handle. The application should be waiting for this response.
|
3216 |
|
|
catch {
|
3217 |
|
|
puts -nonewline $synth::_channel_to_app $struct
|
3218 |
|
|
if { $length > 0 } {
|
3219 |
|
|
puts -nonewline $synth::_channel_to_app $data
|
3220 |
|
|
}
|
3221 |
|
|
}
|
3222 |
|
|
}
|
3223 |
|
|
}
|
3224 |
|
|
|
3225 |
|
|
# }}}
|
3226 |
|
|
|
3227 |
|
|
# {{{ initrc
|
3228 |
|
|
|
3229 |
|
|
# ----------------------------------------------------------------------------
|
3230 |
|
|
# Just before control is returned to the eCos application, run the per-user
|
3231 |
|
|
# file, ~/.ecos/synth/initrc.tcl. The main GUI is now in place and the target
|
3232 |
|
|
# definition file has been read in, but no eCos static constructors have
|
3233 |
|
|
# been run yet and hence no devices have been loaded or activated.
|
3234 |
|
|
# All the various core procedures have been defined. initrc gives the user
|
3235 |
|
|
# a chance to install hooks, redefine some internals, and so on.
|
3236 |
|
|
# Another initialization file mainrc.tcl gets read in later, just before
|
3237 |
|
|
# the eCos application really starts running.
|
3238 |
|
|
#
|
3239 |
|
|
# Possibly ecosynth should also read in a system-wide initialization
|
3240 |
|
|
# file equivalent to emacs' site-start.el, but the extra complexity
|
3241 |
|
|
# does not seem warranted just yet.
|
3242 |
|
|
|
3243 |
|
|
if { !$synth::flag_no_rc } {
|
3244 |
|
|
set _config_file [file join "~/.ecos/synth" "initrc.tcl"]
|
3245 |
|
|
if { [file exists $_config_file] } {
|
3246 |
|
|
if { [file readable $_config_file] } {
|
3247 |
|
|
if { [catch { source $_config_file } msg ] } {
|
3248 |
|
|
set error "Failed to execute user initialization file \"$_config_file\"\n"
|
3249 |
|
|
append error " $msg\n"
|
3250 |
|
|
if { $synth::flag_verbose } {
|
3251 |
|
|
append error "------- backtrace ------------------------------------------\n"
|
3252 |
|
|
append error $::errorInfo
|
3253 |
|
|
append error "\n------- backtrace ends -------------------------------------\n"
|
3254 |
|
|
}
|
3255 |
|
|
synth::report_error $error
|
3256 |
|
|
}
|
3257 |
|
|
} else {
|
3258 |
|
|
synth::report_error "No read access to user initialization file \"$_config_file\"\n"
|
3259 |
|
|
}
|
3260 |
|
|
}
|
3261 |
|
|
unset _config_file
|
3262 |
|
|
}
|
3263 |
|
|
|
3264 |
|
|
# }}}
|
3265 |
|
|
# {{{ Done
|
3266 |
|
|
|
3267 |
|
|
# ----------------------------------------------------------------------------
|
3268 |
|
|
# The last few steps.
|
3269 |
|
|
|
3270 |
|
|
# Once everything has been initialized the application can be woken up again.
|
3271 |
|
|
# It should be blocked waiting for a single byte on the pipe.
|
3272 |
|
|
if { $synth::flag_debug } {
|
3273 |
|
|
synth::report "Core initialization complete, resuming the eCos application.\n"
|
3274 |
|
|
}
|
3275 |
|
|
|
3276 |
|
|
puts -nonewline $synth::_channel_to_app "."
|
3277 |
|
|
|
3278 |
|
|
# Enter the event loop. In console mode there is a problem if -nx has been
|
3279 |
|
|
# specified: there may not be any event handlers left once the eCos application
|
3280 |
|
|
# has exited, so the vwait would abort. This is avoided by a dummy after proc.
|
3281 |
|
|
if { !$synth::flag_gui && !$synth::flag_immediate_exit } {
|
3282 |
|
|
namespace eval synth {
|
3283 |
|
|
proc _dummy_after_handler { } {
|
3284 |
|
|
after 1000000 synth::_dummy_after_handler
|
3285 |
|
|
}
|
3286 |
|
|
}
|
3287 |
|
|
after 1000000 synth::_dummy_after_handler
|
3288 |
|
|
}
|
3289 |
|
|
|
3290 |
|
|
vwait synth::_ecosynth_exit
|
3291 |
|
|
|
3292 |
|
|
# }}}
|