You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/ksirc/puke/HOWTO-PUKE.pod

326 lines
9.2 KiB

=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
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