/*************************************************************************** INDI Driver ------------------- begin : Wed May 7th 2003 copyright : (C) 2001 by Jasem Mutlaq email : mutlaqja@ikarustech.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "indidriver.h" #include "indimenu.h" #include "indihostconf.h" #include "devicemanager.h" #include "indidevice.h" #include "indi/indicom.h" #include "kstars.h" #include "kstarsdata.h" #include "ksutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The dialog will by default be modeless, unless you set 'modal' to * TRUE to construct a modal dialog. */ INDIDriver::INDIDriver(TQWidget *parent) : devManager( parent ) { lastPort = 7263; lastGroup = NULL; lastDevice = NULL; ksw = (KStars *) parent; //FormLayout = makeVBoxMainWidget(); localListView->setSorting(-1); clientListView->setSorting(-1); KIconLoader *icons = KGlobal::iconLoader(); runningPix = icons->loadIcon( "exec", KIcon::Small); stopPix = icons->loadIcon( "button_cancel", KIcon::Small); localMode = icons->loadIcon( "network_local", KIcon::Small); serverMode = icons->loadIcon( "network", KIcon::Small); LocalpopMenu = new KPopupMenu(localListView); LocalpopMenu->insertItem( runningPix, i18n("Run Service") , 0); LocalpopMenu->insertItem( stopPix, i18n("Stop Service"), 1); localListView->setRootIsDecorated(true); connected = icons->loadIcon( "connect_established", KIcon::Small); disconnected = icons->loadIcon( "connect_no", KIcon::Small); establishConnection = icons->loadIcon( "connect_creating", KIcon::Small); ClientpopMenu = new KPopupMenu(clientListView); ClientpopMenu->insertItem( establishConnection, i18n("Connect") , 0); ClientpopMenu->insertItem( disconnected, i18n("Disconnect"), 1); for (uint i = 0; i < ksw->data()->INDIHostsList.count(); i++) { TQListViewItem *item = new TQListViewItem(clientListView, lastGroup); lastGroup = item; item->setPixmap(0, disconnected); item->setText(1, ksw->data()->INDIHostsList.at(i)->name); item->setText(2, ksw->data()->INDIHostsList.at(i)->portnumber); } lastGroup = NULL; TQObject::connect(addB, TQT_SIGNAL(clicked()), this, TQT_SLOT(addINDIHost())); TQObject::connect(modifyB, TQT_SIGNAL(clicked()), this, TQT_SLOT(modifyINDIHost())); TQObject::connect(removeB, TQT_SIGNAL(clicked()), this, TQT_SLOT(removeINDIHost())); TQObject::connect(clientListView, TQT_SIGNAL(rightButtonPressed ( TQListViewItem *, const TQPoint &, int )), this, TQT_SLOT(ClientprocessRightButton( TQListViewItem *, const TQPoint &, int ))); TQObject::connect(ClientpopMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(processHosttqStatus(int))); TQObject::connect(localListView, TQT_SIGNAL(rightButtonPressed ( TQListViewItem *, const TQPoint &, int )), this, TQT_SLOT(LocalprocessRightButton( TQListViewItem *, const TQPoint &, int ))); TQObject::connect(LocalpopMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(processDevicetqStatus(int))); TQObject::connect(ksw->getINDIMenu(), TQT_SIGNAL(driverDisconnected(int)), this, TQT_SLOT(shutdownHost(int))); TQObject::connect(connectHostB, TQT_SIGNAL(clicked()), this, TQT_SLOT(activateHostConnection())); TQObject::connect(disconnectHostB, TQT_SIGNAL(clicked()), this, TQT_SLOT(activateHostDisconnection())); TQObject::connect(runServiceB, TQT_SIGNAL(clicked()), this, TQT_SLOT(activateRunService())); TQObject::connect(stopServiceB, TQT_SIGNAL(clicked()), this, TQT_SLOT(activateStopService())); TQObject::connect(localListView, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(updateLocalButtons())); TQObject::connect(clientListView, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(updateClientButtons())); readXMLDriver(); resize( 500, 300); } void INDIDriver::shutdownHost(int mgrID) { TQListViewItem *affectedItem; for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++) { if (ksw->data()->INDIHostsList.at(i)->mgrID == mgrID) { affectedItem = clientListView->itemAtIndex(i); ksw->data()->INDIHostsList.at(i)->mgrID = -1; ksw->data()->INDIHostsList.at(i)->isConnected = false; affectedItem->setPixmap(0, disconnected); connectHostB->setEnabled(true); disconnectHostB->setEnabled(false); return; } } for (uint i=0; i < devices.size(); i++) { if (devices[i]->mgrID == mgrID) { affectedItem = localListView->findItem(devices[i]->label, 0); if (!affectedItem) return; affectedItem->setPixmap(1, stopPix); affectedItem->setPixmap(2, NULL); affectedItem->setText(4, TQString("")); runServiceB->setEnabled(true); stopServiceB->setEnabled(false); devices[i]->managed = false; devices[i]->restart(); return; } } } void INDIDriver::ClientprocessRightButton( TQListViewItem *item, const TQPoint &p, int column) { column = 0; if (item && item->childCount() == 0) ClientpopMenu->popup(p); } void INDIDriver::LocalprocessRightButton( TQListViewItem *item, const TQPoint &p, int column) { column = 0; if (item && item->childCount() == 0) LocalpopMenu->popup(p); } void INDIDriver::activateRunService() { processDevicetqStatus(0); } void INDIDriver::activateStopService() { processDevicetqStatus(1); } void INDIDriver::activateHostConnection() { processHosttqStatus(0); } void INDIDriver::activateHostDisconnection() { processHosttqStatus(1); } void INDIDriver::updateLocalButtons() { if (localListView->selectedItem() == NULL) return; for (uint i=0; i < devices.size(); i++) if (localListView->selectedItem()->text(0) == devices[i]->label) { runServiceB->setEnabled(devices[i]->state == 0); stopServiceB->setEnabled(devices[i]->state == 1); serverLogText->clear(); for ( TQStringList::Iterator it = devices[i]->serverBuffer.begin(); it != devices[i]->serverBuffer.end(); ++it ) serverLogText->insert(*it); break; } } void INDIDriver::updateClientButtons() { INDIHostsInfo *hostInfo; if (clientListView->currentItem() == NULL) return; for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++) { hostInfo = ksw->data()->INDIHostsList.at(i); if (clientListView->currentItem()->text(1) == hostInfo->name && clientListView->currentItem()->text(2) == hostInfo->portnumber) { connectHostB->setEnabled(!hostInfo->isConnected); disconnectHostB->setEnabled(hostInfo->isConnected); break; } } } void INDIDriver::processDevicetqStatus(int id) { if (localListView->selectedItem() == NULL) return; for (uint i=0; i < devices.size(); i++) if (localListView->selectedItem()->text(0) == devices[i]->label) { devices[i]->state = (id == 0) ? 1 : 0; if (devices[i]->state) { ksw->getINDIMenu()->setCustomLabel(devices[i]->label); devices[i]->label = ksw->getINDIMenu()->currentLabel; devices[i]->serverBuffer.clear(); if (!runDevice(devices[i])) { devices[i]->restart(); return; } if (devices[i]->mode == IDevice::M_LOCAL) { //Allow time for the INDI server to listen usleep(50000); if (!ksw->getINDIMenu()->processServer()) { devices[i]->restart(); return; } } localListView->selectedItem()->setPixmap(1, runningPix); localListView->selectedItem()->setText(4, TQString("%1").tqarg(devices[i]->indiPort)); runServiceB->setEnabled(false); stopServiceB->setEnabled(true); return; } if (devices[i]->mode == IDevice::M_LOCAL) ksw->getINDIMenu()->processServer(); localListView->selectedItem()->setPixmap(1, stopPix); localListView->selectedItem()->setPixmap(2, NULL); localListView->selectedItem()->setText(4, TQString("")); runServiceB->setEnabled(true); stopServiceB->setEnabled(false); devices[i]->restart(); updateMenuActions(); return; } } void INDIDriver::processHosttqStatus(int id) { int mgrID; bool toConnect = (id == 0); TQListViewItem *currentItem = clientListView->selectedItem(); if (!currentItem) return; INDIHostsInfo *hostInfo; for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++) { hostInfo = ksw->data()->INDIHostsList.at(i); if (currentItem->text(1) == hostInfo->name && currentItem->text(2) == hostInfo->portnumber) { // Nothing changed, return if (hostInfo->isConnected == toConnect) return; // connect to host if (toConnect) { // if connection successful if ( (mgrID = ksw->getINDIMenu()->processClient(hostInfo->hostname, hostInfo->portnumber)) >= 0) { currentItem->setPixmap(0, connected); hostInfo->isConnected = true; hostInfo->mgrID = mgrID; connectHostB->setEnabled(false); disconnectHostB->setEnabled(true); } } else { ksw->getINDIMenu()->removeDeviceMgr(hostInfo->mgrID); hostInfo->mgrID = mgrID = -1; hostInfo->isConnected = false; currentItem->setPixmap(0, disconnected); connectHostB->setEnabled(true); disconnectHostB->setEnabled(false); updateMenuActions(); } } } } void INDIDriver::updateMenuActions() { // We iterate over devices, we enable INDI Control Panel if we have any active device // We enable capture image sequence if we have any imaging device KAction *tmpAction; INDIMenu *devMenu = ksw->getINDIMenu(); bool activeDevice = false; bool activeImaging = false; INDI_P *imgProp = NULL; if (devMenu == NULL) return; if (devMenu->mgr.count() > 0) activeDevice = true; for (uint i=0; i < devMenu->mgr.count(); i++) { for (uint j=0; j < devMenu->mgr.at(i)->indi_dev.count(); j++) { imgProp = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("CCD_EXPOSE_DURATION"); if (imgProp && devMenu->mgr.at(i)->indi_dev.at(j)->isOn()) { activeImaging = true; break; } } } tmpAction = ksw->actionCollection()->action("indi_control_panel"); if (!tmpAction) kdDebug() << "Warning: indi_control_panel action not found" << endl; else tmpAction->setEnabled(activeDevice); tmpAction = ksw->actionCollection()->action("capture_sequence"); if (!tmpAction) kdDebug() << "Warning: capture_sequence action not found" << endl; else tmpAction->setEnabled(activeImaging); } bool INDIDriver::runDevice(IDevice *dev) { dev->indiPort = getINDIPort(); if (dev->indiPort < 0) { KMessageBox::error(0, i18n("Cannot start INDI server: port error.")); return false; } dev->proc = new KProcess; *dev->proc << "indiserver"; *dev->proc << "-v" << "-r" << "0" << "-p" << TQString("%1").tqarg(dev->indiPort) << dev->driver; // Check Mode dev->mode = localR->isChecked() ? IDevice::M_LOCAL : IDevice::M_SERVER; if (dev->mode == IDevice::M_LOCAL) localListView->selectedItem()->setPixmap(2, localMode); else localListView->selectedItem()->setPixmap(2, serverMode); connect(dev->proc, TQT_SIGNAL(receivedStderr (KProcess *, char *, int)), dev, TQT_SLOT(processstd(KProcess *, char*, int))); dev->proc->start(KProcess::NotifyOnExit, KProcess::Stderr); //dev->proc->start(); return (dev->proc->isRunning()); } void INDIDriver::removeDevice(IDevice *dev) { for (unsigned int i=0 ; i < devices.size(); i++) if (dev->label == devices[i]->label) devices[i]->restart(); } void INDIDriver::removeDevice(TQString deviceLabel) { for (unsigned int i=0 ; i < devices.size(); i++) if (deviceLabel == devices[i]->label) devices[i]->restart(); } void INDIDriver::saveDevicesToDisk() { TQFile file; TQString elementData; file.setName( locateLocal( "appdata", "drivers.xml" ) ); //determine filename in local user KDE directory tree. if ( !file.open( IO_WriteOnly)) { TQString message = i18n( "unable to write to file 'drivers.xml'\nAny changes to INDI device drivers will not be saved." ); KMessageBox::sorry( 0, message, i18n( "Could Not Open File" ) ); return; } TQTextStream outstream(&file); // Let's write drivers first outstream << "" << endl; for (unsigned int i=0; i < driversList.count(); i++) outstream << " " << driversList[i] << "" << endl; outstream << "" << endl; // Next we write devices, in the following order: // Telescopes, CCDs, Filter Wheels, Video, Dome, GPS // #1 Telescopes outstream << "" << endl; for (unsigned i=0; i < devices.size(); i++) { if (devices[i]->deviceType == KSTARS_TELESCOPE) { outstream << TQString("").tqarg(devices[i]->label).tqarg(devices[i]->focal_length > 0 ? devices[i]->focal_length : -1).tqarg(devices[i]->aperture > 0 ? devices[i]->aperture : -1) << endl; outstream << " " << devices[i]->driver << "" << endl; outstream << " " << devices[i]->version << "" << endl; outstream << "" << endl; } } outstream << "" << endl; // #2 CCDs outstream << "" << endl; for (unsigned i=0; i < devices.size(); i++) { if (devices[i]->deviceType == KSTARS_CCD) { outstream << TQString("").tqarg(devices[i]->label) << endl; outstream << " " << devices[i]->driver << "" << endl; outstream << " " << devices[i]->version << "" << endl; outstream << "" << endl; } } outstream << "" << endl; // #3 Filter wheels outstream << "" << endl; for (unsigned i=0; i < devices.size(); i++) { if (devices[i]->deviceType == KSTARS_FILTER) { outstream << TQString("").tqarg(devices[i]->label) << endl; outstream << " " << devices[i]->driver << "" << endl; outstream << " " << devices[i]->version << "" << endl; outstream << "" << endl; } } outstream << "" << endl; // #4 Video outstream << "" << endl; for (unsigned i=0; i < devices.size(); i++) { if (devices[i]->deviceType == KSTARS_VIDEO) { outstream << TQString("").tqarg(devices[i]->label) << endl; outstream << " " << devices[i]->driver << "" << endl; outstream << " " << devices[i]->version << "" << endl; outstream << "" << endl; } } outstream << "" << endl; file.close(); } bool INDIDriver::isDeviceRunning(TQString deviceLabel) { for (unsigned int i=0 ; i < devices.size(); i++) if (deviceLabel == devices[i]->label) { if (!devices[i]->proc) return false; else return (devices[i]->proc->isRunning()); } return false; } int INDIDriver::getINDIPort() { lastPort+=5; KExtendedSocket ks(TQString(), lastPort, KExtendedSocket::passiveSocket | KExtendedSocket::noResolve); for (uint i=0 ; i < 10; i++) { if (ks.listen() < 0) { lastPort+=5; ks.setPort(lastPort); } else return lastPort; } return -1; } bool INDIDriver::readXMLDriver() { TQString indiFile("drivers.xml"); TQFile file; char errmsg[1024]; if ( !KSUtils::openDataFile( file, indiFile ) ) { KMessageBox::error(0, i18n("Unable to find device driver file 'drivers.xml'. Please locate the file and place it in one of the following locations:\n\n \t$(TDEDIR)/share/apps/kstars/%1 \n\t~/.kde/share/apps/kstars/%1")); return false; } signed char c; LilXML *xmlParser = newLilXML(); XMLEle *root = NULL; while ( (c = (signed char) file.getch()) != -1) { root = readXMLEle(xmlParser, c, errmsg); if (root) { if (!buildDeviceGroup(root, errmsg)) prXMLEle(stderr, root, 0); delXMLEle(root); } else if (errmsg[0]) { kdDebug() << TQString(errmsg); return false; } } delLilXML(xmlParser); return true; } bool INDIDriver::buildDriversList( XMLEle *root, char* /*errmsg[]*/) { XMLEle *ep; for (ep = nextXMLEle (root, 1); ep != NULL; ep = nextXMLEle (root, 0)) driversList << pcdataXMLEle(ep); return true; } bool INDIDriver::buildDeviceGroup(XMLEle *root, char errmsg[]) { XMLAtt *ap; XMLEle *ep; TQString groupName; int groupType = KSTARS_TELESCOPE; if (!strcmp(tagXMLEle(root), "ScopeDrivers")) return buildDriversList(root, errmsg); // avoid overflow if (strlen(tagXMLEle(root)) > 1024) return false; // Get device grouping name ap = findXMLAtt(root, "group"); if (!ap) { snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a group attribute", tagXMLEle(root)); return false; } groupName = valuXMLAtt(ap); if (groupName.find("Telescopes") != -1) groupType = KSTARS_TELESCOPE; else if (groupName.find("CCDs") != -1) groupType = KSTARS_CCD; else if (groupName.find("Filter") != -1) groupType = KSTARS_FILTER; else if (groupName.find("Video") != -1) groupType = KSTARS_VIDEO; else if (groupName.find("Focusers") != -1) groupType = KSTARS_FOCUSER; else if (groupName.find("Domes") != -1) groupType = KSTARS_DOME; else if (groupName.find("GPS") != -1) groupType = KSTARS_GPS; //KListViewItem *group = new KListViewItem(topItem, lastGroup); TQListViewItem *group = new TQListViewItem(localListView, lastGroup); group->setText(0, groupName); lastGroup = group; //group->setOpen(true); for (ep = nextXMLEle(root, 1) ; ep != NULL ; ep = nextXMLEle(root, 0)) /*for (int i = 0; i < root->nel; i++)*/ if (!buildDriverElement(ep, group, groupType, errmsg)) return false; return true; } bool INDIDriver::buildDriverElement(XMLEle *root, TQListViewItem *DGroup, int groupType, char errmsg[]) { XMLAtt *ap; XMLEle *el; IDevice *dv; TQString label; TQString driver; TQString version; double focal_length (-1), aperture (-1); ap = findXMLAtt(root, "label"); if (!ap) { snprintf(errmsg, ERRMSG_SIZE, "Tag %.64s does not have a label attribute", tagXMLEle(root)); return false; } label = valuXMLAtt(ap); // Let's look for telescope-specfic attributes: focal length and aperture ap = findXMLAtt(root, "focal_length"); if (ap) focal_length = TQString(valuXMLAtt(ap)).toDouble(); ap = findXMLAtt(root, "aperture"); if (ap) aperture = TQString(valuXMLAtt(ap)).toDouble(); el = findXMLEle(root, "driver"); if (!el) return false; driver = pcdataXMLEle(el); el = findXMLEle(root, "version"); if (!el) return false; version = pcdataXMLEle(el); TQListViewItem *device = new TQListViewItem(DGroup, lastDevice); device->setText(0, TQString(label)); device->setPixmap(1, stopPix); device->setText(3, TQString(version)); lastDevice = device; dv = new IDevice(label, driver, version); dv->deviceType = groupType; connect(dv, TQT_SIGNAL(newServerInput()), this, TQT_SLOT(updateLocalButtons())); if (focal_length > 0) dv->focal_length = focal_length; if (aperture > 0) dv->aperture = aperture; devices.push_back(dv); // SLOTS/SIGNAL, pop menu, indi server logic return true; } int INDIDriver::activeDriverCount() { int count = 0; for (uint i=0; i < devices.size(); i++) if (devices[i]->state && devices[i]->mode == IDevice::M_LOCAL) count++; for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++) if (ksw->data()->INDIHostsList.at(i)->isConnected) count++; return count; } void INDIDriver::addINDIHost() { INDIHostConf hostConf(this); hostConf.setCaption(i18n("Add Host")); bool portOk = false; if (hostConf.exec() == TQDialog::Accepted) { INDIHostsInfo *hostItem = new INDIHostsInfo; hostItem->name = hostConf.nameIN->text(); hostItem->hostname = hostConf.hostname->text(); hostItem->portnumber = hostConf.portnumber->text(); hostItem->isConnected = false; hostItem->mgrID = -1; hostItem->portnumber.toInt(&portOk); if (portOk == false) { KMessageBox::error(0, i18n("Error: the port number is invalid.")); return; } //search for duplicates for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++) if (hostItem->name == ksw->data()->INDIHostsList.at(i)->name && hostItem->portnumber == ksw->data()->INDIHostsList.at(i)->portnumber) { KMessageBox::error(0, i18n("Host: %1 Port: %2 already exists.").tqarg(hostItem->name).tqarg(hostItem->portnumber)); return; } ksw->data()->INDIHostsList.append(hostItem); TQListViewItem *item = new TQListViewItem(clientListView); item->setPixmap(0, disconnected); item->setText(1, hostConf.nameIN->text()); item->setText(2, hostConf.portnumber->text()); } saveHosts(); } void INDIDriver::modifyINDIHost() { INDIHostConf hostConf(this); hostConf.setCaption(i18n("Modify Host")); TQListViewItem *currentItem = clientListView->currentItem(); if (currentItem == NULL) return; for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++) if (currentItem->text(1) == ksw->data()->INDIHostsList.at(i)->name && currentItem->text(2) == ksw->data()->INDIHostsList.at(i)->portnumber) { hostConf.nameIN->setText(ksw->data()->INDIHostsList.at(i)->name); hostConf.hostname->setText(ksw->data()->INDIHostsList.at(i)->hostname); hostConf.portnumber->setText(ksw->data()->INDIHostsList.at(i)->portnumber); if (hostConf.exec() == TQDialog::Accepted) { INDIHostsInfo *hostItem = new INDIHostsInfo; hostItem->name = hostConf.nameIN->text(); hostItem->hostname = hostConf.hostname->text(); hostItem->portnumber = hostConf.portnumber->text(); currentItem->setText(1, hostConf.nameIN->text()); currentItem->setText(2, hostConf.portnumber->text()); ksw->data()->INDIHostsList.replace(clientListView->itemIndex(currentItem), hostItem); saveHosts(); } } } void INDIDriver::removeINDIHost() { if (clientListView->currentItem() == NULL) return; for (uint i=0; i < ksw->data()->INDIHostsList.count(); i++) if (clientListView->currentItem()->text(1) == ksw->data()->INDIHostsList.at(i)->name && clientListView->currentItem()->text(2) == ksw->data()->INDIHostsList.at(i)->portnumber) { if (ksw->data()->INDIHostsList.at(i)->isConnected) { KMessageBox::error( 0, i18n("You need to disconnect the client before removing it.")); return; } if (KMessageBox::warningContinueCancel( 0, i18n("Are you sure you want to remove the %1 client?").tqarg(clientListView->currentItem()->text(1)), i18n("Delete Confirmation"),KStdGuiItem::del())!=KMessageBox::Continue) return; ksw->data()->INDIHostsList.remove(i); clientListView->takeItem(clientListView->currentItem()); break; } saveHosts(); } void INDIDriver::saveHosts() { TQFile file; TQString hostData; file.setName( locateLocal( "appdata", "indihosts.xml" ) ); //determine filename in local user KDE directory tree. if ( !file.open( IO_WriteOnly)) { TQString message = i18n( "unable to write to file 'indihosts.xml'\nAny changes to INDI hosts configurations will not be saved." ); KMessageBox::sorry( 0, message, i18n( "Could Not Open File" ) ); return; } TQTextStream outstream(&file); for (uint i= 0; i < ksw->data()->INDIHostsList.count(); i++) { hostData = "\n"; outstream << hostData; } file.close(); } INDIDriver::~INDIDriver() { for (uint i=0; i < devices.size(); i++) delete (devices[i]); } IDevice::IDevice(TQString inLabel, TQString inDriver, TQString inVersion) { label = inLabel;; driver = inDriver;; version = inVersion; // Initially off state = 0; // No port initially indiPort = -1; // not yet managed by DeviceManager managed = false; mgrID = -1; focal_length = -1; aperture = -1; proc = NULL; } void IDevice::processstd(KProcess* /*proc*/, char* buffer, int /*buflen*/) { serverBuffer.append(buffer); emit newServerInput(); } IDevice::~IDevice() { if (proc) proc->kill(); } void IDevice::restart() { mgrID = -1; state = 0; indiPort = -1; if (proc) proc->kill(); proc = NULL; } #include "indidriver.moc"