summaryrefslogtreecommitdiffstats
path: root/ksirc/puke/HOWTO-PUKE.pod
blob: c6bf813616df881109d6232c259cd0efd4460f21 (plain)
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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
=head1 OVERVIEW

This document describes how to write puke addons and additional
widgets.  It assumes a good knowledge of C++, perl and X/Qt workings
under Linux.

=head1 1. Description and Background

=over 6

Puke's a generic protocol allowing dsirc to communicate with ksirc.
Communications works over a unix domain socket between multiple
client dsirc process's to a single ksirc process.  All communications
is done via a variable length message with the following layout:

=begin text

struct PukeMessage {
unsigned int iHeader;
int iCommand;
int iWinId;
int iArg;
int iTextSize;
char *cArg;
}

=end text

None of the fields except for iCommand, iWinId and iHeader have any restrictions
on their content and may contain arbitrary values.  iCommand and iWinId must
contain an int and it used by ksirc to determine the destination and
handler of the actual command. (and of course it's meaning).  iHeader is a
fixed pattern used to identify the start of a header message should it loose
syncronization.  The current pattern used it 2863311530, which is:
10101010101010101010101010101010.

=item Internal handling by kSirc:

Messages are received by a generic handler, PukeController where the message
is passed to the iWinId's messageDipatcher for final processing.  The
iWinId of 1 through 10 are reserved for internal use, and 1 is
currently set at the window ID for the PukeController itself.

Connect a signal to PukeControllers writeBuffer (signal's generally
called outputMessage) and pass the fd and PukeMessage to be sent to
the client.  No parsing of the output message is done.

=item Internal handling by dsirc:

All received messages are handled by an internal callback methods.  3
sets of callbacks are checked for handlers in the following order:

$PUKE_HANDLER{$cmd}{$winid}
$PUKE_HANDLER{-$cmd}{$winid}
$PUKE_DEF_HANDLER{$cmd}

If no handler is found an error is printed.

Output is handled by the PukeSendMessage function.  PBase defines an
alternate routine sendMessage which should be a lot friendlier.

=head1 2. How to create a new widget

There are 2 parts to creating a widget, the C++ code and the
supporting perl5-oop object.

=head2 2.1 C++ Widget code

The C++ code must be able to hand all required settings and messages
for the widget.  Each new widget iherites it's parent and so forth
allowing for a nice oop layout.  The widget structure the author is
following is the same as Qt's.  Their seems to work well, why
re-invent the wheel?

=item 2.1.1 General Layout, etc

Figure where your new widget goes in the heirachy.  If it's a simple
Qt widget, I recommend using their existing layout.  Man pages list
what widgets inherit what.

The general idea behind the widget layout should be to be to provide
all the functionality of the widget to dsirc perl script.  Incoming
messages are handled via the messageHandler and ALL messages should
return an ACK with current state info.

New widgets are created as shared objects and loaded on the fly.  This 
means you don't need to recompile ksirc to use new widgets etc. 

Generally you'll have to inherit PWidget at a minimum.  

Functions you HAVE TO overrite:

B<1. createWidget>

This function creates a new widget of your type and returns a
*PWidget.

B<2. messageHandler>

This function receives ALL your commands.

B<3. widget() and setWidget(YourWidget *)>

These set and return your widget.


If you care about inheritance, which you should, all these functions
should be virtual.  (Since we are using pointers to PWidget's
everywhere, it's a good bet you want your children's overriden
functions called, not yours)

The structure internally will have to hold a local copy of the widget,
and connect to it's destroy signal so you can know when it has been
destroyed.

=item 2.1.2 createWidget

createWidget is defined as:

PWidget *createWidget(widgetId *pwi, PWidget *parent);

It is called everytime a new widget of yours is required.  The
widgetId will be the identifier for your widget and must be kept for
all future commands.  PWidget::setWidgetId(pwi) should be called to
set the widget id.  The *parent is the parent of the current widget.
Generally PWidget->widget() is passed to the contructor of your
widget.  If it's 0, there is no parent. Simeplfied code for a the
PFrame is:

=begin text

extern "C" {
PWidget *createWidget(widgetId *pwi, PWIdget *parent);
}

PWidget *createWidget(widgetId *pwi, PWIdget *parent){
	QFrame *f;
	PFrame *pf = new PFrame(parent);
	if(parent != 0){
		f = new QFrame(parent->widget());
	}
	else{
		f = new QPFrame();
	}
	pf->setWidget(f);
	pf->setWidgetId(pwi);
	return pf;
}

=end text

Note: you have to check parent for null since calling NULL->widget()
results in Bad Things (tm).

=item 2.1.3 messageHandler

This receives all commands, etc.  It should process required commands,
if a command is unkown pass it to the parent.  PFrame example:

=begin text

class PFrame : public PWidget
...
void messageHandler(int fd, PukeMessage *pm);
...

void PFrame::messageHandler(int fd, PukeMessage *pm) {
  PukeMessage pmRet;
  switch(pm->iCommand){
  case PUKE_QFRAME_SET_FRAME:
    widget()->setFrameStyle(pm->iArg);
    pmRet.iCommand = PUKE_QFRAME_SET_FRAME_ACK;
    pmRet.iWinId = pm->iWinId;
    pmRet.iArg = widget()->frameStyle();
    pmRet.cArg[0] = 0;
    emit outputMessage(fd, &pmRet);
    break;
  default:
    PWidget::messageHandler(fd, pm);
  }
}

=end text

=item 2.1.4 widget and setWidget

Both these functions should be overriden and return your widget type,
and set your widget.  For setWidget you should connect required
Q_SIGNALS and eventFilters you are using.

Make sure to call the parents setWidget in setWidget so it can connect 
filters etc.  

BEWARE: You might get the widget into setWidget being null (from the
destructor).  

Another PFrame example (APE ;) ):

=begin text
void PFrame::setWidget(QFrame *_f)
{
  frame = _f;
  PWidget::setWidget(_f);

}


QFrame *PFrame::widget()
{
  return frame;
}

=end text

=item 2.1.5 Destructor

Ok, unfortunaly since we have this internal widget floating arround
the destructor has to a little maigc.

Call the destructor as such:

delete widget();
setWidget(0);

This will clear the widget from now and all parents and delete it.
you never want it deleted twice. (deleting 0 won't hurt)

=head2 2.2 The Perl code

Most of the perl oop is pretty straight forward, command simply issue
a require sendMessage and off everything goes.  There's one problem.

You can't get information back on the current read cycle.  Huh? I can
hear most people saying.  It means say someone wanted to do $widget =
$widget->height() and you didn't have the height information locally,
there's no way to get the information and return it to them.  Why? You
issue a sendMessage(...) but until dsirc returns to the main select()
loop, we never know there's more to read.  We can't return to the main
select loop until we return from our current function.  What does this
mean?  We have to store all the information locally.

This also brings up another intresting aspect.  Sometimes a widget may
depend on a prior command before it can complete.  This is the purpose
of canRun function, and onNext.  It's use will have to be explained
latter.

To help with this problem, pbase.pm sets up a fairly complicated set
of message and event queues.  Be warned when you issue a sendMessage,
it might not get sent right away.

I'll provide example bellow of how I've done certain functions, this
is certainly not the only way to do it.  Feel free to use any format
you like aslong as it get's the job done.

Ok, so how do we do this?

=item 2.2.1 Perl oop? where do I start?

Read the perltoot and perlobj man pages.

=item 2.2.2 What to inherit etc.

You probably want to inherit the same object as your C function does.
At very least you'll want to inherit PWidget.

=item 2.2.3 new and DESTROY

Your new function should look something like (APE?):

=begin text

sub new {
  my $class = shift;
  my $self = $class->SUPER::new($class, @_);

  $self->{widgetType} = $::PWIDGET_FRAME;

  if($class eq 'PFrame'){
    $self->create();
  }

  return $self;
}

=end text

$self is the blessed variable and it returned from the super class.
You should always do it this way.  Setting widgetType defines the type 
of widget, and needs to be set before calling create.

If we are creating an object of our own class we call create() which
acutally sends out the correct creation messages, etc.  You can
override the create function, but do be warned it might not be a good
idea.  Make sure you understand what and how it does it first!

=item 2.2.4 sendMessage

sendMessage is the main form of communicating with kSirc.  Generable
arguments are:

=begin text

sendMessage('iCommand' => command number,
	    'iArg'     => interger argument,
            'cArg'     => character string,
            'CallBack' => pointer to sub, generally sub{...}
           );

=end text

You'll note it's a hash so order doesn't count.  The callback is a 1
shot call back (should be) so don't count on it getting hit twice.
The call back's first argument will be a \%ARGS with the return
arguments.

=item 2.2.5 Final notes on perl

Most of the function calls will just send out messages, etc.  For call 
backs the general form I've found works well is: sub {$self->blah()}.

=head1 3. Interfacing with the kSirc's main windows and functions

NOT implemented yet.

=back