-
Notifications
You must be signed in to change notification settings - Fork 9
/
HACKING
250 lines (203 loc) · 10.8 KB
/
HACKING
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
248
249
250
/* Documentation on YaST-GTK */
YaST basis
Yast-core is a simple byte-code interpreted programming
language. On execution, two threads will be fired: one
for the interpreter that runs the YCP application, the other
for the interface. This is so because a user may want
to setup another machine while sitting confortably
with its GTK+ interface.
Yast-core offers an interface API, called YWidgets, that
is then wrapped, by dynamic loading at run-time, by
one of the available interface modules that the user
specified. This code is one of these modules.
Yast is usually distributed with the /sbin/yast script
that automatically looks for the best interface for
the environment.
Yast-core is located at /usr/lib/YaST2/bin/y2base ,
on the Suse distribution.
You don't need to know to program YCP, but you should
have a look at its API because it will give you some
insight into the platform. Yast documentation:
http://forgeftp.novell.com//yast/doc/SL10.1/tdg/
Code structure
Hierarchy of some of the widgets:
YGWidget YWidget
| |
| /|
| / |
|\ YContainerWidget |
|\\ / |
|\\YGSplit |
|\\YGEmpty |
|\\YGSpacing |
| \YSSquash |
| YGAlignment /-------|
| / |
|\ YLabel |
| \ / |
| \---YGLabel |
| |
| |
|\ /|
| \ YTextEntry |
| YGLabeledWidget / |
| | \ / |
| | YGTextEntry |
| | |
| \ |
| YGScrolledWidget /---|
| \ / |
| \ YRichText |
| \ / |
| YGRichText |
| |
|..........................|
As you may see, everything inherits from both YGWidget
and YWidget. YWidget derivated classes provide virtual functions
that we must fill. A pointer to the interface class wrapper may
always be obtain with widgetRep().
On the other hand, YGWidget provides a common set of functionaly
with great value for code re-use and also makes the code more
robust and flexible since it provides an infrastructure to the
whole code.
YGWidgets always creates an associated GtkWidget object, as asked
by the class that inherits it, and that can be obtained via
getWidget().
Every YGWidget has an associated parent, with the single exception
of YGDialog, that provides their children with a GtkFixed container
that they sit upon. getFixed() is the recursive function that asks
the parent for its associated GtkFixed. Container widgets replace
getFixed() by one that returns a GtkFixed that they created, thus
closing the cycle.
YGLabeledWidget and YGSrolledWidget are simple children of
YGWidget that create the asked widget with a label or inside
a GtkScrolledWindow for code re-use value.
The force behind the all thing is in YGUI. Its header has the declaration
for the creation of all the widgets, while its implementation is done
on the widgets files, and also specifies whatever one widget is
supported or not. YGUI.cc has, for the most part, the code that switches
between our code and the main YWidget, since we all use the same thread.
On specific widgets
Layout widgets
Old approach
What yast-ncurses and -qt do, and we used to follow, is to let
YWidget layout framework do that work for us.
Every container widget we implemented had an associated GtkFixed
that it returned with a getFixed(). Other widgets getFixed()
would request the parent's getFixed().
However this approach was actually quite complicated, because we
had to implement nicesize() and setSize() on container widgets in
a way that the containee would be placed there correctly. So, for
instance, for YGDumbTab, we had to request informations like the
tabs labels height, the frame around the page, etc. Furthermore,
the ultimate decision to drop the approach was when we redesigned
YGtkWizard. This time the steps widget was dynamic and the work
to check of any size change and report to YWidget just felt too
hackish. Attempts at abstracting this were successful, but not
very satisfactory from a code's point of view.
Current approach
Currently, we just implement the layout widgets ourselves. The
most important is YGSplit, which would be the equivalent of
GtkBox (though we use our YGtkRatioBox to support the weight
childs attribute).
The YGLayout code is just a little bigger than the previous, it's
easy to understand, and overall it made the code base smaller.
Furthermore, YWidgets had bugs like labels getting truncated
(which had to be avoided by an explicit request for recalculating
the whole layout geometry) that we don't suffer. The layout is
also a lot faster.
To set minimum size to widgets, to make them look prettier, we
install all widgets in our YGtkMinSize container. To be able
to honor the stretchable attribute, we also install this
into a GtkAlignment. So, YGWidget's getWidget() returns the
inner widget that is the one that our YWidget implementation ask
it to create, while getLayout() should be used for layouts to get
the outter widget, which would be the GtkAlignment. Furthermore
the GtkAlignment allows us to align the widget on YGAlignment
(which is just a proxy).
Most of the widgets are a no brainer. Any other widget should have
some comments as appropriate.
Code practices
As you may have noticed from YGWidget.h, we use macros a lot to
enable common functionality, instead of virtual functions. This is
because simply in a few cases virtual functions would not apply --
ie. the need to call a parent class function. You would have to
override the virtual function everytime. With a macro, we may do:
#define YGLABEL_WIDGET_IMPL_SET_LABEL_CHAIN (ParentClass) \
virtual setLabel (const YCPString &label) { \
doSetLabel (label); \
ParentClass::setLabel (label); \
}
The code style is heavily inspired on the Linux guideline:
http://pantransit.reptiles.org/prog/CodingStyle.html
There may be some differences as it obviously doesn't mention
C++ classes.
For indentations, tabs are used. For layout, spaces are used.
Example:
<tab>int variable_with_very_long_name = function (arg1,
<tab>.............................................arg2);
This ensures the code lays well in whatever editor the reader uses.
Lines must just be ensured to be breaked before the 80th column on
4 stop tabs.
Speaking of classes, we don't use headers, unless we have to, as
opposite to the other interfaces code. There is no reason to
make them publicaly available and it just adds even more
files to the pool.
Also, we generally don't split classes into declaration and
implementation code, for the same reasons.
Variables and functions names follow Yast style (variableNameOne),
for the most part. Our Gtk widgets (on ygtk* files) are written
in C and follow this_name_style to fit better with GTK+ one.
We always prefix m_ to class memeber variables.
For GTK+ widgets, we follow some scheme. Most standing, we avoid declaring
functions, and do the class initilization, where the overloading is set,
at the bottom. When doing a widget, just copy and paste from another.
Morphing Suse to use Yast-GTK
We haven't yet managed to create a Suse GTK-based installer, but
these steps should be close to that end.
1. Pick the 1st CD of some Suse edition version ISO
(let's say it is named SUSE.iso)
2. Ensure you have a live / working gtk+/Gnome install
3. Get cramfs (not included on suse cds, just google it and it will be 1st
result), and compile it.
4. Tweak the variables in gtkize-iso to suit
+ you need a chunk of tmp space to unpack & re-build the iso
5. Run ./gtkize-iso SUSE.iso /tmp/new.iso
6. Ready to burn and try.
LibZypp API overview
The Zypp library is a wrapper around at least two package repository
systems (zmd and rug). It is quite a monster with no clear API or
documentation, thus this section.
The first thing we need to do when using Zypp is to initializate it.
This is done for us by yast-core, so we don't have to care about that.
To get a list of packages, you iterate the Zypp pool, that you can get
with zypp::getZYpp()->poolProxy(). So, if you want to iterate through
the package pool, you would do:
for (zypp::ResPoolProxy::const_iterator it =
zypp::getZYpp()->poolProxy().byKindBegin <zypp::Package>();
it != zypp::getZYpp()->poolProxy().byKindEnd <zypp::Package>(); it++)
To iterate through patches, use zypp::Patch as the kind.
The iterators point to objects of type zypp::ui::Selectable::Ptr, where Ptr
is a boost intrusive_ptr -- so, you may turn that into a C pointers with
a "zypp::ui::Selectable *sel = get_pointer (*it);"
A Selectable object contains pointers to the installed version and all
available (through repositories) versions of some packages whose name
you may get with a zypp::ui::Selectable::name(). The package it considers
as the best from the available packages is what it calls a candidate.
Check /usr/include/zypp/ui/Selectable.h for its API.
You may get more details on a given packages by retriving the correspondent
zypp::ResObject from the Selectable of the version you want.
Check /usr/include/zypp/ResObject.h for its API.
To get even more information, you may cast the zypp::ResObject to a
zypp::Package or zypp::Patch, according to what you are iterating.
Since this is boost pointers we are dealing with, you would cast a
zypp::ResObject::constPtr like
zypp::dynamic_pointer_cast <const zypp::Package> (res_object);
Check /usr/include/zypp/Package.h / Patch.h for their API.
The reason why our categories tree is build in an ackward way is because you
get the category of a package with zypp::Package::group() which returns a
std::string like "Productivity/Networking/Email/Utilities". You can't build
the tree before you start adding packages since you don't have a way to know
its hierarchy.
To avoid the lengthy names of Zypp, we use some typedefs and inline helper
functions that you may find right on the start of YGPackageSelector.cc.