summaryrefslogtreecommitdiffstats
path: root/libksirtet/lib
diff options
context:
space:
mode:
Diffstat (limited to 'libksirtet/lib')
-rw-r--r--libksirtet/lib/CHANGELOG39
-rw-r--r--libksirtet/lib/LICENSE18
-rw-r--r--libksirtet/lib/Makefile.am21
-rw-r--r--libksirtet/lib/README63
-rw-r--r--libksirtet/lib/TODO18
-rw-r--r--libksirtet/lib/defines.cpp24
-rw-r--r--libksirtet/lib/defines.h35
-rw-r--r--libksirtet/lib/internal.cpp278
-rw-r--r--libksirtet/lib/internal.h152
-rw-r--r--libksirtet/lib/keys.cpp104
-rw-r--r--libksirtet/lib/keys.h42
-rw-r--r--libksirtet/lib/libksirtet_export.h35
-rw-r--r--libksirtet/lib/meeting.cpp575
-rw-r--r--libksirtet/lib/meeting.h137
-rw-r--r--libksirtet/lib/miscui.cpp58
-rw-r--r--libksirtet/lib/miscui.h43
-rw-r--r--libksirtet/lib/mp_board.h51
-rw-r--r--libksirtet/lib/mp_interface.cpp274
-rw-r--r--libksirtet/lib/mp_interface.h246
-rw-r--r--libksirtet/lib/mp_option.h74
-rw-r--r--libksirtet/lib/mp_simple_board.cpp84
-rw-r--r--libksirtet/lib/mp_simple_board.h45
-rw-r--r--libksirtet/lib/mp_simple_interface.cpp152
-rw-r--r--libksirtet/lib/mp_simple_interface.h48
-rw-r--r--libksirtet/lib/mp_simple_types.cpp6
-rw-r--r--libksirtet/lib/mp_simple_types.h36
-rw-r--r--libksirtet/lib/pline.cpp147
-rw-r--r--libksirtet/lib/pline.h112
-rw-r--r--libksirtet/lib/smanager.cpp115
-rw-r--r--libksirtet/lib/smanager.h88
-rw-r--r--libksirtet/lib/socket.cpp80
-rw-r--r--libksirtet/lib/socket.h65
-rw-r--r--libksirtet/lib/types.cpp254
-rw-r--r--libksirtet/lib/types.h197
-rw-r--r--libksirtet/lib/version.h6
-rw-r--r--libksirtet/lib/wizard.cpp229
-rw-r--r--libksirtet/lib/wizard.h57
37 files changed, 4008 insertions, 0 deletions
diff --git a/libksirtet/lib/CHANGELOG b/libksirtet/lib/CHANGELOG
new file mode 100644
index 00000000..84c03edb
--- /dev/null
+++ b/libksirtet/lib/CHANGELOG
@@ -0,0 +1,39 @@
+0.1.8 (11 April 2001)
+ * better Player/Meeting "choose boxes"
+ * use KExtendedSocket (get rid of custom utilities)
+ and hopefully fix network game
+
+0.1.7
+ * resize handle removed from statusbar of netmeeting dialog
+
+0.1.6
+ * fixed a bug in key configuration
+ * players name access improved
+ * some internal cleanups + use of KDialogBase
+
+0.1.5
+ * many bug fixes for network game
+ * added a framework for simple multiplayers games
+ * Wizard-like configuration
+ * enhanced keys configuration
+
+0.1.4
+ * all data transport is now done via QDataStream (no more bitorder/storage
+ problems).
+ * QDataStream use make things easier from the user point of view (at least
+ I think so).
+ * big cleaning : the library restricts itself to data transport between
+ boards and to game configuration. The library doesn't want to and doesn't
+ have to manage things like game pause or gameover of a specific player ...
+ all those things must be done by the game programmer.
+
+0.1.3
+ * ported to QT 2.0 (hard way : now we send QString over the network :)
+
+0.1.2
+ * finally THE bug has been found (eight months later) !
+ so network game seems stable.
+ * lots of bug fixes
+
+0.1.1
+ * first code (June 1998 : pause in dvplt)
diff --git a/libksirtet/lib/LICENSE b/libksirtet/lib/LICENSE
new file mode 100644
index 00000000..6afe98e6
--- /dev/null
+++ b/libksirtet/lib/LICENSE
@@ -0,0 +1,18 @@
+kdemultiplayers library
+-----------------------
+Copyright (c) 1998-2001 Nicolas HADACEK ([email protected])
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Library General Public License as published
+by the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
diff --git a/libksirtet/lib/Makefile.am b/libksirtet/lib/Makefile.am
new file mode 100644
index 00000000..890ac458
--- /dev/null
+++ b/libksirtet/lib/Makefile.am
@@ -0,0 +1,21 @@
+INCLUDES = $(all_includes)
+
+# Don't compile with hidden symbols since we are a library.
+if disable_VISIBILITY
+KDE_CXXFLAGS = -fvisibility=default
+endif
+
+noinst_LTLIBRARIES = libksirtetmultiplayers.la
+
+noinst_HEADERS = defines.h types.h miscui.h wizard.h pline.h meeting.h \
+ socket.h smanager.h internal.h keys.h mp_board.h mp_option.h \
+ mp_interface.h mp_simple_types.h mp_simple_board.h \
+ mp_simple_interface.h
+
+libksirtetmultiplayers_la_SOURCES = miscui.cpp types.cpp defines.cpp \
+ socket.cpp smanager.cpp pline.cpp \
+ wizard.cpp \
+ meeting.cpp keys.cpp mp_interface.cpp internal.cpp \
+ mp_simple_types.cpp mp_simple_board.cpp mp_simple_interface.cpp
+
+METASOURCES = AUTO
diff --git a/libksirtet/lib/README b/libksirtet/lib/README
new file mode 100644
index 00000000..442c9c30
--- /dev/null
+++ b/libksirtet/lib/README
@@ -0,0 +1,63 @@
+kdemultiplayers library
+-----------------------
+Copyright (c) 1998-2000 Nicolas HADACEK ([email protected])
+Distributed under the GNU Library General Public License
+
+Introduction
+------------
+The "kdemultiplayers" library is an attempt to address the realization of
+multiplayer games localy on a computer (the players have each a "board" on
+the screen and they use the same keyboard) or/and by network (between several
+computers). The implementation is not so simple but this library gives
+a basic API which allows a lot of flexibility.
+
+Note: The headers which defines the API have names beginning with "mp".
+
+The library provides three services :
+ * a set of dialog widgets to allow the players to configure/create/join
+ a game.
+ * management of all the sending/receiving data between
+ player boards so you as a game programmer will *not* have to deal with
+ sockets or network programming.
+ * a framework for simple multiplayers game (for instance ksirtet) with a
+ very simple API ("mp_simple" headers).
+
+Semantics
+---------
+"player" : an individual which take part in the game (it can also be an AI).
+"AI" : artificial intelligence (a game that is played by a program).
+"board" : there is one board per player ; it is the widget where the player
+ acts in the game.
+"host" : a computer which hosts one or several players.
+"server" : the host that has created the game.
+"client" : a host that is not the server.
+
+
+Basic description of the API
+----------------------------
+From the game programmer point of view there should be no difference between
+a local multiplayers game (one host with several players) and a network game
+(several hosts with one or more players).
+
+Each player has a board which is a widget inherited from the class "LocalBoard"
+(see "mp_board.h").
+
+On each host there must be a class inherited from "MPInterface"
+which manages local boards and the data transport with other hosts.
+(see "mp_interface.h").
+
+During the game at given intervals of time, the server asks data from clients
+and from its local boards. After treating this data, the server dispatches back
+results to all boards.
+
+General advice
+--------------
+There should be no blocking operation or long computation as it will freeze
+the user front-end and will also block the multiplayers data exchanges
+which operate by timer signals.
+
+There are general rules to avoid such blocking :
+ * answer to X/QT events such as keyboard/mouse/window events with
+ short methods.
+ * use timer(s) to make things evolve in time or to break long
+ computation in shorter parts.
diff --git a/libksirtet/lib/TODO b/libksirtet/lib/TODO
new file mode 100644
index 00000000..54a924bc
--- /dev/null
+++ b/libksirtet/lib/TODO
@@ -0,0 +1,18 @@
+* change to an event-driven data exchange framework (it currently uses a timer
+ on the server side) -> we probably need a way to ensure clients are not
+ dead (?)
+
+* grid/row layout of boards
+* better dialogs ...
+* check the 64bit fix in "types.h"
+
+* user help (add help button to wizard)
+* tooltips !!
+* API documentation
+
+* heavy test of network game
+* test option widget in netmeeting (--> needs fixing in NetMeeting class)
+* test of options for players and computers
+
+* keys configuration : probably need to change KKeyDialog (the msgbox for
+ duplicate keys is so annoying ...) + unused key finder
diff --git a/libksirtet/lib/defines.cpp b/libksirtet/lib/defines.cpp
new file mode 100644
index 00000000..7c897dd9
--- /dev/null
+++ b/libksirtet/lib/defines.cpp
@@ -0,0 +1,24 @@
+#include "defines.h"
+
+#include <klocale.h>
+
+void errorBox(const QString &msg1, const QString &msg2, QWidget *parent)
+{
+ QString str;
+ if ( msg2.isNull() ) str = msg1;
+ else str = i18n("%1:\n%2").arg(msg1).arg(msg2);
+ KMessageBox::error(parent, str);
+}
+
+QString socketError(const KExtendedSocket *socket)
+{
+ return KExtendedSocket::strError(socket->status(), socket->systemError());
+}
+
+bool checkSocket(int res, const KExtendedSocket *socket,
+ const QString &msg, QWidget *parent)
+{
+ if ( res==0 ) return false;
+ errorBox(msg, socketError(socket), parent);
+ return true;
+}
diff --git a/libksirtet/lib/defines.h b/libksirtet/lib/defines.h
new file mode 100644
index 00000000..9015c6b1
--- /dev/null
+++ b/libksirtet/lib/defines.h
@@ -0,0 +1,35 @@
+#ifndef DEFINES_H
+#define DEFINES_H
+
+#include <kmessagebox.h>
+#include <kextsock.h>
+
+// constants
+#define TALKER_MAX_LENGTH 35
+#define NAME_MAX_LENGTH 15
+
+// config keys
+#define MP_GROUP "Multi-Players"
+#define MP_GAMETYPE "Game type"
+#define MP_PLAYER_NAME "Player name #%1"
+#define MP_PLAYER_TYPE "Player type #%1"
+#define MP_SERVER_ADDRESS "Server address"
+#define MP_PORT "Port"
+
+void errorBox(const QString &msg1, const QString &msg2, QWidget *parent);
+QString socketError(const KExtendedSocket *socket);
+bool checkSocket(int res, const KExtendedSocket *,
+ const QString &msg, QWidget *parent);
+
+#define R_ERROR_BOX(msg1, msg2) { \
+ errorBox(msg1, msg2, this); \
+ return; \
+}
+
+template <class Type>
+bool XOR(Type a, Type b)
+{
+ return ( (!a && b) || (a && !b) );
+}
+
+#endif // DEFINES_H
diff --git a/libksirtet/lib/internal.cpp b/libksirtet/lib/internal.cpp
new file mode 100644
index 00000000..c7b69f9e
--- /dev/null
+++ b/libksirtet/lib/internal.cpp
@@ -0,0 +1,278 @@
+#include "internal.h"
+#include "internal.moc"
+
+#include <qptrlist.h>
+#include <klocale.h>
+#include "mp_interface.h"
+
+#define DATA_ERROR(i) { dataError(i); return; }
+#define READ_ERROR(i) { readError(i); return; }
+#define WRITE_ERROR(i) { writeError(i); return; }
+#define BROKE_ERROR(i) { brokeError(i); return; }
+#define LAG_ERROR { lagError(); return; }
+
+
+//-----------------------------------------------------------------------------
+void Local::dataError(uint i)
+{
+ qWarning("MP : Invalid data from board #%i", i);
+}
+
+void Local::readData(bool inverse)
+{
+ // read data from local boards
+ for (uint i=0; i<ios.size(); i++) {
+ boards[i].ptr->dataOut(ios[i]->writing);
+ if (inverse) ios[i]->writingToReading();
+ }
+}
+
+void Local::writeData(bool inverse)
+{
+ // write data to local boards
+ for (uint i=0; i<ios.size(); i++) {
+ if (inverse) ios[i]->writingToReading();
+ boards[i].ptr->dataIn(ios[i]->reading);
+ if ( !ios[i]->reading.readOk() ) DATA_ERROR(i);
+ }
+}
+
+void Local::treatData()
+{
+ readData(TRUE);
+ interface->treatData();
+ // check reading stream
+ for (uint i=0; i<ios.size(); i++)
+ if ( !ios[i]->reading.readOk() ) DATA_ERROR(i);
+ writeData(TRUE);
+}
+
+//-----------------------------------------------------------------------------
+Server::Server(uint _interval)
+: interval(_interval)
+{
+ timer.start(interval);
+}
+
+void Server::congestion()
+{
+ qWarning("MP : congestion occurred !!");
+}
+
+void Server::serverTimeout()
+{
+ ctimer.start(2*interval, TRUE);
+ timeout();
+}
+
+//-----------------------------------------------------------------------------
+Network::Network(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd)
+: Local(_interface, _boards)
+{
+ RemoteData rd;
+ QPtrListIterator<RemoteHostData> it(rhd);
+ for (; it.current(); ++it) {
+ rd.socket = it.current()->socket;
+ rd.socket->notifier()->setEnabled(TRUE);
+ connect(rd.socket->notifier(), SIGNAL(activated(int)),
+ SLOT(notifier(int)));
+ uint nb = it.current()->bds.count();
+ Q_ASSERT( nb>=1 );
+ rd.array = new BufferArray(nb);
+ for (uint k=0; k<it.current()->bds.count(); k++)
+ rd.names += it.current()->bds[k].name;
+ remotes += rd;
+ }
+}
+
+Network::~Network()
+{
+ for (uint i=0; i<remotes.count(); i++) {
+ delete remotes[i].socket;
+ delete remotes[i].array;
+ }
+}
+
+uint Network::nbPlayers() const
+{
+ uint nb = Local::nbPlayers();
+ for (uint i=0; i<remotes.count(); i++) nb += remotes[i].array->size();
+ return nb;
+}
+
+QString Network::playerName(uint i) const
+{
+ uint l = Local::nbPlayers();
+ if ( i<l ) return Local::playerName(i);
+ for (uint k=0; k<remotes.count(); k++) {
+ uint ll = remotes[k].array->size();
+ if ( i<(l+ll) ) return remotes[k].names[i-l];
+ l += ll;
+ }
+ return QString::null; // dummy
+}
+
+IOBuffer *Network::ioBuffer(uint i) const
+{
+ if ( i<Local::nbPlayers() ) return Local::ioBuffer(i);
+ i -= Local::nbPlayers();
+ for (uint k=0; k<remotes.count(); k++) {
+ if ( i<remotes[k].array->size() ) return (*remotes[k].array)[i];
+ i -= remotes[k].array->size();
+ }
+ Q_ASSERT(FALSE);
+ return 0;
+}
+
+void Network::readError(uint i)
+{
+ disconnectHost(i, i18n("Unable to read socket"));
+}
+
+void Network::writeError(uint i)
+{
+ disconnectHost(i, i18n("Unable to write to socket"));
+}
+
+void Network::brokeError(uint i)
+{
+ disconnectHost(i, i18n("Link broken"));
+}
+
+void Network::disconnectHost(uint i, const QString &msg)
+{
+ delete remotes[i].socket;
+ delete remotes[i].array;
+ remotes.remove(remotes.at(i));
+ interface->hostDisconnected(i, msg);
+}
+
+//-----------------------------------------------------------------------------
+LocalServer::LocalServer(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards,
+ uint _interval)
+: Local(_interface, _boards), Server(_interval)
+{
+ connect(&timer, SIGNAL(timeout()), SLOT(timeoutSlot()));
+ connect(&ctimer, SIGNAL(timeout()), SLOT(congestionTimeoutSlot()));
+ serverTimeout();
+}
+
+//-----------------------------------------------------------------------------
+NetworkServer::NetworkServer(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd, uint _interval)
+: Network(_interface, _boards, rhd), Server(_interval),
+ nbReceived(remotes.count())
+{
+ connect(&timer, SIGNAL(timeout()), SLOT(timeoutSlot()));
+ connect(&ctimer, SIGNAL(timeout()), SLOT(congestionTimeoutSlot()));
+ // to catch unexpected data
+ for (uint i=0; i<remotes.count(); i++) remotes[i].received = TRUE;
+ nbReceived = remotes.count();
+// let the client the time to create itself ...
+// serverTimeout();
+}
+
+void NetworkServer::timeout()
+{
+ if ( nbReceived<remotes.count() ) LAG_ERROR;
+ nbReceived = 0;
+ for (uint i=0; i<remotes.count(); i++) remotes[i].received = FALSE;
+ // send MF_ASK : asking for data from clients
+ for (uint i=0; i<remotes.count(); i++) {
+ remotes[i].socket->writingStream() << MF_Ask;
+// debug("SERVER : send ask flag");
+ if ( !remotes[i].socket->write() ) WRITE_ERROR(i);
+ }
+}
+
+void NetworkServer::notifier(int fd)
+{
+ uint i;
+ for (i=0; i<remotes.count(); i++)
+ if ( remotes[i].socket->fd()==fd ) break;
+ Q_ASSERT( i<remotes.count() );
+
+ if ( remotes[i].received ) READ_ERROR(i);
+ switch ( remotes[i].socket->read() ) {
+ case -1: READ_ERROR(i);
+ case 0: BROKE_ERROR(i);
+ }
+
+ remotes[i].received = TRUE;
+ nbReceived++;
+
+ ReadingStream &s = remotes[i].socket->readingStream();
+// debug("SERVER : notifier + read (fd=%i i=%i size=%i)", fd, i,
+// s.size());
+ s >> *remotes[i].array;
+ if ( !s.readOk() ) DATA_ERROR(i);
+
+ // all data from clients received
+ if ( nbReceived==remotes.count() ) treatData();
+}
+
+void NetworkServer::writeData(bool inverse)
+{
+ Local::writeData(inverse);
+ for (uint i=0; i<remotes.count(); i++) {
+ WritingStream &s = remotes[i].socket->writingStream();
+ s << MF_Data;
+ s << *remotes[i].array;
+ s.writeRawBytes(globalStream()->buffer().data(),
+ globalStream()->size());
+// debug("SERVER : write data (size= 1 + %i + %i=%i)",
+// globalStream()->size(), s.size()-globalStream()->size()-1,
+// s.size());
+ if ( !remotes[i].socket->write() ) WRITE_ERROR(i);
+ }
+ globalStream()->clear();
+}
+
+void NetworkServer::lagError()
+{
+ for (uint i=0; i<remotes.count(); i++)
+ if ( !remotes[i].received )
+ disconnectHost(i, i18n("Client has not answered in time"));
+}
+
+//-----------------------------------------------------------------------------
+void Client::notifier(int)
+{
+ switch ( remotes[0].socket->read() ) {
+ case -1: READ_ERROR(0);
+ case 0: BROKE_ERROR(0);
+ }
+ ReadingStream &s = remotes[0].socket->readingStream();
+ MetaFlag mf;
+ s >> mf;
+ if ( !s.readOk() ) DATA_ERROR(0);
+// debug("CLIENT : reading stream (size=%i flag=%i)", s.size(),
+// (int)mf);
+ switch(mf) {
+ case MF_Ask:
+ // write data from local boards to server socket (cleaning
+ // of writing stream is done in write())
+ readData(FALSE);
+ remotes[0].socket->writingStream() << ios;
+// debug("CLIENT : send ios (size=%i)",
+// remotes[0].socket->writingStream().size());
+ if ( !remotes[0].socket->write() ) WRITE_ERROR(0);
+ break;
+ case MF_Data:
+ // read data from server to interface & local boards
+// debug("CLIENT : before receive ios (at=%i)", s.device()->at());
+ s >> ios;
+// debug("CLIENT : after receive ios (at=%i)", s.device()->at());
+ interface->dataFromServer(s);
+// debug("CLIENT : after dataFromServer (at=%i)", s.device()->at());
+ if ( !s.readOk() ) DATA_ERROR(0);
+ writeData(FALSE);
+ break;
+ default: DATA_ERROR(0);
+ }
+ if ( !s.atEnd() ) qWarning("CLIENT : remaining data");
+}
diff --git a/libksirtet/lib/internal.h b/libksirtet/lib/internal.h
new file mode 100644
index 00000000..4dece16a
--- /dev/null
+++ b/libksirtet/lib/internal.h
@@ -0,0 +1,152 @@
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+#include <qtimer.h>
+#include <qstringlist.h>
+
+#include "socket.h"
+#include "mp_interface.h"
+
+class MPBoard;
+class RemoteHostData;
+
+//-----------------------------------------------------------------------------
+class Local
+{
+ public:
+ Local(MPInterface *_interface, QValueList<MPInterface::Data> &_boards)
+ : interface(_interface), ios(_boards.count()), boards(_boards) {}
+ virtual ~Local() {}
+
+ virtual uint nbPlayers() const { return boards.count(); }
+ virtual QString playerName(uint i) const { return boards[i].name; }
+ virtual IOBuffer *ioBuffer(uint i) const { return ios[i]; }
+ virtual void writeData(bool inverse);
+ virtual WritingStream *globalStream() { return 0; }
+
+ protected:
+ MPInterface *interface;
+ BufferArray ios;
+
+ void dataError(uint i);
+ void readData(bool inverse);
+ void treatData();
+
+ private:
+ QValueList<MPInterface::Data> boards;
+};
+
+//-----------------------------------------------------------------------------
+class Server
+{
+ public:
+ Server(uint _interval);
+ virtual ~Server() {}
+
+ protected:
+ WritingStream stream;
+ QTimer timer, ctimer;
+
+ virtual void timeout() = 0;
+ void serverTimeout();
+ void congestion();
+
+ private:
+ uint interval;
+};
+
+//-----------------------------------------------------------------------------
+class Network : public QObject, public Local
+{
+ Q_OBJECT
+
+ public:
+ Network(MPInterface *_interface, QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd);
+ virtual ~Network();
+
+ virtual uint nbPlayers() const;
+ QString playerName(uint i) const;
+ IOBuffer *ioBuffer(uint i) const;
+
+ protected slots:
+ virtual void notifier(int fd) = 0;
+
+ protected:
+ class RemoteData {
+ public:
+ RemoteData() {}
+ Socket *socket;
+ BufferArray *array;
+ bool received;
+ QStringList names;
+ };
+ QValueList<RemoteData> remotes;
+
+ void readError(uint i);
+ void writeError(uint i);
+ void brokeError(uint i);
+ void disconnectHost(uint i, const QString &msg);
+};
+
+//-----------------------------------------------------------------------------
+class LocalServer : public QObject, public Local, public Server
+{
+ Q_OBJECT
+
+ public:
+ LocalServer(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards, uint _interval);
+
+ WritingStream *globalStream() { return &stream; }
+
+ private slots:
+ void timeoutSlot() { serverTimeout(); }
+ void congestionTimeoutSlot() { congestion(); }
+
+ private:
+ void timeout() { treatData(); }
+};
+
+//-----------------------------------------------------------------------------
+class NetworkServer : public Network, public Server
+{
+ Q_OBJECT
+
+ public:
+ NetworkServer(MPInterface *_interface,
+ QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd, uint _interval);
+
+ void writeData(bool inverse);
+ WritingStream *globalStream() { return &stream; }
+
+ private slots:
+ void timeoutSlot() { serverTimeout(); }
+ void congestionTimeoutSlot() { congestion(); }
+ void notifier(int fd);
+
+ private:
+ uint nbReceived;
+
+ void lagError();
+ void timeout();
+};
+
+//-----------------------------------------------------------------------------
+class Client : public Network
+{
+ Q_OBJECT
+
+ public:
+ Client(MPInterface *_interface, QValueList<MPInterface::Data> &_boards,
+ const QPtrList<RemoteHostData> &rhd)
+ : Network(_interface, _boards, rhd) {}
+
+ uint nbPlayers() const { return Local::nbPlayers(); }
+
+ private slots:
+ void notifier(int fd);
+};
+
+#endif // INTERNAL_H
diff --git a/libksirtet/lib/keys.cpp b/libksirtet/lib/keys.cpp
new file mode 100644
index 00000000..879f0bfa
--- /dev/null
+++ b/libksirtet/lib/keys.cpp
@@ -0,0 +1,104 @@
+#include "keys.h"
+#include "keys.moc"
+
+#include <qsignal.h>
+#include <kkeydialog.h>
+#include <klocale.h>
+
+
+KeyData::KeyData(uint maxNb, uint nbActions, const ActionData *data,
+ QObject *parent)
+ : QObject(parent), _maxNb(maxNb)
+{
+ _data.duplicate(data, nbActions);
+
+ for (uint n=0; n<maxNb; n++)
+ for (uint i=0; i<=n; i++)
+ _keycodes[n][i].fill(0, nbActions);
+}
+
+void KeyData::setKeycodes(uint nb, uint index, const int *keycodes)
+{
+ Q_ASSERT( nb!=0 && nb-1<_maxNb && index<nb );
+ for (uint n=nb-1; n<_maxNb; n++)
+ for (uint k=0; k<_data.size(); k++)
+ if ( n==nb-1 || _keycodes[n][index][k]==0 )
+ _keycodes[n][index][k] = keycodes[k];
+}
+
+void KeyData::setCurrentNb(uint nb)
+{
+ Q_ASSERT( nb<_maxNb );
+ clear();
+ _cols.fill(0, nb);
+}
+
+void KeyData::clear()
+{
+ for (uint i=0; i<_cols.size(); i++)
+ delete _cols[i];
+ _cols.resize(0);
+}
+
+void KeyData::createActionCollection(uint index, QWidget *receiver)
+{
+ Q_ASSERT( index<_cols.size() );
+ _cols[index] = new KActionCollection(receiver, this);
+ for (uint k=0; k<_data.size(); k++) {
+ QString label = i18n(_data[k].label);
+ QString name = QString("%2 %3").arg(index+1).arg(_data[k].name);
+ const char *slot = (_data[k].slotRelease ? 0 : _data[k].slot);
+ KAction *a = new KAction(label, _keycodes[_cols.size()-1][index][k],
+ receiver, slot, _cols[index], name.utf8());
+ a->setEnabled(false);
+ if ( slot==0 ) {
+ SpecialData data;
+ data.enabled = false;
+ data.pressed = new QSignal(this);
+ data.pressed->connect(receiver, _data[k].slot);
+ data.released = new QSignal(this);
+ data.released->connect(receiver, _data[k].slotRelease);
+ _specActions[a] = data;
+ }
+ }
+ _cols[index]->readShortcutSettings(group());
+}
+
+void KeyData::setEnabled(uint index, bool enabled)
+{
+ for (uint k=0; k<_cols[index]->count(); k++) {
+ QMap<KAction *, SpecialData>::Iterator it =
+ _specActions.find(_cols[index]->action(k));
+ if ( it==_specActions.end() )
+ _cols[index]->action(k)->setEnabled(enabled);
+ else (*it).enabled = enabled;
+ }
+}
+
+void KeyData::addKeys(KKeyDialog &d)
+{
+ for (uint i=0; i<_cols.size(); i++)
+ d.insert(_cols[i], i18n("Shortcuts for player #%1/%2").arg(i+1)
+ .arg(_cols.size()));
+}
+
+void KeyData::save()
+{
+ for (uint i=0; i<_cols.size(); i++)
+ _cols[i]->writeShortcutSettings(group());
+}
+
+void KeyData::keyEvent(QKeyEvent *e, bool pressed)
+{
+ if ( e->isAutoRepeat() ) return;
+
+ KKey key(e);
+ QMap<KAction *, SpecialData>::Iterator it = _specActions.begin();
+ for(; it!=_specActions.end(); ++it) {
+ if ( !it.data().enabled ) continue;
+ if ( !it.key()->shortcut().contains(key) ) continue;
+ if (pressed) it.data().pressed->activate();
+ else it.data().released->activate();
+ }
+ e->ignore();
+}
diff --git a/libksirtet/lib/keys.h b/libksirtet/lib/keys.h
new file mode 100644
index 00000000..07c08419
--- /dev/null
+++ b/libksirtet/lib/keys.h
@@ -0,0 +1,42 @@
+#ifndef KEYS_H
+#define KEYS_H
+
+#include <qmap.h>
+#include <kaction.h>
+
+#include "mp_interface.h"
+
+
+class KeyData : public QObject
+{
+ Q_OBJECT
+ public:
+ KeyData(uint maxNb, uint nbActions, const ActionData *,
+ QObject *parent);
+ void setKeycodes(uint nb, uint i, const int *keycodes);
+
+ void setCurrentNb(uint nb);
+ void clear();
+ void createActionCollection(uint index, QWidget *receiver);
+ void setEnabled(uint index, bool enabled);
+ void addKeys(KKeyDialog &);
+ void save();
+
+ void keyEvent(QKeyEvent *e, bool pressed);
+
+ private:
+ uint _maxNb;
+ QMemArray<ActionData> _data;
+ QMap<int, QMap<int, QMemArray<int> > > _keycodes;
+ QMemArray<KActionCollection *> _cols;
+ struct SpecialData {
+ bool enabled;
+ QSignal *pressed, *released;
+ };
+ QMap<KAction *, SpecialData> _specActions;
+
+ QString group() const
+ { return QString("Keys (%1 humans)").arg(_cols.size()); }
+};
+
+#endif // KEYS_H
diff --git a/libksirtet/lib/libksirtet_export.h b/libksirtet/lib/libksirtet_export.h
new file mode 100644
index 00000000..819e0029
--- /dev/null
+++ b/libksirtet/lib/libksirtet_export.h
@@ -0,0 +1,35 @@
+/*
+ This file is part of libkexif project
+ Copyright (c) 2005 Laurent Montel <[email protected]>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _LIBKEXIF_EXPORT_H
+#define _LIBKEXIF_EXPORT_H
+
+#ifdef KDEMACROS_USABLE
+#include <kdemacros.h>
+#endif
+
+#ifdef KDE_EXPORT
+#define LIBKSIRTET_EXPORT KDE_EXPORT
+#else
+#define LIBKSIRTET_EXPORT
+#endif
+
+#endif /* _LIBKEXIF_EXPORT_H */
+
diff --git a/libksirtet/lib/meeting.cpp b/libksirtet/lib/meeting.cpp
new file mode 100644
index 00000000..2cb6e285
--- /dev/null
+++ b/libksirtet/lib/meeting.cpp
@@ -0,0 +1,575 @@
+#include "meeting.h"
+
+#include <qmessagebox.h>
+#include <qpushbutton.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "defines.h"
+#include "mp_option.h"
+
+#define LIST_INTERVAL 0
+
+
+NetMeeting::NetMeeting(const cId &_id, Socket *socket,
+ MPOptionWidget *option,
+ bool _server, QWidget *parent, const char * name)
+: KDialogBase(Plain, i18n("Network Meeting"),
+ (_server ? Ok|Cancel|Help : Cancel|Help),
+ (_server ? Ok : Cancel), parent, name),
+ server(_server), ow(option), id(_id), socketRemoved(FALSE)
+{
+ sm.append(socket, SocketManager::ReadWrite);
+ sm[0]->notifier()->setEnabled(TRUE);
+
+/* top layout */
+ QVBoxLayout *top = new QVBoxLayout(plainPage(), spacingHint());
+ top->setResizeMode(QLayout::Fixed);
+
+ // server line
+ spl = new MeetingLine(server, server, true, plainPage());
+ top->addWidget(spl);
+
+ // widget list
+ wl = new WidgetList<MeetingLine>(LIST_INTERVAL, plainPage());
+ wl->hide();
+ top->addWidget(wl);
+
+ labWait = new QLabel(i18n("Waiting for clients"), plainPage());
+ labWait->setAlignment(AlignCenter);
+ top->addWidget(labWait);
+
+ // options widget
+// if (ow) top->addWidget(ow); #### FIXME
+
+ // status bar
+ status = new QStatusBar(plainPage());
+ status->setSizeGripEnabled(false);
+ top->addWidget(status);
+
+ // buttons
+ enableButtonSeparator(TRUE);
+ if (server) {
+ setButtonOK(i18n("Start Game"));
+ enableButtonOK(FALSE);
+ }
+ setButtonCancel(server ? i18n("Abort") : i18n("Quit"));
+ enableButton(Help, FALSE);
+}
+
+NetMeeting::~NetMeeting()
+{}
+
+void NetMeeting::appendLine(const MeetingLineData &pld, bool server)
+{
+ MeetingLine *pl;
+ pl = new MeetingLine(pld.own, server, false, wl);
+ if (pld.own) connect(pl, SIGNAL(textChanged(const QString &)),
+ SLOT(textChanged(const QString &)));
+ else message(i18n("A new client has just arrived (#%1)")
+ .arg(wl->size()+1));
+ pl->setData(pld.ed);
+ connect(pl, SIGNAL(typeChanged(MeetingCheckBox::Type)),
+ SLOT(typeChanged(MeetingCheckBox::Type)));
+ wl->append(pl);
+ waiting();
+}
+
+void NetMeeting::removeLine(uint i)
+{
+ wl->remove(i);
+ waiting();
+}
+
+void NetMeeting::waiting()
+{
+ if ( wl->size() ) {
+ labWait->hide();
+ wl->show();
+ } else {
+ labWait->show();
+ wl->hide();
+ }
+ if (server) enableButtonOK(ready());
+}
+
+void NetMeeting::setType(const TypeInfo &ti)
+{
+ if ( ti.i==0 ) spl->setType(ti.type); // in fact should not append
+ else {
+ wl->widget(ti.i-1)->setType(ti.type);
+ if (server) enableButtonOK(ready());
+ }
+}
+
+void NetMeeting::setText(const TextInfo &ti)
+{
+ if ( ti.i==0 ) spl->setText(ti.text);
+ else wl->widget(ti.i-1)->setText(ti.text);
+}
+
+bool NetMeeting::ready() const
+{
+ int nbReady = 0;
+ for(uint k=0; k<wl->size(); k++) {
+ switch ( wl->widget(k)->type() ) {
+ case MeetingCheckBox::Ready : nbReady++; break;
+ case MeetingCheckBox::NotReady : return FALSE;
+ default : break;
+ }
+ }
+ return ( nbReady!=0 );
+}
+
+void NetMeeting::cleanReject(const QString &str)
+{
+ sm.clean(); // remove the sockets immediately to avoid possible further mess
+ if ( !str.isEmpty() )
+ KMessageBox::information(this, str, caption());
+ KDialogBase::reject();
+}
+
+#define WRITE(i) if ( !sm[i]->write() ) { writeError(i); return; }
+#define CHECK_READ(i) if ( !sm[i]->readingStream().readOk() ) { dataError(i); return; }
+
+// Read incoming data
+void NetMeeting::readNotifier(int fd)
+{
+ int i = sm.find(fd);
+ Q_ASSERT( i!=-1 );
+ switch ( sm[i]->read() ) {
+ case -1: readError(i); break;
+ case 0: brokeError(i); break;
+ default: readData(i);
+ }
+}
+
+void NetMeeting::readData(uint i)
+{
+ // get message type
+ MeetingMsgFlag mt;
+ sm[i]->readingStream() >> mt;
+ CHECK_READ(i);
+ switch (mt) {
+ case EndFlag: endFlag(i); break;
+ case NewFlag: newFlag(i); break;
+ case Mod_TextFlag: modTextFlag(i); break;
+ case Mod_TypeFlag: modTypeFlag(i); break;
+ case IdFlag: idFlag(i); break;
+ case DelFlag: delFlag(i); break;
+ case Mod_OptFlag: modOptFlag(i); break;
+ case PlayFlag: playFlag(i); break;
+ default: dataError(i);
+ }
+
+ if (socketRemoved) socketRemoved = FALSE;
+ else if ( !sm[i]->readingStream().atEnd() )
+ readData(i); // more pending data
+}
+
+void NetMeeting::readError(uint i)
+ { netError(i, i18n("Error reading data from")); }
+void NetMeeting::dataError(uint i)
+ { netError(i, i18n("Unknown data from")); }
+void NetMeeting::writeError(uint i)
+ { netError(i, i18n("Error writing to")); }
+void NetMeeting::brokeError(uint i)
+ { netError(i, i18n("Link broken or empty data from")); }
+
+bool NetMeeting::checkState(uint i, PlayerState s)
+{
+ bool ok = ( players[i]==s );
+ if (!ok) dataError(i);
+ return ok;
+}
+
+bool NetMeeting::checkAndSetState(uint i, PlayerState os, PlayerState ns)
+{
+ bool ok = checkState(i, os);
+ if (ok) players[i] = ns;
+ return ok;
+}
+
+void NetMeeting::reject()
+{
+ // send an End flag
+ sm.commonWritingStream() << EndFlag;
+ writeToAll();
+
+ cleanReject();
+}
+
+void NetMeeting::accept()
+{
+ KDialogBase::accept();
+}
+
+void NetMeeting::message(const QString &str)
+{
+ status->message(str, 3000);
+}
+
+/** ServerNetMeeting *********************************************************/
+ServerNetMeeting::ServerNetMeeting(const cId &id,
+ const RemoteHostData &r, MPOptionWidget *option,
+ QPtrList<RemoteHostData> &arhd, QWidget *parent, const char * name)
+: NetMeeting(id, r.socket, option, TRUE, parent, name), rhd(arhd)
+{
+ connect(sm[0]->notifier(), SIGNAL(activated(int)), SLOT(newHost(int)));
+ players.append(Accepted); // server
+
+ // set server line
+ ExtData ed(r.bds, "", MeetingCheckBox::Ready);
+ spl->setData(ed);
+ connect(spl, SIGNAL(textChanged(const QString &)),
+ SLOT(textChanged(const QString &)));
+
+ // options signal
+ if (ow) connect(ow, SIGNAL(changed()), SLOT(optionsChanged()));
+}
+
+void ServerNetMeeting::writeToAll(uint i)
+{
+ for (uint k=1; k<sm.size(); k++) {
+ if ( k==i ) continue;
+ if ( !sm.writeCommon(k) ) writeError(k);
+ }
+ sm.commonWritingStream().clear();
+}
+
+void ServerNetMeeting::netError(uint i, const QString &type)
+{
+ Q_ASSERT( i!=0 );
+ disconnectHost(i, i18n("%1 client #%2: disconnect it").arg(type).arg(i));
+}
+
+void ServerNetMeeting::disconnectHost(uint i, const QString &str)
+{
+ sm.remove(i, true);
+ socketRemoved = TRUE;
+ if ( players[i]==Accepted ) {
+ removeLine(i-1);
+
+ // Send a Del message to all (other) clients
+ sm.commonWritingStream() << DelFlag << i;
+ writeToAll();
+ }
+ players.remove(players.at(i));
+ message(str);
+}
+
+void ServerNetMeeting::newHost(int)
+{
+ KExtendedSocket *s;
+ int res = sm[0]->accept(s);
+ if ( res!=0 ) {
+ message(i18n("Failed to accept incoming client:\n%1")
+ .arg(socketError(s)));
+ return;
+ }
+ players.append(NewPlayer);
+ Socket *socket = new Socket(s, true);
+ uint i = sm.append(socket, SocketManager::ReadWrite);
+ connect(sm[i]->notifier(), SIGNAL(activated(int)),
+ SLOT(readNotifier(int)));
+ sm[i]->notifier()->setEnabled(TRUE);
+}
+
+void ServerNetMeeting::idFlag(uint i)
+{
+ bool b = checkAndSetState(i, NewPlayer, IdChecked);
+ Q_ASSERT(b);
+
+ // get client id
+ cId clientId;
+ sm[i]->readingStream() >> clientId;
+ CHECK_READ(i);
+
+ // compare id
+ id.check(clientId);
+
+ // send result to client
+ Stream &s = sm[i]->writingStream();
+ s << IdFlag << id;
+ WRITE(i);
+
+ // if not accepted : remove socket and player from list
+ if ( !id.accepted() )
+ disconnectHost(i, i18n("Client rejected for incompatible ID"));
+}
+
+void ServerNetMeeting::endFlag(uint i)
+{
+ disconnectHost(i, i18n("Client #%1 has left").arg(i));
+}
+
+void ServerNetMeeting::newFlag(uint i)
+{
+ checkAndSetState(i, IdChecked, Accepted);
+
+ // get line infos from new client (GameData struct)
+ MeetingLineData pld;
+ sm[i]->readingStream() >> pld.ed.bds;
+ CHECK_READ(i);
+
+ // complete the MeetingLineData struct with initial values
+ pld.own = FALSE; // client line
+ pld.ed.type = MeetingCheckBox::NotReady; // not ready by default
+ pld.ed.text = ""; // empty line to begin with
+ appendLine(pld, TRUE);
+
+ // send to the new client already present lines including its own
+ // (New flag + MeetingLineData struct)
+ spl->data(pld.ed);
+ sm[i]->writingStream() << NewFlag << pld.ed;
+ for(uint k=1; k<sm.size(); k++) {
+ wl->widget(k-1)->data(pld.ed);
+ pld.own = ( k==i );
+ sm[i]->writingStream() << NewFlag << pld;
+ }
+ WRITE(i);
+
+ // send to all other clients the new line (New flag + MeetingLineData struct)
+ wl->widget(i-1)->data(pld.ed);
+ pld.own = FALSE;
+ sm.commonWritingStream() << NewFlag << pld;
+ writeToAll(i);
+}
+
+void ServerNetMeeting::modTextFlag(uint i)
+{
+ checkState(i-1, Accepted);
+
+ // the client i has just sent a new text (QString)
+ TextInfo ti;
+ sm[i]->readingStream() >> ti.text;
+ CHECK_READ(i);
+ ti.i = i;
+ setText(ti);
+
+ // send it to all other clients (Mod_Text flag + TextInfo struct)
+ sm.commonWritingStream() << Mod_TextFlag << ti;
+ writeToAll(i);
+}
+
+void ServerNetMeeting::modTypeFlag(uint i)
+{
+ checkState(i-1, Accepted);
+
+ // a client has just sent a new TCB type (TCB type)
+ TypeInfo ti;
+ sm[i]->readingStream() >> ti.type;
+ CHECK_READ(i);
+ ti.i = i;
+ setType(ti);
+
+ // send it to all other clients (Mod_Type flag + TypeInfo struct)
+ sm.commonWritingStream() << Mod_TypeFlag << ti;
+ writeToAll(i);
+}
+
+void ServerNetMeeting::textChanged(const QString &text)
+{
+ // server line text changed : send to every clients (Mod_Text flag + TextInfo struct)
+ TextInfo ti; ti.i = 0; ti.text = text;
+ sm.commonWritingStream() << Mod_TextFlag << ti;
+ writeToAll();
+}
+
+void ServerNetMeeting::typeChanged(MeetingCheckBox::Type type)
+{
+ Q_ASSERT( sender()!=spl ); // server TCB not modifiable
+ // the server has changed a client TCB
+
+ // find the changed TCB index
+ TypeInfo ty;
+ ty.type = type;
+ for (ty.i=0; ty.i<wl->size(); ty.i++)
+ if ( sender()==wl->widget(ty.i) ) break;
+ ty.i++;
+
+ // TCB change : send to every clients (Mod_Type flag + TypeInfo struct)
+ sm.commonWritingStream() << Mod_TypeFlag << ty;
+ writeToAll();
+ if (server) enableButtonOK(ready());
+}
+
+void ServerNetMeeting::accept()
+{
+ Q_ASSERT( ready() && rhd.count()==0 );
+
+ // stop receiving data from clients (will be buffered by OS)
+ for (uint k=0; k<sm.size(); k++) disconnect(sm[k]->notifier());
+ sm.remove(0, true);
+
+ // check which client will play and fill RemoteHostData array
+ ExtData ed;
+ bool willPlay;
+ for (uint k=1; k<players.count(); k++) {
+ willPlay = FALSE;
+
+ if ( players[k]==Accepted ) { // client with lines
+ wl->widget(k-1)->data(ed);
+ if ( ed.type==MeetingCheckBox::Ready ) {
+ willPlay = TRUE;
+ RemoteHostData *r = new RemoteHostData;
+ r->socket = sm[0];
+ r->bds = ed.bds;
+ rhd.append(r);
+ }
+
+ // send play message to client (Play flag
+ // + bool [accepted/rejected])
+ sm[0]->writingStream() << PlayFlag << (Q_UINT8)willPlay;
+ // if write failed and the client is not playing : silently
+ // put it aside ...
+ if ( !sm[0]->write() && willPlay ) {
+ cleanReject(i18n("Unable to write to client #%1 at game "
+ "beginning."));
+ return;
+ }
+ }
+
+ sm[0]->notifier()->setEnabled(false);
+ sm.remove(0, !willPlay);
+ }
+
+ NetMeeting::accept();
+}
+
+void ServerNetMeeting::optionsChanged()
+{
+ sm.commonWritingStream() << Mod_OptFlag;
+ ow->dataOut( sm.commonWritingStream() );
+ writeToAll();
+}
+
+/** ClientNetMeeting *********************************************************/
+ClientNetMeeting::ClientNetMeeting(const cId &id,
+ const RemoteHostData &rhd, MPOptionWidget *option,
+ QWidget *parent, const char * name)
+: NetMeeting(id, rhd.socket, option, FALSE, parent, name), bds(rhd.bds)
+{
+ connect(sm[0]->notifier(), SIGNAL(activated(int)),
+ SLOT(readNotifier(int)));
+ players.append(NewPlayer); // server player
+
+ // Send id to server (Id flag + Id struct)
+ sm.commonWritingStream() << IdFlag << id;
+ writeToAll(); // what happens if there is a message box appearing before exec() call ??
+}
+
+void ClientNetMeeting::netError(uint, const QString &str)
+{
+ cleanReject(i18n("%1 server: aborting connection.").arg(str));
+}
+
+void ClientNetMeeting::writeToAll(uint)
+{
+ if ( !sm.writeCommon(0) ) writeError(0);
+ sm.commonWritingStream().clear();
+}
+
+void ClientNetMeeting::idFlag(uint)
+{
+ checkAndSetState(0, NewPlayer, IdChecked);
+
+ // read Id result (Id flag + Id struct)
+ cId serverId;
+ sm[0]->readingStream() >> serverId;
+ CHECK_READ(0);
+
+ // check result
+ if ( !serverId.accepted() ) cleanReject(serverId.errorMessage(id));
+ else {
+ // send client info (New flag + GameData struct)
+ sm.commonWritingStream() << NewFlag << bds;
+ writeToAll();
+ }
+}
+
+void ClientNetMeeting::newFlag(uint)
+{
+ if ( players[0]==IdChecked ) {
+ ExtData ed;
+ sm[0]->readingStream() >> ed;
+ spl->setData(ed);
+ players[0] = Accepted;
+ } else {
+ MeetingLineData pld;
+ sm[0]->readingStream() >> pld;
+ appendLine(pld, FALSE);
+ }
+ CHECK_READ(0);
+}
+
+void ClientNetMeeting::modTextFlag(uint)
+{
+ // receive new text from server (TextInfo struct)
+ TextInfo ti;
+ sm[0]->readingStream() >> ti;
+ CHECK_READ(0);
+ setText(ti);
+}
+
+void ClientNetMeeting::modTypeFlag(uint)
+{
+ // receive new type from server (TypeInfo struct)
+ TypeInfo ti;
+ sm[0]->readingStream() >> ti;
+ CHECK_READ(0);
+ setType(ti);
+}
+
+void ClientNetMeeting::delFlag(uint)
+{
+ // receive client number (uint)
+ uint k;
+ sm[0]->readingStream() >> k;
+ CHECK_READ(0);
+ removeLine(k-1);
+ message(i18n("Client %1 has left").arg(k));
+}
+
+void ClientNetMeeting::textChanged(const QString &text)
+{
+ // text changed : send to server (Mod_Text flag + QString)
+ sm.commonWritingStream() << Mod_TextFlag << text;
+ writeToAll();
+}
+
+void ClientNetMeeting::typeChanged(MeetingCheckBox::Type type)
+{
+ // type changed : send to server (Mod_Type flag + TCB)
+ sm.commonWritingStream() << Mod_TypeFlag << type;
+ writeToAll();
+}
+
+void ClientNetMeeting::playFlag(uint)
+{
+ // receive accept or reject (bool)
+ Q_UINT8 i;
+ sm[0]->readingStream() >> i;
+ CHECK_READ(0);
+ sm[0]->notifier()->setEnabled(false);
+ sm.remove(0, i==0);
+ socketRemoved = true;
+ if (i) accept();
+ else cleanReject(i18n("The game has begun without you\n"
+ "(You have been excluded by the server)."));
+}
+
+void ClientNetMeeting::modOptFlag(uint)
+{
+ // read new option data
+ ow->dataIn( sm[0]->readingStream() );
+ CHECK_READ(0);
+}
+
+void ClientNetMeeting::endFlag(uint)
+{
+ // abort from server
+ cleanReject(i18n("The server has aborted the game."));
+}
+#include "meeting.moc"
diff --git a/libksirtet/lib/meeting.h b/libksirtet/lib/meeting.h
new file mode 100644
index 00000000..cb534404
--- /dev/null
+++ b/libksirtet/lib/meeting.h
@@ -0,0 +1,137 @@
+#ifndef MEETING_H
+#define MEETING_H
+
+#include <qstatusbar.h>
+#include <kdialogbase.h>
+#include "smanager.h"
+#include "pline.h"
+#include "types.h"
+
+class MPOptionWidget;
+
+/** Internal class : net meeting. */
+class NetMeeting : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ // "gameName" and "gameId" are QByteArray because they are
+ // used for ID comparing between games.
+ NetMeeting(const cId &id, Socket *, MPOptionWidget *option, bool server,
+ QWidget *parent = 0, const char * name = 0);
+ virtual ~NetMeeting();
+
+ protected slots:
+ void readNotifier(int socket);
+ virtual void textChanged(const QString &) = 0;
+ virtual void typeChanged(MeetingCheckBox::Type) = 0;
+ virtual void reject();
+ virtual void accept();
+
+ protected:
+ enum PlayerState { NewPlayer, IdChecked, Accepted };
+ QValueList<PlayerState> players;
+ bool server;
+ MeetingLine *spl;
+ WidgetList<MeetingLine> *wl;
+ SocketManager sm;
+ MPOptionWidget *ow;
+ cId id;
+ bool socketRemoved;
+
+ void appendLine(const MeetingLineData &pld, bool server);
+ void removeLine(uint i);
+ void setType(const TypeInfo &ti);
+ void setText(const TextInfo &ti);
+
+ void cleanReject(const QString &str = QString::null);
+ bool checkState(uint i, PlayerState s);
+ bool checkAndSetState(uint i, PlayerState os, PlayerState ns);
+ bool ready() const;
+
+ virtual void idFlag(uint i) { dataError(i); }
+ virtual void newFlag(uint i) { dataError(i); }
+ virtual void endFlag(uint i) { dataError(i); }
+ virtual void modTypeFlag(uint i) { dataError(i); }
+ virtual void modTextFlag(uint i) { dataError(i); }
+ virtual void delFlag(uint i) { dataError(i); }
+ virtual void modOptFlag(uint i) { dataError(i); }
+ virtual void playFlag(uint i) { dataError(i); }
+
+ virtual void netError(uint i, const QString &str) = 0;
+ virtual void writeToAll(uint i=0) = 0;
+ void readError(uint i);
+ void writeError(uint i);
+ void dataError(uint i);
+ void brokeError(uint i);
+ void message(const QString &str);
+
+ private:
+ QLabel *labWait;
+ QStatusBar *status;
+
+ void waiting();
+ void readData(uint i);
+};
+
+class ServerNetMeeting : public NetMeeting
+{
+ Q_OBJECT
+
+ public:
+ ServerNetMeeting(const cId &id,
+ const RemoteHostData &rhd, MPOptionWidget *options,
+ QPtrList<RemoteHostData> &arhd,
+ QWidget *parent = 0, const char * name = 0);
+
+ private slots:
+ void newHost(int);
+ void textChanged(const QString &text);
+ void typeChanged(MeetingCheckBox::Type);
+ void accept();
+ void optionsChanged();
+
+ private:
+ QPtrList<RemoteHostData> &rhd;
+
+ void idFlag(uint i);
+ void newFlag(uint i);
+ void endFlag(uint i);
+ void modTypeFlag(uint i);
+ void modTextFlag(uint i);
+
+ void netError(uint i, const QString &str);
+ void writeToAll(uint i = 0);
+ void disconnectHost(uint i, const QString &str);
+};
+
+class ClientNetMeeting : public NetMeeting
+{
+ Q_OBJECT
+
+ public:
+ ClientNetMeeting(const cId &id,
+ const RemoteHostData &rhd, MPOptionWidget *options,
+ QWidget *parent = 0, const char * name = 0);
+
+ private slots:
+ void textChanged(const QString &text);
+ void typeChanged(MeetingCheckBox::Type);
+
+ private:
+ QValueList<BoardData> bds;
+
+ void idFlag(uint);
+ void newFlag(uint);
+ void endFlag(uint);
+ void delFlag(uint);
+ void modTypeFlag(uint);
+ void modTextFlag(uint);
+ void modOptFlag(uint);
+ void playFlag(uint);
+
+ void writeToAll(uint i=0);
+ void netError(uint, const QString &str);
+};
+
+#endif // MEETING_H
diff --git a/libksirtet/lib/miscui.cpp b/libksirtet/lib/miscui.cpp
new file mode 100644
index 00000000..28f00914
--- /dev/null
+++ b/libksirtet/lib/miscui.cpp
@@ -0,0 +1,58 @@
+#include "miscui.h"
+#include "miscui.moc"
+
+#include <qlayout.h>
+
+#include <klocale.h>
+
+
+//-----------------------------------------------------------------------------
+MeetingCheckBox::MeetingCheckBox(Type type, bool owner, bool server,
+ QWidget *parent)
+ : QWidget(parent, "meeting_check_box")
+{
+ QVBoxLayout *vbox = new QVBoxLayout(this);
+
+ _ready = new QCheckBox(i18n("Ready"), this);
+ vbox->addWidget(_ready);
+ _ready->setEnabled(owner);
+ connect(_ready, SIGNAL(clicked()), SLOT(changedSlot()));
+
+ _excluded = new QCheckBox(i18n("Excluded"), this);
+ vbox->addWidget(_excluded);
+ _excluded->setEnabled(server);
+ connect(_excluded, SIGNAL(clicked()), SLOT(changedSlot()));
+
+ setType(type);
+}
+
+void MeetingCheckBox::setType(Type type)
+{
+ _ready->setChecked( type==Ready );
+ _excluded->setChecked( type==Excluded );
+}
+
+MeetingCheckBox::Type MeetingCheckBox::type() const
+{
+ if ( _excluded->isChecked() ) return Excluded;
+ if ( _ready->isChecked() ) return Ready;
+ return NotReady;
+}
+
+void MeetingCheckBox::changedSlot()
+{
+ emit changed(type());
+}
+
+//-----------------------------------------------------------------------------
+PlayerComboBox::PlayerComboBox(Type type, bool canBeEmpty, bool acceptAI,
+ QWidget *parent)
+ : QComboBox(parent, "player_combo_box")
+{
+ insertItem(i18n("Human"));
+ if (acceptAI) insertItem(i18n("AI"));
+ if (canBeEmpty) insertItem(i18n("None"));
+ setCurrentItem(type);
+
+ connect(this, SIGNAL(activated(int)), SIGNAL(changed(int)));
+}
diff --git a/libksirtet/lib/miscui.h b/libksirtet/lib/miscui.h
new file mode 100644
index 00000000..7de5dfbc
--- /dev/null
+++ b/libksirtet/lib/miscui.h
@@ -0,0 +1,43 @@
+#ifndef MISCUI_H
+#define MISCUI_H
+
+#include <qcombobox.h>
+#include <qcheckbox.h>
+
+
+//-----------------------------------------------------------------------------
+class MeetingCheckBox : public QWidget
+{
+ Q_OBJECT
+ public:
+ enum Type { Ready, NotReady, Excluded };
+ MeetingCheckBox(Type, bool owner, bool server, QWidget *parent);
+
+ void setType(Type);
+ Type type() const;
+
+ signals:
+ void changed(int);
+
+ private slots:
+ void changedSlot();
+
+ private:
+ QCheckBox *_ready, *_excluded;
+};
+
+//-----------------------------------------------------------------------------
+class PlayerComboBox : public QComboBox
+{
+ Q_OBJECT
+ public:
+ enum Type { Human = 0, AI, None };
+ PlayerComboBox(Type, bool canBeNone, bool acceptAI, QWidget *parent);
+
+ Type type() const { return (Type)currentItem(); }
+
+ signals:
+ void changed(int);
+};
+
+#endif // MISCUI_H
diff --git a/libksirtet/lib/mp_board.h b/libksirtet/lib/mp_board.h
new file mode 100644
index 00000000..fbd1e01f
--- /dev/null
+++ b/libksirtet/lib/mp_board.h
@@ -0,0 +1,51 @@
+#ifndef MP_BOARD_H
+#define MP_BOARD_H
+
+#include <qwidget.h>
+
+/**
+ * The MP_Board class is the base widget from which each individual
+ * board should inheritate ; you must implement its virtual methods.
+ */
+class MPBoard : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ MPBoard(QWidget *parent, const char *name=0)
+ : QWidget(parent, name) {}
+ virtual ~MPBoard() {}
+
+ /**
+ * This method is called once at the board creation.
+ * @param AI is TRUE if the player is not human.
+ * @param multiplayers is TRUE if the game is not a single player game.
+ * @param first is TRUE if this board is the first local one.
+ */
+ virtual void init(bool AI, bool multiplayers, bool server, bool first,
+ const QString &name) = 0;
+
+ /**
+ * Put data on the stream.
+ *
+ * This method is the communication way out. The data given here will
+ * be the only information that will go to the server.
+ */
+ virtual void dataOut(QDataStream &) = 0;
+
+ /**
+ * Get data from the stream.
+ *
+ * This method is the communication way in. The data given here will be
+ * the only information that you will receive from the server.
+ */
+ virtual void dataIn(QDataStream &) = 0;
+
+ signals:
+ /**
+ * Call this signal to enable/disable the keys associated with a board.
+ */
+ void enableKeys(bool);
+};
+
+#endif // MP_BOARD_H
diff --git a/libksirtet/lib/mp_interface.cpp b/libksirtet/lib/mp_interface.cpp
new file mode 100644
index 00000000..61742562
--- /dev/null
+++ b/libksirtet/lib/mp_interface.cpp
@@ -0,0 +1,274 @@
+#include "mp_interface.h"
+#include "mp_interface.moc"
+
+#include <qpainter.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kkeydialog.h>
+
+#include "defines.h"
+#include "types.h"
+#include "meeting.h"
+#include "internal.h"
+#include "keys.h"
+#include "wizard.h"
+
+/*****************************************************************************/
+/* Constructor & Destructor */
+/*****************************************************************************/
+MPInterface::MPInterface(const MPGameInfo &_gameInfo,
+ uint nbActions, const ActionData *data,
+ QWidget *parent, const char *name)
+: QWidget(parent, name), internal(0), gameInfo(_gameInfo), nbLocalHumans(0)
+{
+ Q_ASSERT( gameInfo.maxNbLocalPlayers>=1 );
+
+ hbl = new QHBoxLayout(this, 0, 5);
+ hbl->setResizeMode( QLayout::Fixed );
+
+ _keyData = new KeyData(gameInfo.maxNbLocalPlayers, nbActions, data, this);
+}
+
+MPInterface::~MPInterface()
+{
+ delete internal;
+}
+
+/*****************************************************************************/
+/* Game creation */
+/*****************************************************************************/
+void MPInterface::clear()
+{
+ if (internal) {
+ stop();
+ delete internal;
+ internal = 0;
+ _keyData->clear();
+ }
+}
+
+void MPInterface::dialog()
+{
+ clear();
+
+ // configuration wizard
+ ConnectionData cd;
+ MPWizard wiz(gameInfo, cd, this);
+ if ( wiz.exec()==QDialog::Rejected ) {
+ singleHuman(); // create a single game
+ return;
+ }
+
+ // net meeting
+ QPtrList<RemoteHostData> rhd;
+ rhd.setAutoDelete(TRUE);
+ if (cd.network) {
+ cId id(kapp->name(), gameInfo.gameId);
+ MPOptionWidget *ow = newOptionWidget();
+ NetMeeting *nm;
+ if (cd.server) nm = new ServerNetMeeting(id, cd.rhd, ow, rhd, this);
+ else nm = new ClientNetMeeting(id, cd.rhd, ow, this);
+ int res = nm->exec();
+ if (ow) {
+ if (res) ow->saveData();
+ delete ow;
+ }
+ delete nm;
+ if (!res) {
+ singleHuman();
+ return;
+ }
+ }
+
+ createLocalGame(cd);
+ if (cd.server) createServerGame(rhd);
+ else createClientGame(cd.rhd);
+}
+
+void MPInterface::specialLocalGame(uint nbHumans, uint nbAIs)
+{
+ clear();
+
+ ConnectionData cd;
+ BoardData bd;
+ PlayerComboBox::Type t;
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+ for (uint i=0; i<(nbHumans+nbAIs); i++) {
+ bd.type = (i<nbHumans ? PlayerComboBox::Human : PlayerComboBox::AI);
+ bd.name = QString::null;
+ t = (PlayerComboBox::Type)
+ cg.config()->readNumEntry(QString(MP_PLAYER_TYPE).arg(i),
+ PlayerComboBox::None);
+ if ( bd.type==t )
+ bd.name = cg.config()->readEntry(QString(MP_PLAYER_NAME).arg(i),
+ QString::null);
+ if ( bd.name.isNull() )
+ bd.name = (i<nbHumans ? i18n("Human %1").arg(i+1)
+ : i18n("AI %1").arg(i-nbHumans+1));
+ cd.rhd.bds += bd;
+ }
+ cd.server = TRUE;
+ cd.network = FALSE;
+ Q_ASSERT( (nbHumans+nbAIs)<=gameInfo.maxNbLocalPlayers );
+ Q_ASSERT( gameInfo.AIAllowed || nbAIs==0 );
+
+ createLocalGame(cd);
+ QPtrList<RemoteHostData> rhd;
+ createServerGame(rhd);
+}
+
+void MPInterface::createServerGame(const QPtrList<RemoteHostData> &rhd)
+{
+ internal = (rhd.count()
+ ? (Local *)new NetworkServer(this, boards, rhd, gameInfo.interval)
+ : (Local *)new LocalServer(this, boards, gameInfo.interval));
+ init();
+}
+
+void MPInterface::createClientGame(const RemoteHostData &rhd)
+{
+ QPtrList<RemoteHostData> r;
+ r.append((RemoteHostData *)&rhd);
+ internal = new Client(this, boards, r);
+ init();
+}
+
+void MPInterface::createLocalGame(const ConnectionData &cd)
+{
+ _server = cd.server;
+ nbLocalHumans = 0;
+ for (uint i=0; i<cd.rhd.bds.count(); i++)
+ if ( cd.rhd.bds[i].type==PlayerComboBox::Human ) nbLocalHumans++;
+
+ // add or remove boards
+ uint old_s = boards.count();
+ uint new_s = cd.rhd.bds.count();
+ for (uint i=new_s; i<old_s; i++) {
+ delete boards[i].ptr;
+ boards.remove(boards.at(i));
+ }
+ Data d;
+ for(uint i=old_s; i<new_s; i++) {
+ d.ptr = newBoard(i);
+ hbl->addWidget(d.ptr);
+ d.ptr->show();
+ connect(d.ptr, SIGNAL(enableKeys(bool)), SLOT(enableKeys(bool)));
+ boards += d;
+ }
+
+ // init local boards
+ _keyData->setCurrentNb(nbLocalHumans);
+ int k = 0;
+ for (uint i=0; i<boards.count(); i++) {
+ bool h = ( cd.rhd.bds[i].type==PlayerComboBox::Human );
+ boards[i].humanIndex = (h ? k : -1);
+ if (h) {
+ _keyData->createActionCollection(k, boards[i].ptr);
+ k++;
+ }
+ boards[i].name = cd.rhd.bds[i].name;
+ boards[i].ptr->init(!h, cd.network || boards.count()>1, _server, i==0,
+ cd.rhd.bds[i].name);
+ }
+}
+
+/*****************************************************************************/
+/* Key management */
+/*****************************************************************************/
+void MPInterface::setDefaultKeycodes(uint nb, uint i, const int *def)
+{
+ _keyData->setKeycodes(nb, i, def);
+}
+
+void MPInterface::addKeys(KKeyDialog &d)
+{
+ _keyData->addKeys(d);
+}
+
+void MPInterface::saveKeys()
+{
+ _keyData->save();
+}
+
+void MPInterface::enableKeys(bool enable)
+{
+ if ( nbLocalHumans==0 ) return;
+ // find the sending board
+ uint i;
+ for (i=0; i<boards.count(); i++) if ( sender()==boards[i].ptr ) break;
+ int hi = boards[i].humanIndex;
+ if ( hi==-1 ) return; // AI board (no keys)
+ _keyData->setEnabled(hi, enable);
+}
+
+void MPInterface::keyPressEvent(QKeyEvent *e)
+{
+ _keyData->keyEvent(e, true);
+}
+
+void MPInterface::keyReleaseEvent(QKeyEvent *e)
+{
+ _keyData->keyEvent(e, false);
+}
+
+/*****************************************************************************/
+/* Misc. functions */
+/*****************************************************************************/
+uint MPInterface::nbPlayers() const
+{
+ return internal->nbPlayers();
+}
+
+QString MPInterface::playerName(uint i) const
+{
+ Q_ASSERT(_server);
+ return internal->playerName(i);
+}
+
+QDataStream &MPInterface::readingStream(uint i) const
+{
+ Q_ASSERT(_server);
+ return internal->ioBuffer(i)->reading;
+}
+
+QDataStream &MPInterface::writingStream(uint i) const
+{
+ return internal->ioBuffer(i)->writing;
+}
+
+QDataStream &MPInterface::dataToClientsStream() const
+{
+ Q_ASSERT(_server);
+ return *internal->globalStream();
+}
+
+void MPInterface::immediateWrite()
+{
+ internal->writeData(_server);
+}
+
+void MPInterface::hostDisconnected(uint, const QString &msg)
+{
+ errorBox(msg, QString::null, this);
+
+ if ( !disconnected ) { // to avoid multiple calls
+ disconnected = TRUE;
+ // the zero timer is used to be outside the "internal" class
+ QTimer::singleShot(0, this, SLOT(singleHumanSlot()));
+ }
+}
+
+void MPInterface::singleHumanSlot()
+{
+ disconnected = FALSE;
+ singleHuman();
+}
+
+void MPInterface::paintEvent(QPaintEvent *e)
+{
+ QPainter p(this);
+ p.fillRect(e->rect(), darkGray);
+}
diff --git a/libksirtet/lib/mp_interface.h b/libksirtet/lib/mp_interface.h
new file mode 100644
index 00000000..ad925cba
--- /dev/null
+++ b/libksirtet/lib/mp_interface.h
@@ -0,0 +1,246 @@
+#ifndef MP_INTERFACE_H
+#define MP_INTERFACE_H
+
+#include <qwidget.h>
+#include <qvaluelist.h>
+#include <qptrlist.h>
+
+#include "mp_board.h"
+#include "mp_option.h"
+
+class QHBoxLayout;
+class Local;
+class ConnectionData;
+class RemoteHostData;
+class KeyData;
+class KeyCollection;
+class KKeyDialog;
+class KAction;
+
+struct ActionData {
+ const char *label, *name;
+ const char *slot, *slotRelease; // if slotRelease!=0
+ // : use keyPress/ReleaseEvent mecanism
+};
+
+/**
+ * This structure contains information about the game
+ * configuration.
+ */
+typedef struct {
+ /** The game version id used for identification (e.g. "4").
+ * You should change this id when the game is made incompatible
+ * with previous version. (changes in data for example).
+ */
+ const char *gameId;
+
+ /** Maximum number of local players. */
+ uint maxNbLocalPlayers;
+
+ /** Interval (in msec.) between data exchange. */
+ uint interval;
+
+ /** If there are built-in artificial intelligences that can play. */
+ bool AIAllowed;
+
+ /** Slot for player/AI additional configuration. These must be SLOTs which
+ * take an "int" as parameter. It must open a setting
+ * dialog for the corresponding local player/computer and save the
+ * new settings in the config file. It should probably create a group
+ * with the given number in its name.
+ * If such a pointer is set to 0 : it means there is no perticular
+ * setting.
+ */
+ const char *humanSettingSlot, *AISettingSlot;
+} MPGameInfo;
+
+/**
+ * The MPInterface class is useful for multiplayers game
+ * management. Each game is represented by a class you have inherited
+ * from the @ref MPBoard class.
+ *
+ * Multiplayers games can take place with several (humans or eventually
+ * AIs) players on the same computer (they use the same keyboard and have
+ * each a @ref MPBoard widget on the screen) or/and network players.
+ *
+ * This class is intended to do all the hard work of sending/receiving data
+ * between the players and to send the keyboard events to the right
+ * @ref MPBoard. So multiplayers game should be completely transparent
+ * from your point of view.
+ *
+ * Note : The data exchange is done in background with a timer calling at given
+ * intervals the read/write methods. Obviously this kind of things can be done
+ * easily with threads but I have no experience with thread programming
+ * and not all people have thread libraries and a thread-safe system.
+ */
+class MPInterface : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ /** Constructor which takes a MPGameInfo struct as parameter.
+ */
+ MPInterface(const MPGameInfo &gameInfo,
+ uint nbActions, const ActionData *data,
+ QWidget *parent = 0, const char *name = 0);
+ virtual ~MPInterface();
+
+ public slots:
+ /** Create a single player game for a human being.
+ * Call @ref stop if a game is already created. */
+ void singleHuman() { specialLocalGame(1, 0); }
+ /** Create a local game opposing two human beings.
+ * Call @ref stop if a game is already created. */
+ void humanVsHuman() { specialLocalGame(2, 0); }
+ /** Create a local game opposing a human with an AI.
+ * Call @ref stop if a game is already created. */
+ void humanVsComputer() { specialLocalGame(1, 1); }
+
+ /** Open a dialog to create a multiplayer game.
+ * Call @ref stop if a game is already created. */
+ void dialog();
+
+ public:
+ virtual void addKeys(KKeyDialog &);
+ void saveKeys();
+
+ /** Called when a new game is created. At this point
+ * the number of players is known. */
+ virtual void init() {}
+
+ /** Called just before a new game is created (called by
+ * singleHuman, humanVsHuman, humanVsComputer and dialog). */
+ virtual void stop() {}
+
+ /** Called when the start button of the netmeeting is pressed. */
+ virtual void start() {}
+
+ /**
+ * Set keys configuration for the given number of human players.
+ * The size of the array is the number of defined actions.
+ */
+ void setDefaultKeycodes(uint nbHumans, uint human, const int *keycodes);
+
+ /**
+ * @return the total number of players.
+ * (If called from client : return the number of local boards).
+ */
+ uint nbPlayers() const;
+
+ /**
+ * @return true if the interface is the server.
+ */
+ bool server() const { return _server; }
+
+ /** @return the player name.
+ Do not call from client !
+ */
+ QString playerName(uint i) const;
+
+ /**
+ * Create a new @ref MPBoard.
+ *
+ * @param i is the game index that goes from 0 to the number of
+ * local players : it can be used to retrieve configuration settings.
+ */
+ virtual MPBoard *newBoard(uint i) = 0;
+
+ /**
+ * This method must read data from each client with method
+ * @ref readingStream, do the needed treatement
+ * (for instance which players has lost, which data to be resent, ...) and
+ * then write the useful data to each client with method
+ * @ref writingStream.
+ *
+ * NB: this method is also called for single player games but
+ * you probably only want to check for game over condition (it depends
+ * on game implementation that you really return data to the board).
+ */
+ virtual void treatData() = 0;
+
+ /** @return the reading stream for board #i.
+ * Do not call from client !
+ */
+ QDataStream &readingStream(uint i) const;
+
+ /** @return the writing stream for board #i.
+ */
+ QDataStream &writingStream(uint i) const;
+
+ /**
+ * Read data sent from server to clients "MultiplayersInterface"
+ * (this data is not addressed to boards).
+ * These are meta data that are not directly used in game.
+ * It can be used to display "game over" infos for all
+ * local games.
+ * NB: the use of this method is optional.
+ */
+ virtual void dataFromServer(QDataStream &) {}
+
+ /** Used by the server to write meta data to clients.
+ * NB: the use of this method is optional.
+ * Do not call from client !
+ */
+ QDataStream &dataToClientsStream() const;
+
+ /** Write immediately data to clients and local boards.
+ * It is unlike the normal exchange which is driven
+ * by the timer of the server. Be aware of possible
+ * interactions.
+ */
+ void immediateWrite();
+
+ /**
+ * This method should be overload if an option widget is used in the
+ * the "netmeeting" dialog). By default a
+ * null pointer is returned and so no option widget is shown.
+ * The option widget must be inherited from the @ref MPOptionWidget class.
+ */
+ virtual MPOptionWidget *newOptionWidget() const { return 0; }
+
+ /** Called when a network error occurred or when a host gets disconnected.
+ * The default implementation displays a message and calls singleHumans()
+ * ie it stops the current game. By overloading this method, it is
+ * possible to continue the game at this point with the remaining players.
+ */
+ virtual void hostDisconnected(uint i, const QString &msg);
+
+ protected:
+ void paintEvent(QPaintEvent *);
+ void keyPressEvent(QKeyEvent *);
+ void keyReleaseEvent(QKeyEvent *);
+
+ private slots:
+ void enableKeys(bool enable);
+ void singleHumanSlot();
+
+ public:
+ class Data {
+ public:
+ Data() {}
+ MPBoard *ptr;
+ int humanIndex;
+ QString name;
+ };
+
+ private:
+ Local *internal;
+ const MPGameInfo gameInfo;
+ QValueList<Data> boards;
+ uint nbLocalHumans;
+ QHBoxLayout *hbl;
+ bool _server, disconnected;
+
+ KeyData *_keyData;
+ QMemArray<KeyCollection *> _keyCol;
+
+ void createServerGame(const QPtrList<RemoteHostData> &);
+ void createClientGame(const RemoteHostData &);
+ void createLocalGame(const ConnectionData &);
+ void specialLocalGame(uint nbHumans, uint nbComputers);
+
+ void clear();
+ void initKeys(uint nbHumans);
+};
+
+#endif // MP_INTERFACE_H
diff --git a/libksirtet/lib/mp_option.h b/libksirtet/lib/mp_option.h
new file mode 100644
index 00000000..94c59107
--- /dev/null
+++ b/libksirtet/lib/mp_option.h
@@ -0,0 +1,74 @@
+#ifndef MP_OPTION_H
+#define MP_OPTION_H
+
+#include <qwidget.h>
+
+/**
+ * The OptionWidget is a base widget for the option widget in the
+ * "netmeeting" dialog. This option widget is optional (!).
+ *
+ * For example you will have :
+ * <PRE>
+ * class MyOptionWidget : public OptionWidget { ... };
+ * class MyMultiPlayerInterface : public MultiPlayerInterface { ... };
+ *
+ * OptionWidget *MyMultiPlayerInterface::newOptionWidget(bool server) const
+ * { return new MyOptionWidget(server); };
+ * </PRE>
+ *
+ * The option widget must have two different behaviours for server and
+ * clients. The server is able to change the options but the clients are only
+ * able to see them. The library will catch the @ref #changed signal from
+ * the option widget and will send the changes to the clients. It uses the
+ * @ref #dataOut to obtain the data from the
+ * server option dialog and then on the client side, it sets the new data with
+ * the method @ref #dataIn.
+ * You must implement this three methods to have useful option widgets and be
+ * careful to emit the signal @ref #changed whenever the server widget is
+ * changed. In addition you'll need to implement the method @ref #saveData
+ * to save the configuration in the config file ; so that it will be available
+ * for the initialisation of @ref LocalBoard.
+ * It seems a good idea that the widget have the same layout
+ * on both (server and client) sides but with the inner widgets all disabled
+ * on the client side.
+ */
+class MPOptionWidget : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ MPOptionWidget(bool Server, QWidget *parent = 0, const char *name = 0)
+ : QWidget(parent, name), server(Server) {}
+ virtual ~MPOptionWidget() {}
+
+ bool isServer() const { return server; }
+
+ /**
+ * This method is used on the client side to set the data coming from
+ * the server widget.
+ */
+ virtual void dataIn(QDataStream &s) = 0;
+
+ /** This method is used on the server side to get the data. */
+ virtual void dataOut(QDataStream &s) const = 0;
+
+ /**
+ * When the game will begin (ie when the "netmeeting" is over and the
+ * server has press the "start game" button) this method will be called
+ * (for clients and server). It must save the settings in the config
+ * file.
+ */
+ virtual void saveData() = 0;
+
+ signals:
+ /**
+ * This signal must be called each time options are changed
+ * (by the server).
+ */
+ void changed();
+
+ private:
+ bool server;
+};
+
+#endif // MP_OPTION_H
diff --git a/libksirtet/lib/mp_simple_board.cpp b/libksirtet/lib/mp_simple_board.cpp
new file mode 100644
index 00000000..f032c488
--- /dev/null
+++ b/libksirtet/lib/mp_simple_board.cpp
@@ -0,0 +1,84 @@
+#include "mp_simple_board.h"
+#include "mp_simple_board.moc"
+
+
+void MPSimpleBoard::init(bool AI, bool multiplayers, bool server, bool first,
+ const QString &name)
+{
+ state = BS_Init;
+ _init(AI, multiplayers, server, first, name);
+}
+
+void MPSimpleBoard::dataIn(QDataStream &s)
+{
+ if ( s.atEnd() ) return; // no data
+
+ IO_Flag f;
+ s >> f;
+ switch ( f.value() ) {
+ case IO_Flag::Init: initFlag(s); break;
+ case IO_Flag::Play: playFlag(s); break;
+ case IO_Flag::Pause: pauseFlag(); break;
+ case IO_Flag::GameOver: gameOverFlag(); break;
+ case IO_Flag::Stop: stopFlag(); break;
+ }
+}
+
+void MPSimpleBoard::initFlag(QDataStream &s)
+{
+ state = BS_Play;
+ emit enableKeys(true);
+ _initFlag(s);
+}
+
+void MPSimpleBoard::playFlag(QDataStream &s)
+{
+ Q_ASSERT( state==BS_Play );
+ _playFlag(s);
+}
+
+void MPSimpleBoard::pauseFlag()
+{
+ Q_ASSERT( state==BS_Play || state==BS_Pause );
+ bool p = ( state==BS_Pause );
+ state = (p ? BS_Play : BS_Pause);
+ emit enableKeys(p);
+ _pauseFlag(!p);
+}
+
+void MPSimpleBoard::gameOverFlag()
+{
+ Q_ASSERT( BS_Play );
+ _stop(TRUE);
+ state = BS_Stop;
+}
+
+void MPSimpleBoard::stopFlag()
+{
+ _stop(FALSE);
+ state = BS_Standby;
+}
+
+void MPSimpleBoard::_stop(bool gameover)
+{
+ if ( state==BS_Pause ) _pauseFlag(FALSE);
+ emit enableKeys(false);
+ _stopFlag(gameover);
+}
+
+void MPSimpleBoard::dataOut(QDataStream &s)
+{
+ switch (state) {
+ case BS_Init:
+ _initDataOut(s);
+ state = BS_Standby;
+ return;
+ case BS_Play: _dataOut(s); return;
+ case BS_Stop:
+ _gameOverDataOut(s);
+ state = BS_Standby;
+ return;
+ case BS_Pause: return;
+ case BS_Standby: return;
+ }
+}
diff --git a/libksirtet/lib/mp_simple_board.h b/libksirtet/lib/mp_simple_board.h
new file mode 100644
index 00000000..2131feb2
--- /dev/null
+++ b/libksirtet/lib/mp_simple_board.h
@@ -0,0 +1,45 @@
+#ifndef MP_SIMPLE_BOARD_H
+#define MP_SIMPLE_BOARD_H
+
+#include "mp_board.h"
+#include "mp_simple_types.h"
+
+#include <kdemacros.h>
+
+class KDE_EXPORT MPSimpleBoard : public MPBoard
+{
+ Q_OBJECT
+
+ public:
+ MPSimpleBoard(QWidget *parent = 0, const char *name = 0)
+ : MPBoard(parent, name) {}
+ virtual ~MPSimpleBoard() {}
+
+ void init(bool AI, bool multiplayers, bool server, bool first,
+ const QString &name);
+ void dataOut(QDataStream &s);
+ void dataIn(QDataStream &s);
+
+ protected:
+ virtual void _init(bool AI, bool multiplayers, bool server, bool first,
+ const QString &name) = 0;
+ virtual void _initFlag(QDataStream &s) = 0;
+ virtual void _playFlag(QDataStream &s) = 0;
+ virtual void _pauseFlag(bool pause) = 0;
+ virtual void _stopFlag(bool gameover) = 0;
+ virtual void _dataOut(QDataStream &s) = 0;
+ virtual void _gameOverDataOut(QDataStream &s) = 0;
+ virtual void _initDataOut(QDataStream &s) = 0;
+
+ private:
+ BoardState state;
+
+ void initFlag(QDataStream &s);
+ void playFlag(QDataStream &s);
+ void pauseFlag();
+ void stopFlag();
+ void gameOverFlag();
+ void _stop(bool button);
+};
+
+#endif // MP_SIMPLE_BOARD_H
diff --git a/libksirtet/lib/mp_simple_interface.cpp b/libksirtet/lib/mp_simple_interface.cpp
new file mode 100644
index 00000000..e75243a6
--- /dev/null
+++ b/libksirtet/lib/mp_simple_interface.cpp
@@ -0,0 +1,152 @@
+#include "mp_simple_interface.h"
+#include "mp_simple_interface.moc"
+
+#include <kmessagebox.h>
+#include <qtimer.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kmainwindow.h>
+
+
+#define PAUSE_ACTION \
+ ((KToggleAction *)((KMainWindow *)topLevelWidget())->action("game_pause"))
+
+MPSimpleInterface::MPSimpleInterface(const MPGameInfo &gi,
+ uint nbActions, const ActionData *data,
+ QWidget *parent, const char *name)
+: MPInterface(gi, nbActions, data, parent, name), state(SS_Standby)
+{}
+
+void MPSimpleInterface::init()
+{
+ if ( server() ) {
+ state = SS_Standby;
+ first_init = TRUE;
+ }
+ _init();
+}
+
+void MPSimpleInterface::start()
+{
+ // WARNING : multiple calls can happen here (because button
+ // hiding is delayed)
+ state = SS_Init;
+}
+
+void MPSimpleInterface::stop()
+{
+ state = SS_Standby;
+ SC_Flag f1(SC_Flag::Stop);
+ if ( server() ) dataToClientsStream() << f1;
+ IO_Flag f2(IO_Flag::Stop);
+ for (uint i=0; i<nbPlayers(); i++) writingStream(i) << f2;
+ immediateWrite();
+}
+
+void MPSimpleInterface::addKeys(KKeyDialog &d)
+{
+ if ( !isPaused() ) pause();
+ MPInterface::addKeys(d);
+}
+
+void MPSimpleInterface::pause()
+{
+ // WARNING : multiple calls can happen here (because button
+ // hiding is delayed)
+ switch (state) {
+ case SS_Play:
+ state = SS_PauseAsked;
+ break;
+ case SS_Pause:
+ state = SS_UnpauseAsked;
+ break;
+ default: break;
+ }
+}
+
+void MPSimpleInterface::dataFromServer(QDataStream &s)
+{
+ if ( s.atEnd() ) return; // no data
+
+ SC_Flag scf;
+ s >> scf;
+ switch (scf.value()) {
+ case SC_Flag::Stop:
+ KMessageBox::information(this, i18n("Server has left game!"));
+ QTimer::singleShot(0, this, SLOT(singleHuman()));
+ return;
+ case SC_Flag::GameOver:
+ _readGameOverData(s);
+ _showGameOverData();
+ return;
+ }
+}
+
+void MPSimpleInterface::treatData()
+{
+ switch (state) {
+ case SS_Init: treatInit(); break;
+ case SS_Play: treatPlay(); break;
+ case SS_Pause: break;
+ case SS_Stop: treatStop(); break;
+ case SS_Standby: break;
+ case SS_PauseAsked: treatPause(TRUE); break;
+ case SS_UnpauseAsked: treatPause(FALSE); break;
+ }
+}
+
+void MPSimpleInterface::treatInit()
+{
+ state = SS_Play;
+
+ if (first_init) {
+ _firstInit();
+ first_init = FALSE;
+ }
+
+ IO_Flag f(IO_Flag::Init);
+ for (uint i=0; i<nbPlayers(); i++) writingStream(i) << f;
+ _treatInit();
+}
+
+void MPSimpleInterface::treatPlay()
+{
+ PAUSE_ACTION->setEnabled(true);
+ PAUSE_ACTION->setChecked(false);
+
+ bool end = _readPlayData();
+ if (end) {
+ state = SS_Stop;
+ IO_Flag f(IO_Flag::GameOver);
+ for (uint i=0; i<nbPlayers(); i++) writingStream(i) << f;
+ return;
+ }
+ if ( nbPlayers()==1 ) return; // no need to send data for singleplayer game
+ IO_Flag f(IO_Flag::Play);
+ for(uint i=0; i<nbPlayers(); i++) writingStream(i) << f;
+ _sendPlayData();
+}
+
+void MPSimpleInterface::treatPause(bool pause)
+{
+ state = (pause ? SS_Pause : SS_Play);
+ IO_Flag f(IO_Flag::Pause);
+ for (uint i=0; i<nbPlayers(); i++) writingStream(i) << f;
+
+ PAUSE_ACTION->setChecked(pause);
+}
+
+void MPSimpleInterface::treatStop()
+{
+ state = SS_Standby;
+
+ // read game over data + send them to all clients
+ QDataStream &s = dataToClientsStream();
+ SC_Flag f(SC_Flag::GameOver);
+ s << f;
+ _sendGameOverData(s);
+ _showGameOverData();
+
+ PAUSE_ACTION->setEnabled(false);
+ PAUSE_ACTION->setChecked(false);
+}
diff --git a/libksirtet/lib/mp_simple_interface.h b/libksirtet/lib/mp_simple_interface.h
new file mode 100644
index 00000000..84e6f444
--- /dev/null
+++ b/libksirtet/lib/mp_simple_interface.h
@@ -0,0 +1,48 @@
+#ifndef MP_SIMPLE_INTERFACE_H
+#define MP_SIMPLE_INTERFACE_H
+
+#include "mp_interface.h"
+#include "mp_simple_types.h"
+
+class MPSimpleInterface : public MPInterface
+{
+ Q_OBJECT
+
+ public:
+ MPSimpleInterface(const MPGameInfo &gi,
+ uint nbActions, const ActionData *data,
+ QWidget *parent = 0, const char *name = 0);
+
+ bool isPaused() const { return state==SS_Pause; }
+
+ public slots:
+ void start();
+ void pause();
+ void addKeys(KKeyDialog &);
+
+ protected:
+ virtual void _init() = 0;
+ virtual void _readGameOverData(QDataStream &s) = 0;
+ virtual void _sendGameOverData(QDataStream &s) = 0;
+ virtual void _showGameOverData() = 0;
+ virtual void _firstInit() = 0;
+ virtual void _treatInit() = 0;
+ virtual bool _readPlayData() = 0;
+ virtual void _sendPlayData() = 0;
+
+ private:
+ ServerState state;
+ bool first_init;
+
+ void treatData();
+ void treatInit();
+ void treatPlay();
+ void treatPause(bool pause);
+ void treatStop();
+
+ void init();
+ void stop();
+ void dataFromServer(QDataStream &);
+};
+
+#endif // MP_SIMPLE_INTERFACE_H
diff --git a/libksirtet/lib/mp_simple_types.cpp b/libksirtet/lib/mp_simple_types.cpp
new file mode 100644
index 00000000..a470ce7c
--- /dev/null
+++ b/libksirtet/lib/mp_simple_types.cpp
@@ -0,0 +1,6 @@
+#include "mp_simple_types.h"
+
+QDataStream &operator <<(QDataStream &s, const EnumClass &ec)
+ { s << (Q_UINT8)ec.f; return s; }
+QDataStream &operator >>(QDataStream &s, EnumClass &ec)
+ { Q_UINT8 t; s >> t; ec.f = (int)t; return s; }
diff --git a/libksirtet/lib/mp_simple_types.h b/libksirtet/lib/mp_simple_types.h
new file mode 100644
index 00000000..2fd0c289
--- /dev/null
+++ b/libksirtet/lib/mp_simple_types.h
@@ -0,0 +1,36 @@
+#ifndef WF_TYPES_H
+#define WF_TYPES_H
+
+#include <qdatastream.h>
+
+class EnumClass
+{
+ public:
+ EnumClass(int _f) : f(_f) {}
+ int f;
+};
+QDataStream &operator <<(QDataStream &s, const EnumClass &f);
+QDataStream &operator >>(QDataStream &s, EnumClass &f);
+
+class IO_Flag : public EnumClass
+{
+ public:
+ enum IOF { Init = 0, Play, Pause, Stop, GameOver };
+ IO_Flag(IOF f = Init) : EnumClass(f) {}
+ IOF value() const { return (IOF)f; }
+};
+
+enum ServerState { SS_Init, SS_Play, SS_Pause, SS_Stop, SS_Standby,
+ SS_PauseAsked, SS_UnpauseAsked };
+
+class SC_Flag : public EnumClass
+{
+ public:
+ enum SC { Stop = 0, GameOver };
+ SC_Flag(SC f = Stop) : EnumClass(f) {}
+ SC value() const { return (SC)f; }
+};
+
+enum BoardState { BS_Init, BS_Play, BS_Pause, BS_Stop, BS_Standby };
+
+#endif
diff --git a/libksirtet/lib/pline.cpp b/libksirtet/lib/pline.cpp
new file mode 100644
index 00000000..41faf6ac
--- /dev/null
+++ b/libksirtet/lib/pline.cpp
@@ -0,0 +1,147 @@
+#include "pline.h"
+#include "pline.moc"
+
+#include <qfont.h>
+#include <qpushbutton.h>
+#include <klocale.h>
+#include "defines.h"
+
+#define THIN_BORDER 4
+
+MeetingLine::MeetingLine(bool isOwner, bool serverIsReader, bool serverLine,
+ QWidget *parent, const char *name)
+: QFrame(parent, name)
+{
+ setFrameStyle(Panel | (serverLine ? Raised : Plain));
+
+ // Top layout
+ hbl = new QHBoxLayout(this, THIN_BORDER + frameWidth());
+
+ /* TriCheckBox */
+ tcb = new MeetingCheckBox(MeetingCheckBox::Ready, isOwner, serverIsReader,
+ this);
+ if ( !XOR(isOwner, serverIsReader) ) tcb->setEnabled(FALSE);
+ else connect(tcb, SIGNAL(changed(int)), SLOT(_typeChanged(int)));
+ hbl->addWidget(tcb);
+
+ /* Name */
+ lname = new QLabel(" ", this);
+ lname->setAlignment(AlignCenter);
+ lname->setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ lname->setLineWidth(2);
+ lname->setMidLineWidth(3);
+ QFont f = lname->font();
+ f.setBold(TRUE);
+ lname->setFont(f);
+ lname->setFixedSize(lname->fontMetrics().maxWidth()*NAME_MAX_LENGTH,
+ lname->sizeHint().height());
+ hbl->addWidget(lname);
+ hbl->addStretch(1);
+
+ // Nb humans
+ labH = new QLabel(this);
+ hbl->addWidget(labH);
+
+ // Nb AIs
+ labAI = new QLabel(this);
+ hbl->addWidget(labAI);
+
+ // talker
+ qle = new QLineEdit(this);
+ qle->setMaxLength(TALKER_MAX_LENGTH);
+ qle->setFont( QFont("fixed", 12, QFont::Bold) );
+ qle->setFixedSize(qle->fontMetrics().maxWidth()*TALKER_MAX_LENGTH,
+ qle->sizeHint().height());
+ connect(qle, SIGNAL(textChanged(const QString &)),
+ SLOT(_textChanged(const QString &)));
+ qle->setEnabled(isOwner);
+ hbl->addWidget(qle);
+}
+
+void MeetingLine::setData(const ExtData &ed)
+{
+ bds = ed.bds;
+ uint nbh = 0, nba = 0;
+ for (uint i=0; i<bds.count(); i++) {
+ if ( bds[i].type==PlayerComboBox::Human ) nbh++;
+ else if ( bds[i].type==PlayerComboBox::AI ) nba++;
+ }
+ labH->setText(i18n("Hu=%1").arg(nbh));
+ labAI->setText(i18n("AI=%1").arg(nba));
+ lname->setText(bds[0].name);
+ setType(ed.type);
+ setText(ed.text);
+}
+
+void MeetingLine::data(ExtData &ed) const
+{
+ ed.bds = bds;
+ ed.type = tcb->type();
+ ed.text = text();
+}
+
+/*****************************************************************************/
+PlayerLine::PlayerLine(PlayerComboBox::Type type, const QString &txt,
+ bool humanSetting, bool AISetting,
+ bool canBeEmpty, bool acceptAI,
+ QWidget *parent, const char *name)
+: QFrame(parent, name), hs(humanSetting), as(AISetting)
+{
+ setFrameStyle(Panel | Raised);
+
+ // Top layout
+ QHBoxLayout *hbl;
+ hbl = new QHBoxLayout(this, THIN_BORDER + frameWidth());
+
+ /* CheckBox */
+ pcb = new PlayerComboBox(type, canBeEmpty, acceptAI, this);
+ connect(pcb, SIGNAL(changed(int)), SLOT(typeChangedSlot(int)));
+ hbl->addWidget(pcb);
+
+ /* Name */
+ edit = new QLineEdit(txt, this);
+ edit->setMaxLength(NAME_MAX_LENGTH);
+ edit->setFixedSize(edit->fontMetrics().maxWidth()*(NAME_MAX_LENGTH+2),
+ edit->sizeHint().height());
+ hbl->addWidget(edit);
+
+ /* settings button */
+ setting = new QPushButton(i18n("Settings"), this);
+ connect(setting, SIGNAL(clicked()), SLOT(setSlot()));
+ hbl->addWidget(setting);
+
+ typeChangedSlot(type);
+}
+
+void PlayerLine::typeChangedSlot(int t)
+{
+ edit->setEnabled(type()!=PlayerComboBox::None);
+ setting->setEnabled( (type()==PlayerComboBox::Human && hs)
+ || (type()==PlayerComboBox::AI && as) );
+ emit typeChanged(t);
+}
+
+void PlayerLine::setSlot()
+{
+ if ( type()==PlayerComboBox::Human ) emit setHuman();
+ else emit setAI();
+}
+
+/*****************************************************************************/
+GWidgetList::GWidgetList(uint interval, QWidget *parent, const char * name)
+ : QWidget(parent, name), vbl(this, interval)
+{
+ widgets.setAutoDelete(TRUE);
+}
+
+void GWidgetList::append(QWidget *wi)
+{
+ vbl.addWidget(wi);
+ wi->show();
+ widgets.append(wi);
+}
+
+void GWidgetList::remove(uint i)
+{
+ widgets.remove(i);
+}
diff --git a/libksirtet/lib/pline.h b/libksirtet/lib/pline.h
new file mode 100644
index 00000000..2defd10a
--- /dev/null
+++ b/libksirtet/lib/pline.h
@@ -0,0 +1,112 @@
+#ifndef PLINE_H
+#define PLINE_H
+
+#include <qframe.h>
+#include <qscrollbar.h>
+#include <qlineedit.h>
+#include <qlabel.h>
+#include <qptrlist.h>
+#include <qlayout.h>
+
+#include "types.h"
+
+class QPushButton;
+
+/** Internal class : display a "player line" in netmeeting. */
+class MeetingLine : public QFrame
+{
+ Q_OBJECT
+
+ public:
+ MeetingLine(bool isOwner, bool readerIsServer, bool serverLine,
+ QWidget *parent, const char *name = 0);
+
+ MeetingCheckBox::Type type() const { return tcb->type(); }
+ void setType(MeetingCheckBox::Type type) { tcb->setType(type); }
+ void setText(const QString &text) { qle->setText(text); }
+
+ void setData(const ExtData &ed);
+ void data(ExtData &ed) const;
+ QString text() const { return qle->text(); }
+
+ signals:
+ void typeChanged(MeetingCheckBox::Type);
+ void textChanged(const QString &);
+
+ private slots:
+ void _typeChanged(int t)
+ { emit typeChanged((MeetingCheckBox::Type)t); }
+ void _textChanged(const QString &text) { emit textChanged(text); }
+
+ protected:
+ QHBoxLayout *hbl;
+
+ private:
+ MeetingCheckBox *tcb;
+ QLabel *lname, *labH, *labAI;
+ QValueList<BoardData> bds;
+ QLineEdit *qle;
+};
+
+class PlayerLine : public QFrame
+{
+ Q_OBJECT
+
+ public:
+ PlayerLine(PlayerComboBox::Type type, const QString &txt,
+ bool humanSetting, bool AISetting,
+ bool canBeEmpty, bool acceptAI,
+ QWidget *parent = 0, const char *name = 0);
+
+ PlayerComboBox::Type type() const { return pcb->type(); }
+ QString name() const { return edit->text(); }
+
+ signals:
+ void setHuman();
+ void setAI();
+ void typeChanged(int);
+
+ private slots:
+ void setSlot();
+ void typeChangedSlot(int);
+
+ private:
+ PlayerComboBox *pcb;
+ QLineEdit *edit;
+ QPushButton *setting;
+ bool hs, as;
+};
+
+/** Internal class : scrolable list of widgets. */
+class GWidgetList : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ GWidgetList(uint interval, QWidget *parent = 0, const char * name = 0);
+
+ void remove(uint i);
+ uint size() const { return widgets.count(); }
+
+ protected:
+ /** The widget must be created with this widget as parent. */
+ void append(QWidget *);
+ QWidget *widget(uint i) { return widgets.at(i); }
+
+ private:
+ QPtrList<QWidget> widgets;
+ QVBoxLayout vbl;
+};
+
+template <class Type>
+class WidgetList : public GWidgetList
+{
+ public:
+ WidgetList(uint interval, QWidget *parent=0, const char *name=0)
+ : GWidgetList(interval, parent, name) {}
+
+ void append(Type *w) { GWidgetList::append(w); }
+ Type *widget(uint i) { return (Type *)GWidgetList::widget(i); }
+};
+
+#endif // PLINE_H
diff --git a/libksirtet/lib/smanager.cpp b/libksirtet/lib/smanager.cpp
new file mode 100644
index 00000000..f6588546
--- /dev/null
+++ b/libksirtet/lib/smanager.cpp
@@ -0,0 +1,115 @@
+#include "smanager.h"
+
+#include <config.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <sys/utsname.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <strings.h>
+
+
+SocketManager::SocketManager()
+{
+ max_fd = 0;
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ nbWriteable = 0;
+}
+
+SocketManager::~SocketManager()
+{
+ clean();
+}
+
+void SocketManager::clean()
+{
+ for(uint i=0; i<sockets.size(); i++) delete sockets[i];
+ sockets.resize(0);
+}
+
+int SocketManager::find(int fd)
+{
+ for(uint i=0; i<sockets.size(); i++)
+ if ( sockets[i]->fd()==fd ) return i;
+ return -1;
+}
+
+uint SocketManager::append(Socket *socket, SocketProperty sp)
+{
+ uint s = sockets.size();
+ sockets.resize(s+1);
+ sockets[s] = socket;
+
+ max_fd = QMAX(max_fd, socket->fd());
+
+ if ( sp==ReadWrite || sp==ReadOnly ) FD_SET(socket->fd(), &read_set);
+ if ( sp==ReadWrite || sp==WriteOnly ) {
+ nbWriteable++;
+ FD_SET(socket->fd(), &write_set);
+ }
+
+ return s;
+}
+
+void SocketManager::remove(uint i, bool del)
+{
+ Socket *so = sockets[i];
+
+ uint s = sockets.size()-1;
+ for(uint j=i; j<s; j++) sockets[j] = sockets[j+1];
+ sockets.resize(s);
+
+ max_fd = 0;
+ for(uint j=0; j<s; j++)
+ max_fd = QMAX(max_fd, sockets[j]->fd());
+
+ int fd = so->fd();
+ if ( FD_ISSET(fd, &read_set) ) FD_CLR(fd, &read_set);
+ if ( FD_ISSET(fd, &write_set) ) {
+ nbWriteable--;
+ FD_CLR(fd, &write_set);
+ }
+
+ if (del) delete so;
+}
+
+bool SocketManager::canWriteAll(uint sec, uint usec)
+{
+ write_tmp = write_set;
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ return ( select(max_fd+1, 0, &write_tmp, 0, &tv)==(int)nbWriteable );
+}
+
+bool SocketManager::canWrite(uint i, uint sec, uint usec)
+{
+ int fd = sockets[i]->fd();
+ FD_ZERO(&write_tmp);
+ FD_SET(fd, &write_tmp);
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ return ( select(fd+1, 0, &write_tmp, 0, &tv)==1 );
+}
+
+bool SocketManager::checkPendingData(uint sec, uint usec)
+{
+ read_tmp = read_set;
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ return ( select(max_fd+1, &read_tmp, 0, 0, &tv)!=-1 );
+}
+
+bool SocketManager::dataPending(uint i)
+{
+ int fd = sockets[i]->fd();
+ return FD_ISSET(fd, &read_tmp);
+}
+
+bool SocketManager::writeCommon(uint i)
+{
+ return sockets[i]->write(writing.buffer());
+}
diff --git a/libksirtet/lib/smanager.h b/libksirtet/lib/smanager.h
new file mode 100644
index 00000000..a831b702
--- /dev/null
+++ b/libksirtet/lib/smanager.h
@@ -0,0 +1,88 @@
+#ifndef SMANAGER_H
+#define SMANAGER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/types.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include <sys/time.h>
+
+#include "socket.h"
+
+/**
+ * The SocketManager class is useful to manage (rw, ro, wo) sockets.
+ *
+ * You must add the sockets you want to manage to this class before other
+ * operations. The sockets are stored in an array and other methods reference
+ * the sockets by their index in that array.
+ */
+class SocketManager
+{
+ public:
+ SocketManager();
+
+ /** Be aware that unremoved sockets will be closed there. */
+ ~SocketManager();
+
+ /** Remove all sockets and close them. */
+ void clean();
+
+ enum SocketProperty { ReadOnly, WriteOnly, ReadWrite };
+
+ /** @return the number of sockets. */
+ uint size() const { return sockets.size(); }
+
+ const Socket *operator[](uint i) const { return sockets[i]; }
+ Socket *operator [](uint i) { return sockets[i]; }
+
+ /** @return the index of the socket (-1 if not present). */
+ int find(int fd);
+
+ /**
+ * Append a socket at the end of the array of sockets.
+ * @param sp determines if the socket will be used for ReadWrite,
+ * ReadOnly or WriteOnly operations.
+ * @return the index of the socket.
+ */
+ uint append(Socket *, SocketProperty sp = ReadWrite);
+
+ /**
+ * Remove the socket indexed <I>i</I>. Note that the following sockets in
+ * the array will have their index decremented by one.
+ * @param deleteSocket if true, the socket is deleted
+ */
+ void remove(uint i, bool deleteSocket);
+
+ /** @return TRUE if it is possible to write to all the writeable sockets. */
+ bool canWriteAll(uint sec = 0, uint usec = 0);
+
+ /** @return TRUE if it is possible to write to the specified socket. */
+ bool canWrite(uint i, uint sec = 0, uint usec = 0);
+
+ Stream &commonWritingStream() { return writing; }
+ bool writeCommon(uint i); // do not clear stream
+
+ /**
+ * Check if there are pending data on at least one of the readeable
+ * socket.
+ */
+ bool checkPendingData(uint sec = 0, uint usec = 0);
+
+ bool dataPending(uint i);
+
+ private:
+ QMemArray<Socket *> sockets;
+
+ fd_set read_set, write_set, read_tmp, write_tmp;
+ struct timeval tv;
+ int max_fd;
+ uint nbWriteable;
+
+ WritingStream writing;
+};
+
+#endif // SMANAGER_H
diff --git a/libksirtet/lib/socket.cpp b/libksirtet/lib/socket.cpp
new file mode 100644
index 00000000..9ef3ba3c
--- /dev/null
+++ b/libksirtet/lib/socket.cpp
@@ -0,0 +1,80 @@
+#include "socket.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <ctype.h>
+#include <netdb.h>
+#include <sys/utsname.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h> // for FIONREAD
+#endif
+
+Socket::Socket(KExtendedSocket *s, bool createNotifier,
+ QObject *parent, const char *name)
+: _socket(s), _notifier(0)
+{
+ Q_ASSERT(s);
+ if (createNotifier) {
+ _notifier = new QSocketNotifier(s->fd(), QSocketNotifier::Read,
+ parent, name);
+ _notifier->setEnabled(FALSE);
+ }
+}
+
+Socket::~Socket()
+{
+ delete _notifier;
+ delete _socket;
+}
+
+bool Socket::write(const QByteArray &a)
+{
+ return ( _socket->writeBlock(a.data(), a.size())==(int)a.size() );
+}
+
+bool Socket::write()
+{
+ bool res = write(writing.buffer());
+ writing.clear();
+ return res;
+}
+
+int Socket::pendingData() const
+{
+ int size = 0;
+ if ( ioctl(_socket->fd(), FIONREAD, (char *)&size)<0 ) return -1;
+ return size;
+}
+
+int Socket::read()
+{
+ reading.clearRead();
+
+ int size = pendingData();
+ if ( size==-1 ) return -1;
+
+ reading.device()->close();
+ int dec = reading.size();
+ reading.buffer().resize(dec + size);
+ size = _socket->readBlock(reading.buffer().data() + dec, size);
+ if ( size==-1 ) reading.buffer().resize(dec);
+ reading.device()->open(IO_ReadOnly);
+
+ return size;
+}
+
+int Socket::accept(KExtendedSocket *&s)
+{
+ return _socket->accept(s);
+}
diff --git a/libksirtet/lib/socket.h b/libksirtet/lib/socket.h
new file mode 100644
index 00000000..a2f47a63
--- /dev/null
+++ b/libksirtet/lib/socket.h
@@ -0,0 +1,65 @@
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include <qsocketnotifier.h>
+
+#include <kextsock.h>
+
+#include "types.h"
+
+
+class Socket
+{
+ public:
+ Socket(KExtendedSocket *, bool createNotifier = FALSE,
+ QObject *parent = 0, const char *name = 0);
+
+ /** close the socket */
+ ~Socket();
+
+ int fd() const { return _socket->fd(); }
+
+ /**
+ * Accept a new socket.
+ */
+ int accept(KExtendedSocket *&);
+
+ /**
+ * @return the socket notifier associated with the socket
+ * (0 if none).
+ */
+ QSocketNotifier *notifier() const { return _notifier; }
+
+ /**
+ * Write data contained in the writing stream to the socket.
+ * It clears the stream.
+ * @return TRUE if all was written without error.
+ */
+ bool write();
+ bool write(const QByteArray &a);
+
+ /** @return the QDataStream for writing. */
+ WritingStream &writingStream() { return writing; }
+
+ /** @return the size of pending data. */
+ int pendingData() const;
+
+ /**
+ * Read data from socket and append them to reading stream for the specified socket.
+ * The portion of the stream that has been read is cleared.
+ * @return the read size or -1 on error
+ */
+ int read();
+
+ /** @return the reading stream. */
+ ReadingStream &readingStream() { return reading; }
+
+ private:
+ KExtendedSocket *_socket;
+ QSocketNotifier *_notifier;
+
+ WritingStream writing;
+ ReadingStream reading;
+};
+
+#endif // SOCKET_H
diff --git a/libksirtet/lib/types.cpp b/libksirtet/lib/types.cpp
new file mode 100644
index 00000000..557fffff
--- /dev/null
+++ b/libksirtet/lib/types.cpp
@@ -0,0 +1,254 @@
+#include "types.h"
+
+#include <klocale.h>
+#include "version.h"
+
+cId::cId(const QString &_gameName, const QString &_gameId)
+: libId(MULTI_ID), gameName(_gameName), gameId(_gameId)
+{}
+
+void cId::check(const cId &id)
+{
+ if ( libId!=id.libId ) state = LibIdClash;
+ else if ( gameName!=id.gameName ) state = GameNameClash;
+ else if ( gameId!=id.gameId ) state = GameIdClash;
+ else state = Accepted;
+}
+
+QString cId::errorMessage(const cId &id) const
+{
+ QString str = i18n("\nServer: \"%1\"\nClient: \"%2\"");
+
+ switch (state) {
+ case Accepted: return QString::null;
+ case LibIdClash:
+ return i18n("The MultiPlayer library of the server is incompatible")
+ + str.arg(libId).arg(id.libId);
+ case GameNameClash:
+ return i18n("Trying to connect a server for another game type")
+ + str.arg(gameName).arg(id.gameName);
+ case GameIdClash:
+ return i18n("The server game version is incompatible")
+ + str.arg(gameId).arg(id.gameId);
+ }
+ Q_ASSERT(0);
+ return QString::null;
+}
+
+QDataStream &operator << (QDataStream &s, const cId &id)
+{
+ s << id.libId << id.gameName << id.gameId << (Q_UINT8)id.state;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, cId &id)
+{
+ Q_UINT8 state;
+ s >> id.libId >> id.gameName >> id.gameId >> state;
+ id.state = (cId::State)state;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const MeetingMsgFlag &f)
+{
+ s << (Q_UINT8)f;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, MeetingMsgFlag &f)
+{
+ Q_UINT8 i;
+ s >> i; f = (MeetingMsgFlag)i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const TextInfo &ti)
+{
+ s << (Q_UINT32)ti.i << ti.text;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, TextInfo &ti)
+{
+ Q_UINT32 i;
+ s >> i >> ti.text; ti.i = i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const MeetingCheckBox::Type &t)
+{
+ s << (Q_UINT8)t;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, MeetingCheckBox::Type &t)
+{
+ Q_UINT8 i;
+ s >> i; t = (MeetingCheckBox::Type)i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const TypeInfo &ti)
+{
+ s << (Q_UINT32)ti.i << ti.type;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, TypeInfo &ti)
+{
+ Q_UINT32 i;
+ s >> i >> ti.type; ti.i = i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const BoardData &bd)
+{
+ s << (Q_UINT8)bd.type << bd.name;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, BoardData &bd)
+{
+ Q_UINT8 i;
+ s >> i >> bd.name;
+ bd.type = (PlayerComboBox::Type)i;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const ExtData &ed)
+{
+ s << ed.bds << ed.text << ed.type;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, ExtData &ed)
+{
+ s >> ed.bds >> ed.text >> ed.type;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const MeetingLineData &pld)
+{
+ s << pld.ed << (Q_UINT8)pld.own;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, MeetingLineData &pld)
+{
+ Q_UINT8 b;
+ s >> pld.ed >> b; pld.own = b;
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+QDataStream &operator << (QDataStream &s, const MetaFlag &f)
+{
+ s << (Q_UINT8)f;
+ return s;
+}
+
+QDataStream &operator >> (QDataStream &s, MetaFlag &f)
+{
+ Q_UINT8 i;
+ s >> i; f = (MetaFlag)i;
+ return s;
+}
+
+
+//-----------------------------------------------------------------------------
+Stream::Stream(int _mode)
+: mode(_mode)
+{
+ setDevice(&buf);
+ Q_ASSERT( _mode==IO_ReadOnly || _mode==IO_WriteOnly );
+ buf.open(_mode);
+}
+
+void Stream::clear()
+{
+ buf.close();
+ buf.open(mode | IO_Truncate);
+}
+
+void Stream::setArray(QByteArray a)
+{
+ buf.close();
+ buf.setBuffer(a);
+ buf.open(mode);
+}
+
+bool ReadingStream::readOk()
+{
+ return ( buf.status()==IO_Ok );
+}
+
+void ReadingStream::clearRead()
+{
+ int i = buf.at();
+ if ( i==0 ) return;
+ buf.close();
+ QByteArray a;
+ a.duplicate(buffer().data() + i, size() - i);
+ buf.setBuffer(a);
+ buf.open(IO_ReadOnly);
+}
+
+//-----------------------------------------------------------------------------
+void IOBuffer::writingToReading()
+{
+ // this should do the trick :)
+ reading.setArray(writing.buffer());
+ QByteArray a;
+ writing.setArray(a);
+}
+
+//-----------------------------------------------------------------------------
+void BufferArray::clear(uint k)
+{
+ for (uint i=k; i<a.size(); i++) delete a[i];
+}
+
+BufferArray::~BufferArray()
+{
+ clear(0);
+}
+
+void BufferArray::resize(uint nb)
+{
+ uint s = a.size();
+ if ( nb<s ) clear(nb);
+ a.resize(nb);
+ for (uint i=s; i<nb; i++) a[i] = new IOBuffer;
+}
+
+QDataStream &operator <<(QDataStream &s, const BufferArray &b)
+{
+ for (uint i=0; i<b.size(); i++) {
+ s.writeBytes(b[i]->writing.buffer().data(), b[i]->writing.size());
+// debug("BUFFERARRAY : << (i=%i size=%i)", i, b[i]->writing.size());
+ b[i]->writing.clear();
+ }
+ return s;
+}
+
+QDataStream &operator >>(QDataStream &s, BufferArray &b)
+{
+ uint size;
+ char *c;
+ for (uint i=0; i<b.size(); i++) {
+ s.readBytes(c, size);
+ QByteArray a;
+ a.assign(c, size);
+ b[i]->reading.setArray(a);
+// debug("BUFFERARRAY : >> (i=%i c=%i size=%i s=%i)",
+// i, (int)c, size, b[i]->reading.size());
+ }
+ return s;
+}
diff --git a/libksirtet/lib/types.h b/libksirtet/lib/types.h
new file mode 100644
index 00000000..2ccdcba0
--- /dev/null
+++ b/libksirtet/lib/types.h
@@ -0,0 +1,197 @@
+#ifndef MTYPES_H
+#define MTYPES_H
+
+#include <qstring.h>
+#include <qbuffer.h>
+#include <qvaluelist.h>
+
+#include "miscui.h"
+
+
+/** Internal class : used for client identification. */
+class cId
+{
+ public:
+ cId() {}
+ cId(const QString &gameName, const QString &gameId);
+
+ enum State { Accepted, LibIdClash, GameNameClash, GameIdClash };
+ void check(const cId &id);
+ bool accepted() const { return state==Accepted; }
+ QString errorMessage(const cId &id) const;
+
+ friend QDataStream &operator << (QDataStream &s, const cId &id);
+ friend QDataStream &operator >> (QDataStream &s, cId &id);
+
+ private:
+ QString libId, gameName, gameId;
+ State state;
+};
+QDataStream &operator << (QDataStream &s, const cId &id);
+QDataStream &operator >> (QDataStream &s, cId &id);
+
+/** Flags used for the netmeeting. */
+enum MeetingMsgFlag
+ { IdFlag = 0, EndFlag, NewFlag, DelFlag, Mod_TextFlag, Mod_TypeFlag, Mod_OptFlag, PlayFlag };
+QDataStream &operator << (QDataStream &s, const MeetingMsgFlag &f);
+QDataStream &operator >> (QDataStream &s, MeetingMsgFlag &f);
+
+/** Internal class : used in netmeeting to transport text line. */
+class TextInfo
+{
+ public:
+ TextInfo() {}
+
+ uint i;
+ QString text;
+};
+QDataStream &operator << (QDataStream &s, const TextInfo &ti);
+QDataStream &operator >> (QDataStream &s, TextInfo &ti);
+
+/** Internal class : used in netmeeting to transport readiness status. */
+typedef struct {
+ uint i;
+ MeetingCheckBox::Type type;
+} TypeInfo;
+QDataStream &operator << (QDataStream &s, const MeetingCheckBox::Type &t);
+QDataStream &operator >> (QDataStream &s, MeetingCheckBox::Type &t);
+QDataStream &operator << (QDataStream &s, const TypeInfo &ti);
+QDataStream &operator >> (QDataStream &s, TypeInfo &ti);
+
+/* Internal class : store game data. */
+class BoardData
+{
+ public:
+ BoardData() {}
+
+ QString name;
+ PlayerComboBox::Type type;
+};
+QDataStream &operator <<(QDataStream &, const BoardData &);
+QDataStream &operator >>(QDataStream &, BoardData &);
+
+/* Internal class : store extended game data (used in netmeeting). */
+class ExtData
+{
+ public:
+ ExtData() {}
+ ExtData(const QValueList<BoardData> &_bds, const QString &_text,
+ MeetingCheckBox::Type _type)
+ : bds(_bds), text(_text), type(_type) {}
+
+ QValueList<BoardData> bds;
+ QString text;
+ MeetingCheckBox::Type type;
+};
+QDataStream &operator << (QDataStream &s, const ExtData &ed);
+QDataStream &operator >> (QDataStream &s, ExtData &ed);
+
+/* Internal class : store meeting line data (in netmeeting). */
+class MeetingLineData
+{
+ public:
+ MeetingLineData() {}
+
+ ExtData ed;
+ bool own;
+};
+QDataStream &operator << (QDataStream &s, const MeetingLineData &pld);
+QDataStream &operator >> (QDataStream &s, MeetingLineData &pld);
+
+/* Internal class : store remote host data. */
+class Socket;
+
+class RemoteHostData
+{
+ public:
+ RemoteHostData() : socket(0) {}
+
+ Socket *socket;
+ QValueList<BoardData> bds;
+};
+
+/* Internal class : store connection data (used by config. wizard). */
+class ConnectionData
+{
+ public:
+ ConnectionData() {}
+
+ bool network, server;
+ RemoteHostData rhd;
+};
+
+/** Flags used for network communication. */
+enum MetaFlag { MF_Ask = 0, MF_Data };
+QDataStream &operator << (QDataStream &s, const MetaFlag &f);
+QDataStream &operator >> (QDataStream &s, MetaFlag &f);
+
+/** Internal class : encapsulate read/write QBuffer. */
+class Stream : public QDataStream
+{
+ public:
+ Stream(int mode);
+
+ void clear();
+ void setArray(QByteArray a);
+
+ QByteArray buffer() const { return buf.buffer(); }
+ uint size() const { return buf.buffer().size(); }
+
+ protected:
+ QBuffer buf;
+
+ private:
+ int mode;
+};
+
+/** Internal class : encapsulate write QBuffer. */
+class WritingStream : public Stream
+{
+ public:
+ WritingStream() : Stream(IO_WriteOnly) {}
+};
+
+/** Internal class : encapsulate read QBuffer. */
+class ReadingStream : public Stream
+{
+ public:
+ ReadingStream() : Stream(IO_ReadOnly) {}
+
+ bool readOk();
+ void clearRead();
+};
+
+/** Internal class : include a @ref ReadingStream and a @ref WritingStream. */
+class IOBuffer
+{
+ public:
+ IOBuffer() {}
+
+ void writingToReading();
+
+ ReadingStream reading;
+ WritingStream writing;
+};
+
+/** Internal class : array of @ref IOBuffer. */
+class BufferArray
+{
+ public:
+ BufferArray() {}
+ BufferArray(uint nb) { resize(nb); }
+ ~BufferArray();
+
+ void resize(uint nb);
+
+ uint size() const { return a.size(); }
+ IOBuffer *operator [](uint i) const { return a[i]; }
+
+ private:
+ QMemArray<IOBuffer *> a;
+
+ void clear(uint nb);
+};
+QDataStream &operator <<(QDataStream &s, const BufferArray &b);
+QDataStream &operator >>(QDataStream &s, BufferArray &b);
+
+#endif // MTYPES_H
diff --git a/libksirtet/lib/version.h b/libksirtet/lib/version.h
new file mode 100644
index 00000000..49dc7b6b
--- /dev/null
+++ b/libksirtet/lib/version.h
@@ -0,0 +1,6 @@
+#define MULTI_VERSION "0.1.8"
+#define MULTI_LONG_VERSION "0.1.8 (11 April 2001)"
+#define MULTI_COPYLEFT "(c) 1998-2001, Nicolas Hadacek"
+
+#define MULTI_ID "003" // should be increased when incompatible
+ // changes are made.
diff --git a/libksirtet/lib/wizard.cpp b/libksirtet/lib/wizard.cpp
new file mode 100644
index 00000000..30d5a89d
--- /dev/null
+++ b/libksirtet/lib/wizard.cpp
@@ -0,0 +1,229 @@
+#include "wizard.h"
+#include "wizard.moc"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <qvbuttongroup.h>
+#include <qradiobutton.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qsignalmapper.h>
+#include <qvgroupbox.h>
+#include <qgrid.h>
+#include <qfile.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdialogbase.h>
+
+#include "types.h"
+#include "defines.h"
+#include "socket.h"
+
+#ifdef __bsdi__
+#define IPPORT_USERRESERVED IPPORT_DYNAMIC
+#endif
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sgi)
+#define IPPORT_USERRESERVED IPPORT_RESERVED
+#endif
+#define MIN_USER_PORT (unsigned short int)IPPORT_USERRESERVED
+#define MAX_USER_PORT 65535
+
+MPWizard::MPWizard(const MPGameInfo &gi, ConnectionData &_cd,
+ QWidget *parent, const char *name)
+: KWizard(parent, name, TRUE), cd(_cd)
+{
+// setupTypePage(); // #### REMOVE NETWORK GAMES UNTIL FIXED
+ type = Local;
+ setupLocalPage(gi);
+}
+
+//-----------------------------------------------------------------------------
+void MPWizard::setupTypePage()
+{
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+
+ typePage = new QVBox(this);
+ typePage->setMargin(KDialogBase::marginHint());
+
+ QVButtonGroup *vbg = new QVButtonGroup(typePage);
+ connect(vbg, SIGNAL(clicked(int)), SLOT(typeChanged(int)));
+ QRadioButton *b;
+ b = new QRadioButton(i18n("Create a local game"), vbg);
+ b = new QRadioButton(i18n("Create a network game"), vbg);
+ b = new QRadioButton(i18n("Join a network game"), vbg);
+ type = (Type)cg.config()->readNumEntry(MP_GAMETYPE, 0);
+ if ( type<0 || type>2 ) type = Local;
+ vbg->setButton(type);
+
+ typePage->setSpacing(KDialogBase::spacingHint());
+ net = new QVGroupBox(i18n("Network Settings"), typePage);
+ QGrid *grid = new QGrid(2, net);
+ lserver = new QLabel(" ", grid);
+ grid->setSpacing(KDialogBase::spacingHint());
+ eserver = new QLineEdit(grid);
+ (void)new QLabel(i18n("Port:"), grid);
+ int p = cg.config()->readNumEntry(MP_PORT, (uint)MIN_USER_PORT);
+ eport = new KIntNumInput(p, grid);
+ eport->setRange(MIN_USER_PORT, MAX_USER_PORT, 1, false);
+
+ addPage(typePage, i18n("Choose Game Type"));
+ setHelpEnabled(typePage, FALSE);
+ typeChanged(type);
+}
+
+//-----------------------------------------------------------------------------
+void MPWizard::setupLocalPage(const MPGameInfo &gi)
+{
+ localPage = new QVBox(this);
+ localPage->setMargin(KDialogBase::marginHint());
+
+ wl = new WidgetList<PlayerLine>(5, localPage);
+ QSignalMapper *husm = new QSignalMapper(this);
+ if (gi.humanSettingSlot) connect(husm, SIGNAL(mapped(int)),
+ gi.humanSettingSlot);
+ QSignalMapper *aism = new QSignalMapper(this);
+ if (gi.AISettingSlot) connect(aism, SIGNAL(mapped(int)), gi.AISettingSlot);
+
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+ QString n;
+ PlayerComboBox::Type type;
+ PlayerLine *pl;
+ Q_ASSERT( gi.maxNbLocalPlayers>0 );
+ for (uint i=0; i<gi.maxNbLocalPlayers; i++) {
+ type = (PlayerComboBox::Type)
+ cg.config()->readNumEntry(QString(MP_PLAYER_TYPE).arg(i),
+ (i==0 ? PlayerComboBox::Human : PlayerComboBox::None));
+ n = cg.config()->readEntry(QString(MP_PLAYER_NAME).arg(i),
+ i18n("Player #%1").arg(i));
+
+ pl = new PlayerLine(type, n, gi.humanSettingSlot, gi.AISettingSlot,
+ i!=0, gi.AIAllowed, wl);
+ connect(pl, SIGNAL(typeChanged(int)), SLOT(lineTypeChanged(int)));
+ husm->setMapping(pl, i);
+ connect(pl, SIGNAL(setHuman()), husm, SLOT(map()));
+ aism->setMapping(pl, i);
+ connect(pl, SIGNAL(setAI()), aism, SLOT(map()));
+ wl->append(pl);
+ }
+
+ ((QVBox *)localPage)->setSpacing(KDialogBase::spacingHint());
+
+// keys = new QPushButton(i18n("Configure Keys..."), localPage);
+// connect(keys, SIGNAL(clicked()), SLOT(configureKeysSlot()));
+
+ addPage(localPage, i18n("Local Player's Settings"));
+ setHelpEnabled(localPage, FALSE);
+ lineTypeChanged(0);
+}
+
+QString MPWizard::name(uint i) const
+{
+ QString s = wl->widget(i)->name();
+ if ( s.length()==0 ) s = i18n("Player #%1").arg(i);
+ return s;
+}
+
+void MPWizard::typeChanged(int t)
+{
+ type = (Type)t;
+
+ QString str;
+ if ( type!=Client ) {
+ str = "localhost";
+ lserver->setText(i18n("Hostname:"));
+ } else {
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+ str = cg.config()->readEntry(MP_SERVER_ADDRESS,
+ i18n("the.server.address"));
+ lserver->setText(i18n("Server address:"));
+ }
+ eserver->setText(str);
+ eserver->setEnabled(type==Client);
+ eport->setEnabled(type!=Local);
+ net->setEnabled(type!=Local);
+}
+
+void MPWizard::lineTypeChanged(int)
+{
+ bool b = FALSE;
+ for (uint i=0; i<wl->size(); i++)
+ if ( wl->widget(i)->type()==PlayerComboBox::Human ) {
+ b = TRUE;
+ break;
+ }
+// keys->setEnabled(b);
+}
+
+void MPWizard::accept()
+{
+ KConfigGroupSaver cg(kapp->config(), MP_GROUP);
+
+ cd.network = ( type!=Local );
+ cd.server = ( type!=Client );
+
+ if (cd.network) {
+ //**********************************************************
+ // create socket
+ int flags = KExtendedSocket::inetSocket
+ | KExtendedSocket::streamSocket;
+ if (cd.server) flags |= KExtendedSocket::passiveSocket;
+ QString host = QFile::encodeName(eserver->text());
+ KExtendedSocket *socket
+ = new KExtendedSocket(host, eport->value(), flags);
+
+ // do lookup
+ int res = socket->lookup();
+ if ( checkSocket(res, socket, i18n("Error looking up for \"%1\"")
+ .arg(host), this) ) {
+ delete socket;
+ return;
+ }
+
+ // connect (client) or listen (server)
+ res = (cd.server ? socket->listen() : socket->connect());
+ if ( checkSocket(res, socket, i18n("Error opening socket"), this) ) {
+ delete socket;
+ return;
+ }
+
+ cd.rhd.socket = new Socket(socket, true);
+
+ if ( !cd.server )
+ cg.config()->writeEntry(MP_SERVER_ADDRESS, eserver->text());
+ cg.config()->writeEntry(MP_PORT, eport->value());
+ }
+
+ BoardData bd;
+ for (uint i=0; i<wl->size(); i++) {
+ if ( wl->widget(i)->type()==PlayerComboBox::None ) continue;
+ bd.name = name(i);
+ bd.type = wl->widget(i)->type();
+ cd.rhd.bds += bd;
+ }
+
+ cg.config()->writeEntry(MP_GAMETYPE, (int)type);
+ for (uint i=0; i<wl->size(); i++) {
+ cg.config()->writeEntry(QString(MP_PLAYER_TYPE).arg(i),
+ (int)wl->widget(i)->type());
+ cg.config()->writeEntry(QString(MP_PLAYER_NAME).arg(i), name(i));
+ }
+
+ KWizard::accept();
+}
+
+void MPWizard::showPage(QWidget *page)
+{
+ if ( page==localPage ) setFinishEnabled(localPage, TRUE);
+ KWizard::showPage(page);
+}
+
+void MPWizard::configureKeysSlot()
+{
+ uint nb = 0;
+ for (uint i=0; i<wl->size(); i++)
+ if ( wl->widget(i)->type()==PlayerComboBox::Human ) nb++;
+ emit configureKeys(nb);
+}
diff --git a/libksirtet/lib/wizard.h b/libksirtet/lib/wizard.h
new file mode 100644
index 00000000..29287508
--- /dev/null
+++ b/libksirtet/lib/wizard.h
@@ -0,0 +1,57 @@
+#ifndef WIZARD_H
+#define WIZARD_H
+
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qvbox.h>
+#include <qvgroupbox.h>
+#include <qpushbutton.h>
+
+#include <knuminput.h>
+#include <kconfig.h>
+#include <kwizard.h>
+
+#include "pline.h"
+#include "mp_interface.h"
+
+class ConnectionData;
+
+class MPWizard : public KWizard
+{
+ Q_OBJECT
+
+ public:
+ MPWizard(const MPGameInfo &gi, ConnectionData &cd,
+ QWidget *parent = 0, const char *name = 0);
+
+ void showPage(QWidget *page);
+
+ signals:
+ void configureKeys(uint);
+
+ protected slots:
+ void accept();
+
+ private slots:
+ void typeChanged(int t);
+ void lineTypeChanged(int);
+ void configureKeysSlot();
+
+ private:
+ ConnectionData &cd;
+ enum Type { Local, Server, Client };
+ Type type;
+ QVBox *typePage, *localPage;
+ WidgetList<PlayerLine> *wl;
+ QLabel *lserver;
+ QLineEdit *eserver;
+ KIntNumInput *eport;
+ QVGroupBox *net;
+// QPushButton *keys;
+
+ void setupTypePage();
+ void setupLocalPage(const MPGameInfo &gi);
+ QString name(uint i) const;
+};
+
+#endif // WIZARD_H