File indexing completed on 2023-09-24 08:17:47

0001 /*
0002     This file is part of the KDE games lskat program
0003     SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "mainwindow.h"
0009 
0010 // Include files for Qt
0011 #include <QAction>
0012 #include <QDir>
0013 #include <QKeySequence>
0014 #include <QPointer>
0015 #include <QRandomGenerator>
0016 #include <QStatusBar>
0017 #include <QStandardPaths>
0018 
0019 // KF includes
0020 #include <kwidgetsaddons_version.h>
0021 #include <KActionCollection>
0022 #include <KConfigGroup>
0023 #include <KMessageBox>
0024 #include <KSharedConfig>
0025 #include <KStandardGameAction>
0026 #include <KLocalizedString>
0027 #include <KSelectAction>
0028 // Application specific includes
0029 #include "lskat_debug.h"
0030 #include "lskatglobal.h"
0031 #include "gameview.h"
0032 #include "abstractengine.h"
0033 #include "engine_two.h"
0034 #include "display_two.h"
0035 #include "config_two.h"
0036 #include "display_intro.h"
0037 #include "deck.h"
0038 #include "player.h"
0039 #include "mouseinput.h"
0040 #include "aiinput.h"
0041 #include "namedialogwidget.h"
0042 #include "fromlibkdegames/kcarddialog.h"
0043 #include "fromlibkdegames/carddeckinfo.h"
0044 
0045 // Configuration file
0046 #include <config-src.h>
0047 
0048 // Forward declarations
0049 const int ADVANCE_PERIOD =  20;
0050 
0051 // Shortcut to access the actions
0052 #define ACTION(x)   (actionCollection()->action(x))
0053 
0054 using namespace InputDevice;
0055 
0056 // Construct the main application window
0057 Mainwindow::Mainwindow(QWidget *parent)
0058           : KXmlGuiWindow(parent)
0059 {
0060     // Reset stuff
0061     mDeck        = nullptr;
0062     mEngine      = nullptr;
0063     mDisplay     = nullptr;
0064     mView        = nullptr;
0065     mLSkatConfig = nullptr;
0066     mCanvas      = nullptr;
0067     mTheme       = nullptr;
0068 
0069     // Read theme files
0070     QStringList themeList;
0071     const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("grafix"), QStandardPaths::LocateDirectory);
0072     for (const QString& dir : dirs) {
0073         const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.desktop"));
0074         for (const QString& file : fileNames) {
0075             themeList.append(dir + '/' + file);
0076         }
0077     }
0078     if (themeList.isEmpty())
0079     {
0080         KMessageBox::error(this, i18n("Installation error: No theme list found."));
0081         QTimer::singleShot(0, this, &QWidget::close);
0082         return;
0083     }
0084 
0085     // Read theme files
0086     for (int i = 0; i < themeList.size(); i++)
0087     {
0088         KConfig themeInfo(themeList.at(i), KConfig::SimpleConfig);
0089         KConfigGroup themeGroup(&themeInfo, "Theme");
0090         QString name = themeGroup.readEntry("Name", QString());
0091         QString file = themeGroup.readEntry("File", QString());
0092         bool isDefault = themeGroup.readEntry("Default", false);
0093         mThemeFiles[name] = file;
0094         if (mThemeDefault.isNull()) mThemeDefault = name;
0095         if (isDefault) mThemeDefault = name;
0096 
0097         if (global_debug > 0) qCDebug(LSKAT_LOG) << "Found theme: " << themeList.at(i) << " Name(i18n)=" << name << " File=" << file << " default=" << isDefault;
0098     }
0099     mThemeIndexNo = themeIdxFromName(mThemeDefault);
0100 
0101     // Create menus etc
0102     initGUI();
0103 
0104     // The LSkat config
0105     mLSkatConfig = new ConfigTwo(this);
0106     connect(mLSkatConfig, &ConfigTwo::signalInputType, this, &Mainwindow::setInputType);
0107     mLSkatConfig->reset();
0108 
0109     // Read game properties and set default values (after config)
0110     readProperties();
0111 
0112     // TODO: Bugfix: Needs to be here if initGUI is before readProperties
0113     if (global_debug > 0) qCDebug(LSKAT_LOG) << "Setting current theme item to " << mThemeIndexNo;
0114     ((KSelectAction *)ACTION(QLatin1String("theme")))->setCurrentItem(mThemeIndexNo);
0115 
0116     // Get the card deck
0117     const quint32 seed = QRandomGenerator::global()->generate();
0118     if (global_debug > 0) qCDebug(LSKAT_LOG) << "Random seed" << seed;
0119     mDeck = new Deck(seed, this);
0120 
0121     // Theme manager
0122     QString themeFile = themefileFromIdx(mThemeIndexNo);
0123     if (global_debug > 0) qCDebug(LSKAT_LOG) << "Load theme" << themeFile << " no=" << mThemeIndexNo;
0124     mTheme  = new ThemeManager(mCardTheme, themeFile, this, this->width());
0125     if (mTheme->checkTheme() != 0)
0126     {
0127         KMessageBox::error(this, i18n("Installation error: Theme file error."));
0128         QTimer::singleShot(0, this, &QWidget::close);
0129         return;
0130     }
0131 
0132     // Overall view
0133     mCanvas        = new QGraphicsScene(this);
0134     mView          = new GameView(QSize(880, 675), ADVANCE_PERIOD, mCanvas, mTheme, this);
0135 
0136     // Create intro
0137     mGameMode      = Intro;
0138     mDisplay       = new DisplayIntro(mDeck, mCanvas, mTheme, ADVANCE_PERIOD, mView);
0139     setCentralWidget(mView);
0140     connect(mView, &GameView::signalLeftMousePress, this, &Mainwindow::menuNewLSkatGame);
0141 
0142     // Create GUI
0143     setupGUI();
0144 
0145     statusBar()->showMessage(i18n("Welcome to Skat! Please start a new game."));
0146 
0147     // Skip intro?
0148     if (global_skip_intro)
0149     {
0150         menuNewLSkatGame();
0151     }
0152     // Start game automatically in demo mode
0153     else if (global_demo_mode)
0154     {
0155         // Start intro
0156         mDisplay->start();
0157         QTimer::singleShot(12500, this, &Mainwindow::menuNewLSkatGame);
0158     }
0159     else
0160     {
0161         // Start intro
0162         mDisplay->start();
0163     }
0164     if (global_debug > 0) qCDebug(LSKAT_LOG) << "Mainwindow setup constructor done";
0165 }
0166 
0167 // Destructor
0168 Mainwindow::~Mainwindow()
0169 {
0170     saveProperties();
0171     delete mEngine;
0172     delete mDisplay;
0173     delete mLSkatConfig;
0174     delete mDeck;
0175     delete mView;
0176     delete mCanvas;
0177     delete mTheme;
0178 }
0179 
0180 // Called by KMainWindow when the last window of the application is
0181 void Mainwindow::closeEvent(QCloseEvent *event)
0182 {
0183     if (mEngine)
0184     {
0185         mEngine->stopGame();
0186     }
0187     saveProperties();
0188     KXmlGuiWindow::closeEvent(event);
0189 }
0190 
0191 // Retrieve a theme file name from the menu index number
0192 QString Mainwindow::themefileFromIdx(int idx)
0193 {
0194     QStringList list(mThemeFiles.keys());
0195     list.sort();
0196     QString themeFile = mThemeFiles[list.at(idx)];
0197     return themeFile;
0198 }
0199 
0200 // Retrieve a theme idx from a theme name
0201 int Mainwindow::themeIdxFromName(const QString &name)
0202 {
0203     QStringList list(mThemeFiles.keys());
0204     list.sort();
0205     for (int i = 0; i < list.size(); ++i)
0206     {
0207         if (list[i] == name) return i;
0208     }
0209     qCCritical(LSKAT_LOG) << "Theme index lookup failed for " << name;
0210     return 0;
0211 }
0212 
0213 // Save properties
0214 void Mainwindow::saveProperties()
0215 {
0216     KConfig *config = KSharedConfig::openConfig().data();
0217 
0218     // Program data
0219     KConfigGroup cfg = config->group("ProgramData");
0220     cfg.writeEntry("startplayer", mStartPlayer);
0221     cfg.writeEntry("ThemeIndexNo", mThemeIndexNo);
0222 
0223     // LSkat data
0224     mLSkatConfig->save(config);
0225     config->sync();
0226 }
0227 
0228 // Load properties
0229 void Mainwindow::readProperties()
0230 {
0231     KConfig *config = KSharedConfig::openConfig().data();
0232 
0233     // Program data
0234     KConfigGroup cfg = config->group("ProgramData");
0235 
0236     // Theme number
0237     mThemeIndexNo = cfg.readEntry("ThemeIndexNo", themeIdxFromName(mThemeDefault));
0238     if (mThemeIndexNo >= mThemeFiles.size()) mThemeIndexNo = 0;
0239 
0240     // Read card path
0241     mCardTheme  = CardDeckInfo::deckName(cfg);
0242 
0243     int no = cfg.readEntry("startplayer", 0);
0244     setStartPlayer(no);
0245     mLSkatConfig->load(config);
0246 }
0247 
0248 // Create a input with the given type
0249 AbstractInput *Mainwindow::createInput(
0250                                  InputDeviceType inputType,
0251                                  AbstractDisplay *display,
0252                                  AbstractEngine *engine)
0253 {
0254     AbstractInput *input = nullptr;
0255 
0256     // Always use AI input in demo mode
0257     if (global_demo_mode)
0258     {
0259         inputType = TypeAiInput;
0260     }
0261 
0262     // Create the player input
0263     if (inputType == TypeMouseInput)
0264     {
0265         MouseInput *mouseInput = new MouseInput(this);
0266         connect(mView, &GameView::signalLeftMousePress,
0267                 mouseInput, &MouseInput::mousePress);
0268         connect(mouseInput, &MouseInput::signalConvertMousePress,
0269                 display, &AbstractDisplay::convertMousePress);
0270         connect(mouseInput, &MouseInput::signalPlayerInput,
0271                 engine, &AbstractEngine::playerInput);
0272         input = mouseInput;
0273         if (global_debug > 0) qCDebug(LSKAT_LOG) << "Create MOUSE INPUT";
0274     }
0275     else if (inputType == TypeAiInput)
0276     {
0277         AiInput *aiInput = new AiInput((EngineTwo *)engine, this);
0278         connect(aiInput, &AiInput::signalPlayerInput,
0279                 engine, &AbstractEngine::playerInput);
0280         input = aiInput;
0281         if (global_debug > 0) qCDebug(LSKAT_LOG) << "Create AI INPUT";
0282     }
0283     else
0284     {
0285         qCCritical(LSKAT_LOG) << "Unsupported input device type" << inputType;
0286     }
0287 
0288     return input;
0289 }
0290 
0291 // Start a new game
0292 void Mainwindow::startGame()
0293 {
0294     // Enable game action
0295     QLatin1String endName(KStandardGameAction::name(KStandardGameAction::End));
0296     ACTION(endName)->setEnabled(true);
0297 
0298     // Deal cards to player - Shuffle card deck and reset pile
0299     mDeck->shuffle();
0300 
0301     // Draw Trump
0302     Suite trump = mDeck->randomTrump();
0303 
0304     // Loop all players in the game
0305     QHashIterator<int,Player *> it = mLSkatConfig->playerIterator();
0306     while (it.hasNext())
0307     {
0308         it.next();
0309         Player *player = it.value();
0310         player->setDeck(mDeck);
0311         // Deal cards
0312         player->deal(16);
0313         // Store trump
0314         player->setTrump(trump);
0315     }
0316 
0317     // Start display
0318     mDisplay->start();
0319 
0320     // Start the game engine
0321     mEngine->startGame(trump, mStartPlayer);
0322 
0323     // Start player for next game
0324     setStartPlayer(1-mStartPlayer);
0325 
0326     //  statusBar()->clearMessage();
0327 }
0328 
0329 // Here a game over is signalled
0330 void Mainwindow::gameOver(int /*winner*/)
0331 {
0332     QLatin1String endName(KStandardGameAction::name(KStandardGameAction::End));
0333     ACTION(endName)->setEnabled(false);
0334     statusBar()->showMessage(i18n("Game Over. Please start a new game."));
0335 
0336     // Automatically restart game in demo mode
0337     if (global_demo_mode)
0338     {
0339         QTimer::singleShot(10000, this, &Mainwindow::menuNewLSkatGame);
0340     }
0341 }
0342 
0343 // Show next player
0344 void Mainwindow::nextPlayer(Player *player)
0345 {
0346     int no       = player->id() + 1;
0347     QString name = player->name();
0348     statusBar()->showMessage(i18nc("Player name and number", "Next move for %1 (player %2)", name, no));
0349 }
0350 
0351 // Setup the GUI
0352 void Mainwindow::initGUI()
0353 {
0354     QAction *action;
0355 
0356     // Start a new game
0357     action = KStandardGameAction::gameNew(this, &Mainwindow::menuNewLSkatGame, actionCollection());
0358     if (global_demo_mode) action->setEnabled(false);
0359 
0360     // Clear all time statistics
0361     action = KStandardGameAction::clearStatistics(this, &Mainwindow::menuClearStatistics, actionCollection());
0362     action->setWhatsThis(i18n("Clears the all time statistics which is kept in all sessions."));
0363     if (global_demo_mode) action->setEnabled(false);
0364 
0365     // End a game
0366     action = KStandardGameAction::end(this, &Mainwindow::menuEndGame, actionCollection());
0367     action->setWhatsThis(i18n("Ends a currently played game. No winner will be declared."));
0368     action->setEnabled(false);
0369 
0370     // Quit the program
0371     action = KStandardGameAction::quit(this, &QWidget::close, actionCollection());
0372     action->setWhatsThis(i18n("Quits the program."));
0373 
0374     // Determine start player
0375     KSelectAction *startPlayerAct = new KSelectAction(i18n("Starting Player"), this);
0376     actionCollection()->addAction(QStringLiteral("startplayer"), startPlayerAct);
0377     connect(startPlayerAct, &KSelectAction::indexTriggered, this, &Mainwindow::menuStartplayer);
0378     startPlayerAct->setToolTip(i18n("Changing starting player..."));
0379     startPlayerAct->setWhatsThis(i18n("Chooses which player begins the next game."));
0380     QStringList list;
0381     list.clear();
0382     list.append(i18n("Player &1"));
0383     list.append(i18n("Player &2"));
0384     startPlayerAct->setItems(list);
0385     if (global_demo_mode) startPlayerAct->setEnabled(false);
0386 
0387     // Determine who plays player 1
0388     KSelectAction *player1Act = new KSelectAction(i18n("Player &1 Played By"), this);
0389     actionCollection()->addAction(QStringLiteral("player1"), player1Act);
0390     connect(player1Act, &KSelectAction::indexTriggered, this, &Mainwindow::menuPlayer1By);
0391     player1Act->setToolTip(i18n("Changing who plays player 1..."));
0392     player1Act->setWhatsThis(i18n("Changing who plays player 1."));
0393     list.clear();
0394     list.append(i18n("&Mouse"));
0395     list.append(i18n("&Computer"));
0396     player1Act->setItems(list);
0397     if (global_demo_mode) player1Act->setEnabled(false);
0398 
0399     // Determine who plays player 2
0400     KSelectAction *player2Act = new KSelectAction(i18n("Player &2 Played By"), this);
0401     actionCollection()->addAction(QStringLiteral("player2"), player2Act);
0402     connect(player2Act, &KSelectAction::indexTriggered, this, &Mainwindow::menuPlayer2By);
0403     player2Act->setToolTip(i18n("Changing who plays player 2..."));
0404     player2Act->setWhatsThis(i18n("Changing who plays player 2."));
0405     player2Act->setItems(list);
0406     if (global_demo_mode) player2Act->setEnabled(false);
0407 
0408     // Add all theme files to the menu
0409     QStringList themes(mThemeFiles.keys());
0410     themes.sort();
0411 
0412     KSelectAction *themeAct = new KSelectAction(i18n("&Theme"), this);
0413     actionCollection()->addAction(QStringLiteral("theme"), themeAct);
0414     themeAct->setItems(themes);
0415     connect(themeAct, &KSelectAction::indexTriggered, this, &Mainwindow::changeTheme);
0416     if (global_debug > 0) qCDebug(LSKAT_LOG) << "Setting current theme item to " << mThemeIndexNo;
0417     themeAct->setCurrentItem(mThemeIndexNo);
0418     themeAct->setToolTip(i18n("Changing theme..."));
0419     themeAct->setWhatsThis(i18n("Changing theme."));
0420 
0421     // Choose card deck
0422     QAction *action1 = actionCollection()->addAction(QStringLiteral("select_carddeck"));
0423     action1->setText(i18n("Select &Card Deck..."));
0424     actionCollection()->setDefaultShortcut(action1, QKeySequence(Qt::Key_F10));
0425     connect(action1, &QAction::triggered, this, &Mainwindow::menuCardDeck);
0426     action1->setToolTip(i18n("Configure card decks..."));
0427     action1->setWhatsThis(i18n("Choose how the cards should look."));
0428 
0429     // Change player names
0430     action = actionCollection()->addAction(QStringLiteral("change_names"));
0431     action->setText(i18n("&Change Player Names..."));
0432     connect(action, &QAction::triggered, this, &Mainwindow::menuPlayerNames);
0433     if (global_demo_mode) action->setEnabled(false);
0434 }
0435 
0436 // Choose start player
0437 void Mainwindow::menuStartplayer()
0438 {
0439     int i = ((KSelectAction *)ACTION(QLatin1String("startplayer")))->currentItem();
0440     setStartPlayer(i);
0441 }
0442 
0443 // Change the theme of the game
0444 void Mainwindow::changeTheme(int idx)
0445 {
0446     mThemeIndexNo = idx;
0447     QString themeFile = themefileFromIdx(idx);
0448     if (global_debug > 0) qCDebug(LSKAT_LOG) << "Select theme " << themeFile;
0449     mTheme->updateTheme(themeFile);
0450 }
0451 
0452 // Select input for player 1
0453 void Mainwindow::menuPlayer1By()
0454 {
0455     int i = ((KSelectAction *)ACTION(QLatin1String("player1")))->currentItem();
0456     mLSkatConfig->setInputType(0, (InputDeviceType)i);
0457 }
0458 
0459 // Select input for player 2
0460 void Mainwindow::menuPlayer2By()
0461 {
0462     int i = ((KSelectAction *)ACTION(QLatin1String("player2")))->currentItem();
0463     mLSkatConfig->setInputType(1, (InputDeviceType)i);
0464 }
0465 
0466 // Choose a card deck
0467 void Mainwindow::menuCardDeck()
0468 {
0469     QString front = mCardTheme;
0470 
0471     KConfigGroup grp = KSharedConfig::openConfig()->group("ProgramData");
0472     KCardWidget *cardwidget = new KCardWidget();
0473     QPointer<KCardDialog> dlg;
0474 
0475     cardwidget->readSettings(grp);
0476     dlg = new KCardDialog(cardwidget);
0477     if (dlg->exec() == QDialog::Accepted)
0478     {
0479         // Always store the settings, other things than the deck may have changed
0480         cardwidget->saveSettings(grp);
0481         grp.sync();
0482         if (global_debug > 0) qCDebug(LSKAT_LOG) << "NEW CARDDECK: " << front;
0483         bool change = false; // Avoid unnecessary changes
0484         if (!cardwidget->deckName().isEmpty() && cardwidget->deckName() != mCardTheme)
0485         {
0486             mCardTheme    = cardwidget->deckName();
0487             change = true;
0488         }
0489         if (change)
0490         {
0491             mTheme->updateCardTheme(mCardTheme);
0492             mView->update(); // Be on the safe side and update
0493         }
0494     }
0495     delete dlg;
0496 }
0497 
0498 // Clear all time statistics
0499 void Mainwindow::menuClearStatistics()
0500 {
0501     QString message;
0502     message = i18n("Do you really want to clear the all time "
0503                     "statistical data?");
0504 
0505 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0506     if (KMessageBox::PrimaryAction == KMessageBox::questionTwoActions(this,
0507 #else
0508     if (KMessageBox::Yes == KMessageBox::questionYesNo(this,
0509 #endif
0510                                                        message,
0511                                                        QString(),
0512                                                        KStandardGuiItem::clear(),
0513                                                        KStandardGuiItem::cancel()))
0514     {
0515         QHashIterator<int,Player *> it = mLSkatConfig->playerIterator();
0516         while (it.hasNext())
0517         {
0518             it.next();
0519             Player *player = it.value();
0520             player->clear();
0521         }
0522     }
0523 }
0524 
0525 // Abort a game
0526 void Mainwindow::menuEndGame()
0527 {
0528     if (mEngine)
0529     {
0530         mEngine->stopGame();
0531     }
0532 }
0533 
0534 // Start a new game
0535 void Mainwindow::menuNewLSkatGame()
0536 {
0537     disconnect(mView, &GameView::signalLeftMousePress, this, &Mainwindow::menuNewLSkatGame);
0538 
0539     Player *p1 = mLSkatConfig->player(0);
0540     Player *p2 = mLSkatConfig->player(1);
0541 
0542     // Stop running games
0543     if (mEngine)
0544     {
0545         mEngine->stopGame();
0546     }
0547 
0548     // Get rid of old stuff?
0549     if (true || (mGameMode != LSkat)) // Yes! Fixes bugs 330308 and 228067.
0550     // Jenkins objected to the indentation if the above was simply commented out.
0551     {
0552         // Set new game mode
0553         mGameMode = LSkat;
0554 
0555         // Start deleting
0556         delete mDisplay;
0557         delete mEngine;
0558 
0559         auto *display = new DisplayTwo(mDeck, mCanvas, mTheme, ADVANCE_PERIOD, mView);
0560         mDisplay = display;
0561         mEngine = new EngineTwo(this, mDeck, (DisplayTwo *)mDisplay);
0562         connect(mEngine, &AbstractEngine::signalGameOver, this, &Mainwindow::gameOver);
0563         connect(mEngine, &AbstractEngine::signalNextPlayer, this, &Mainwindow::nextPlayer);
0564 
0565         // Connect player score widget updates
0566         connect(p1, &Player::signalUpdate, display, &DisplayTwo::updatePlayer);
0567         connect(p2, &Player::signalUpdate, display, &DisplayTwo::updatePlayer);
0568 
0569         mEngine->addPlayer(0, p1);
0570         mEngine->addPlayer(1, p2);
0571     }// end if
0572 
0573     // Create inputs and store in player
0574     AbstractInput *input1 = createInput(mLSkatConfig->inputType(0), mDisplay, mEngine);
0575     p1->setInput(input1);
0576     AbstractInput *input2 = createInput(mLSkatConfig->inputType(1), mDisplay, mEngine);
0577     p2->setInput(input2);
0578 
0579     statusBar()->showMessage(i18n("Dealing cards..."));
0580 
0581     // Start game
0582     startGame();
0583 }
0584 
0585 // Change the player names in a dialog
0586 void Mainwindow::menuPlayerNames()
0587 {
0588     QPointer<NameDialogWidget> dlg = new NameDialogWidget(this);
0589     for (int i = 0; i < 2; i++)
0590     {
0591         Player *p = mLSkatConfig->player(i);
0592         dlg->setName(i, p->name());
0593     }
0594 
0595     if (dlg->exec() == QDialog::Accepted)
0596     {
0597         for (int i = 0; i < 2; i++)
0598         {
0599             Player *p = mLSkatConfig->player(i);
0600             p->setName(dlg->name(i));
0601         }
0602     }
0603 
0604     delete dlg;
0605 }
0606 
0607 // Set the start player.
0608 void Mainwindow::setStartPlayer(int no)
0609 {
0610     mStartPlayer = no;
0611     ((KSelectAction *)ACTION(QLatin1String("startplayer")))->setCurrentItem(mStartPlayer);
0612 }
0613 
0614 // Set the input type for a given player number.
0615 void Mainwindow::setInputType(int no, InputDeviceType type)
0616 {
0617     Player *p = nullptr;
0618     // Player 1
0619     if (no == 0)
0620     {
0621         ((KSelectAction *)ACTION(QLatin1String("player1")))->setCurrentItem((int)type);
0622         p = mLSkatConfig->player(0);
0623     }
0624     else if (no == 1)
0625     {
0626         ((KSelectAction *)ACTION(QLatin1String("player2")))->setCurrentItem((int)type);
0627         p = mLSkatConfig->player(1);
0628     }
0629 
0630     // Exchange player input at runtime
0631     if (mEngine && p && mDisplay && mEngine->isGameRunning())
0632     {
0633         AbstractInput *input = createInput(type, mDisplay, mEngine);
0634         p->setInput(input);
0635     }
0636 }
0637 
0638 #include "moc_mainwindow.cpp"