File: multides.html

package info (click to toggle)
librnd 4.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 12,812 kB
  • sloc: ansic: 126,990; sh: 2,602; makefile: 2,145; awk: 7
file content (247 lines) | stat: -rw-r--r-- 10,775 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
<html>
<body>

<h1> Multiple design support </h1>
<p>
This document describes how multiple design and project files are
supported starting from librnd 4.0.0.


<h2> Architecture </h2>
<p>
There is a global variable in librnd, called rnd_designs. This is a
doubly linked list holding all designs currently open, each an
(rnd_design_t *). There is a hash of project files (key is realpath):
each project file opened and cached in memory only once, referenced
by one or more of the design files.
<p>
Globally there is a "current design", which also determines the
"current project" (via it's parent project pointer).
<p>
Note: a design (rnd_design_t *) represents a board file in pcb-rnd, a sheet
in sch-rnd.

<h3> What's in a design </h3>
<p>
All actual design data is on the app's side, where rnd_design_t MUST be the
first field of the design structure. On librnd side rnd_design_t contains:
<ul>
	<li> public fields:
	<ul>
		<li> original file name (as the user specified)
		<li> realpath file name
		<li> reference to the parent <i>project</i> (a dummy is generated if there's none)
		<li> app config tree pointer (was conf_core before 4.0.0)
		<li> librnd config tree pointer (was rnd_conf before 4.0.0)
		<li> grid state
		<li> crosshair & tool state
	</ul>
	<li> private fields:
		<ul>
			<li> conf backing data
		</ul>
</ul>

<h3> What's in a project </h3>
<p>
A project structure reflects a project file. A project file contains configuration
(and design file list) for multiple tools. Librnd will handle the subtree for
related to the current application only. For example a typical ringdove project
file will contain a subtree for sch-rnd and another for pcb-rnd. When librnd
linked in pcb-rnd is dealing with the project file, it will read only
the pcb-rnd subtree but will preserve the sch-rnd and other subtrees.
<p>
For applications implemented using librnd, the app-specific subtree of the
project file is a config tree. Thus the (optional) design file list of
the project is a config list (which can be specified only at project role).
<p>
When a project file is open via UI, what really happens is:
<ol>
	<li> creating a project structure
	<li> loading the application specific config tree of the project file
	<li> loading all designs listed in that config tree
	<li> if there's no design listed, the operation fails and the project
	     structure is discarded
</ol>
<p>
When an individual design file is open via UI:
<ol>
	<li> the design is loaded
	<li> if a project file is present in the same directory as the design file,
	     it is assumed to be the project file for the given design, independently
	     of whether it lists the design file on its file listing;
	     such project file is loaded, and the design is put into that project
	     in memory (without altering the project's design file listing)
	<li> if there's no such project file, a <i>dummy</i> project file is created in
	     memory; a dummy project file is not saved until the user makes explicit
	     UI commands (e.g. changing config settings on project file level)
</ol>
<p>
Each project file (as per realpath) is stored only once in memory, and has
only one project structure. Every design file references to one of the
project file structures in memory. Every project file in memory has
an internal, only-in-memory list of design structures that references it.
<p>
If, due to design unloading, a project structure's internal design list
becomes empty, the project structure is unloaded too.
<p>
Thus the project structure contains:
<ul>
	<li> the realpath of the file (also key of the hash table)
	<li> the list of design structures that consider this project file as parent
	     (not necessarily the same as the list of explicitly referenced design files
	     from the conf tree of the project file!)
	<li> the lihata document or at least the app-specific conf subtree
	<li> a bit to indicate whether the project file is <i>dummy</i>
</ul>
<p>
Since the project file may be used by multiple applications in parallel,
any change to the project config (of non-dummy project files) is saved
to disk as soon as possible.
<p>
A design that is part of a project in-memory is not always explicitly
part of a project as per configured design file list. This can happen
if:
<ul>
	<li> there was no project file in a directory and one or more design file
	     is created/loaded from that dir and a dummy project structure is made
	     in memory then a project role conf change is made; in this case the
	     new project file is created, its settings affect the design file(s)
	     loaded from that dir, but it's design file list is empty
	<li> a project is loaded, it listed a.lht and b.lht, but the directory
	     also contained c.lht, unlisted; the user loads c.lht. Because it's
	     in the same dir, c.lht design struct will reference the same project
	     file as its parent, even tho the project file config tree doesn't
	     explicitly list c.lht as being part of the project.
</ul>
<p>
UI needs to be provided for the user to indicate this situation and
shortcuts should be provided so that a design file already associated
to a project structure in memory can be made an explicit listed design
file in the project config tree, or a design file part of the config tree
can be removed from there.

<h3> How config is managed </h3>
<p>
Runtime configuration, or <i>config</i> for short, is stored in read-only
global variables (structs) for quick access. There are multiple such global
config variables:
<p>
<ul>
	<li> rnd_conf: librnd's core conf
	<li> conf_core: the app's core config
	<li> each module ("plugin") may have its own config, dynamically created when the module is loaded
</ul>
<p>
Upon switching the current design, global config states are saved in a field
of the design we are switching away from and then the global config states are
loaded from the design we are switching to. This way global config states always
reflect the config state of the current design.
<p>
There's an API provided for plugins to register their config states:
rnd_conf_plug_reg(). This in turn calls rnd_conf_state_plug_reg(), which
registers the global var and its size in the list of "need to save/load on
switch".
<p>
The app's conf_core is registered on the same list, by the app, using
rnd_conf_state_plug_reg().

<h3> Why config is not stored in rnd_design_t </h3>
<p>
Saves and loads are done using memcpy. A typical config struct ranges from
a few dozen bytes to a few hundred bytes. Which means a full switch takes
a handful of memcpy()s moving at most a few kilobytes. Compared to the
frequency of design switches and all the GUI refresh implications, this
cost is reasonable.
<p>
An earlier plan was to move rnd_conf and conf_core into the design struct so
each design really has its won, then every single time the code needs to
access the config, it needs to do so using a design (e.g. the current design).
Besides the code complication on each config access, the main problem with
this approach is that it can't deal with dynamic config vars supplied by
the modules without making config access from modules real complicated.

<h2> Local vs. global access </h2>

<H3> librnd core </H3>

Librnd is aware of the <i>current design</i> (see rnd_multi_*), the list
of designs loaded and their project files. Apps should use the core
librnd API to manipulate <i>current design</i> or the list of designs.

<H3> UI </H3>
<p>
For example the UI is dealing with one design at a time: the UI state,
accessed with ->set_hidlib() and ->get_hidlib(), stored by the HID. In
any GUI event, such as dialog box callbacks on user input events, the
related (rnd_design_t *) is delivered. This means dialog boxes are always
bound to a specific design and they remember their (rnd_design_t *) and pass
that on in one way or another to any of their callbacks.
<p>
There is an event called RND_EVENT_DESIGN_SET_CURRENT for switching the current design shown on the GUI.
If a dialog box is <b>global</b>:
<p>
<ul>
	<li> typically only one instance can be open per process (the dialog is not per design)
	<li> it still has it's (rnd_design_t *), but that field is changed to the new
	     current GUI design when the RND_EVENT_DESIGN_SET_CURRENT event is received
	<li> the necessary GUI updates are also done on the RND_EVENT_DESIGN_SET_CURRENT event
</ul>
<p>
A typical example of "single instance <b>global</b> dialog" is the preferences dialog.
Only one instance can be open, and it changes its contents when the current
design is switched.
<p>
The other type of dialog box is <b>local</b>, which presents states/data of
a given design and is not affected by RND_EVENT_DESIGN_SET_CURRENT.
<p>
A typical example of a "multiple instance local dialog" is pcb-rnd's propedit
dialog: there can be multiple propedits open, but each is bound to a specific
board, listing and manipulating objects of only that one board.

<h3> events (rnd_event()) </h3>
<p>
Event callbacks get a (rnd_design_t *). There are two kind of events:
<ul>
 <li>per-design: the event affects a specific design only; most often called
               for the current design, but callbacks shouldn't depend on that.
               Examples: RND_EVENT_SAVE_*, RND_EVENT_DESIGN_*.
               Marked as [d].
 <li>per-app: the event affects the whole application and the design pointer
            passed is irrelevant (points to the current design or is NULL).
            Examples: RND_EVENT_NEW_DIALOG, RND_EVENT_BUSY.
            Marked as [a].
</ul>

<p>
Each event must be marked as [d] or [a] in the documentation (comment).

<h3> config change callbacks </h3>
<p>
These are always local, always delivering a (hidlib_t *) that is affected.
There are cases when a config change affects multiple open designs:
<ul>
	<li> change in system config (may affect all open design)
	<li> change in user config (may affect all open design)
	<li> change in project config (may affect all open design associated with the project)
	<li> change in CLI config (may affect all open design)
</ul>
<p>
In such case librnd delivers a separate config change callback for each
design affected.


<h2> Special use case: single-design app </h2>
<p>
An application may decide not to use multiple designs. This simplifies the
code because there's always one active design, in all respects, and it
can be achieved from the HID struct if nothing else offers it.
<p>
This is indicated in rnd_app.single_design. Librnd internals are still
all prepared for multiple designs and related project administration, but
when a new design is registered, it is not appended to the list of designs
but replaces the previous design there. So the list of designs is always
of size 1.
<p>
Instead of rnd_multi_* API, such app should use rnd_single_*.