diff options
author | Michele Calgaro <[email protected]> | 2024-11-05 12:24:20 +0900 |
---|---|---|
committer | Michele Calgaro <[email protected]> | 2024-11-19 22:26:14 +0900 |
commit | 1711870a073597c2282f32ad39a9b8ad5e1cf6bb (patch) | |
tree | 1fb9f1d9ad4ec650960add05c3dd287cd42c7d61 /tdecore/tdeuniqueapplication.cpp | |
parent | 87ad1e6c4d56fb52faa0d1af4afe5c33f05e6dca (diff) | |
download | tdelibs-1711870a073597c2282f32ad39a9b8ad5e1cf6bb.tar.gz tdelibs-1711870a073597c2282f32ad39a9b8ad5e1cf6bb.zip |
Rename KUniqueApplication to TDEUniqueApplication and remove obsolete kapp.h and kuniqueapp.h headers
Signed-off-by: Michele Calgaro <[email protected]>
Diffstat (limited to 'tdecore/tdeuniqueapplication.cpp')
-rw-r--r-- | tdecore/tdeuniqueapplication.cpp | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/tdecore/tdeuniqueapplication.cpp b/tdecore/tdeuniqueapplication.cpp new file mode 100644 index 000000000..7db9f528e --- /dev/null +++ b/tdecore/tdeuniqueapplication.cpp @@ -0,0 +1,505 @@ +/* This file is part of the KDE libraries + Copyright (c) 1999 Preston Brown <[email protected]> + + $Id$ + + 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 Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <config.h> + +#include <sys/types.h> +#include <sys/wait.h> + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include <tqfile.h> +#include <tqptrlist.h> +#include <tqtimer.h> + +#include <dcopclient.h> +#include <tdecmdlineargs.h> +#include <kstandarddirs.h> +#include <tdeaboutdata.h> + +#if defined TQ_WS_X11 +#include <twin.h> +#include <tdestartupinfo.h> +#endif + +#include <tdeconfig.h> +#include "kdebug.h" +#include "tdeuniqueapplication.h" + +#if defined TQ_WS_X11 +#include <netwm.h> +#include <X11/Xlib.h> +#define DISPLAY "DISPLAY" +#else +# ifdef TQ_WS_QWS +# define DISPLAY "QWS_DISPLAY" +# else +# define DISPLAY "DISPLAY" +# endif +#endif + +bool TDEUniqueApplication::s_nofork = false; +bool TDEUniqueApplication::s_multipleInstances = false; +bool TDEUniqueApplication::s_uniqueTestDone = false; +bool TDEUniqueApplication::s_handleAutoStarted = false; + +static TDECmdLineOptions tdeunique_options[] = +{ + { "nofork", "Don't run in the background.", 0 }, + TDECmdLineLastOption +}; + +struct DCOPRequest { + TQCString fun; + TQByteArray data; + DCOPClientTransaction *transaction; +}; + +class TDEUniqueApplicationPrivate { +public: + TQPtrList <DCOPRequest> requestList; + bool processingRequest; + bool firstInstance; +}; + +void +TDEUniqueApplication::addCmdLineOptions() +{ + TDECmdLineArgs::addCmdLineOptions(tdeunique_options, 0, "tdeuniqueapp", "tde" ); +} + +bool +TDEUniqueApplication::start() +{ + if( s_uniqueTestDone ) + return true; + s_uniqueTestDone = true; + addCmdLineOptions(); // Make sure to add cmd line options +#ifdef TQ_WS_WIN + s_nofork = true; +#else + TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs("tdeuniqueapp"); + s_nofork = !args->isSet("fork"); + delete args; +#endif + + TQCString appName = TDECmdLineArgs::about->appName(); + + if (s_nofork) + { + if (s_multipleInstances) + { + TQCString pid; + pid.setNum(getpid()); + appName = appName + "-" + pid; + } + + // Check to make sure that we're actually able to register with the DCOP + // server. + +#ifndef TQ_WS_WIN //TODO + if(dcopClient()->registerAs(appName, false).isEmpty()) { + startKdeinit(); + if(dcopClient()->registerAs(appName, false).isEmpty()) { + kdError() << "TDEUniqueApplication: Can't setup DCOP communication." << endl; + ::exit(255); + } + } +#endif + + // We'll call newInstance in the constructor. Do nothing here. + return true; + } + DCOPClient *dc; + int fd[2]; + signed char result; + if (0 > pipe(fd)) + { + kdError() << "TDEUniqueApplication: pipe() failed!" << endl; + ::exit(255); + } + int fork_result = fork(); + switch(fork_result) { + case -1: + kdError() << "TDEUniqueApplication: fork() failed!" << endl; + ::exit(255); + break; + case 0: + // Child + ::close(fd[0]); + if (s_multipleInstances) + appName.append("-").append(TQCString().setNum(getpid())); + dc = dcopClient(); + { + TQCString regName = dc->registerAs(appName, false); + if (regName.isEmpty()) + { + // Check DISPLAY + if (TQCString(getenv(DISPLAY)).isEmpty()) + { + kdError() << "TDEUniqueApplication: Can't determine DISPLAY. Aborting." << endl; + result = -1; // Error + ::write(fd[1], &result, 1); + ::exit(255); + } + + // Try to launch tdeinit. + startKdeinit(); + regName = dc->registerAs(appName, false); + if (regName.isEmpty()) + { + kdError() << "TDEUniqueApplication: Can't setup DCOP communication." << endl; + result = -1; + delete dc; // Clean up DCOP commmunication + ::write(fd[1], &result, 1); + ::exit(255); + } + } + if (regName != appName) + { + // Already running. Ok. + result = 0; + delete dc; // Clean up DCOP commmunication + ::write(fd[1], &result, 1); + ::close(fd[1]); +#if 0 +#ifdef TQ_WS_X11 + // say we're up and running ( probably no new window will appear ) + TDEStartupInfoId id; + if( kapp != NULL ) // TDEApplication constructor unsets the env. variable + id.initId( kapp->startupId()); + else + id = TDEStartupInfo::currentStartupIdEnv(); + if( !id.none()) + { + Display* disp = XOpenDisplay( NULL ); + if( disp != NULL ) // use extra X connection + { + TDEStartupInfo::sendFinishX( disp, id ); + XCloseDisplay( disp ); + } + } +#else //FIXME(E): implement +#endif +#endif + return false; + } + dc->setPriorityCall(true); + } + + { +#ifdef TQ_WS_X11 + TDEStartupInfoId id; + if( kapp != NULL ) // TDEApplication constructor unsets the env. variable + id.initId( kapp->startupId()); + else + id = TDEStartupInfo::currentStartupIdEnv(); + if( !id.none()) + { // notice about pid change + Display* disp = XOpenDisplay( NULL ); + if( disp != NULL ) // use extra X connection + { + TDEStartupInfoData data; + data.addPid( getpid()); + TDEStartupInfo::sendChangeX( disp, id, data ); + XCloseDisplay( disp ); + } + } +#else //FIXME(E): Implement +#endif + } + result = 0; + ::write(fd[1], &result, 1); + ::close(fd[1]); + return true; // Finished. + default: + // Parent +// DCOPClient::emergencyClose(); +// dcopClient()->detach(); + if (s_multipleInstances) + appName.append("-").append(TQCString().setNum(fork_result)); + ::close(fd[1]); + for(;;) + { + int n = ::read(fd[0], &result, 1); + if (n == 1) break; + if (n == 0) + { + kdError() << "TDEUniqueApplication: Pipe closed unexpectedly." << endl; + ::exit(255); + } + if (errno != EINTR) + { + kdError() << "TDEUniqueApplication: Error reading from pipe." << endl; + ::exit(255); + } + } + ::close(fd[0]); + + if (result != 0) + ::exit(result); // Error occurred in child. + + dc = new DCOPClient(); + if (!dc->attach()) + { + kdError() << "TDEUniqueApplication: Parent process can't attach to DCOP." << endl; + delete dc; // Clean up DCOP commmunication + ::exit(255); + } + if (!dc->isApplicationRegistered(appName)) { + kdError() << "TDEUniqueApplication: Registering failed!" << endl; + } + + TQCString new_asn_id; +#if defined TQ_WS_X11 + TDEStartupInfoId id; + if( kapp != NULL ) // TDEApplication constructor unsets the env. variable + id.initId( kapp->startupId()); + else + id = TDEStartupInfo::currentStartupIdEnv(); + if( !id.none()) + new_asn_id = id.id(); +#endif + + TQByteArray data, reply; + TQDataStream ds(data, IO_WriteOnly); + + TDECmdLineArgs::saveAppArgs(ds); + ds << new_asn_id; + + dc->setPriorityCall(true); + TQCString replyType; + if (!dc->call(appName, TDECmdLineArgs::about->appName(), "newInstance()", data, replyType, reply)) + { + kdError() << "Communication problem with " << TDECmdLineArgs::about->appName() << ", it probably crashed." << endl; + delete dc; // Clean up DCOP commmunication + ::exit(255); + } + dc->setPriorityCall(false); + if (replyType != "int") + { + kdError() << "TDEUniqueApplication: DCOP communication error!" << endl; + delete dc; // Clean up DCOP commmunication + ::exit(255); + } + TQDataStream rs(reply, IO_ReadOnly); + int exitCode; + rs >> exitCode; + delete dc; // Clean up DCOP commmunication + ::exit(exitCode); + break; + } + return false; // make insure++ happy +} + + +TDEUniqueApplication::TDEUniqueApplication(bool allowStyles, bool GUIenabled, bool configUnique) + : TDEApplication( allowStyles, GUIenabled, initHack( configUnique )), + DCOPObject(TDECmdLineArgs::about->appName()) +{ + d = new TDEUniqueApplicationPrivate; + d->processingRequest = false; + d->firstInstance = true; + + if (s_nofork) { + // Can't call newInstance directly from the constructor since it's virtual... + TQTimer::singleShot( 0, this, TQ_SLOT(newInstanceNoFork()) ); + } + else { + // Force to handle DCOP requests (newInstance call) + TQTimer::singleShot( 0, this, TQ_SLOT(processDelayed())); + } +} + + +#ifdef TQ_WS_X11 +TDEUniqueApplication::TDEUniqueApplication(Display *display, TQt::HANDLE visual, + TQt::HANDLE colormap, bool allowStyles, bool configUnique) + : TDEApplication( display, visual, colormap, allowStyles, initHack( configUnique )), + DCOPObject(TDECmdLineArgs::about->appName()) +{ + d = new TDEUniqueApplicationPrivate; + d->processingRequest = false; + d->firstInstance = true; + + if (s_nofork) { + // Can't call newInstance directly from the constructor since it's virtual... + TQTimer::singleShot( 0, this, TQ_SLOT(newInstanceNoFork()) ); + } + else { + // Force to handle DCOP requests (newInstance call) + TQTimer::singleShot( 0, this, TQ_SLOT(processDelayed())); + } +} +#endif + + +TDEUniqueApplication::~TDEUniqueApplication() +{ + delete d; +} + +// this gets called before even entering TQApplication::TQApplication() +TDEInstance* TDEUniqueApplication::initHack( bool configUnique ) +{ + TDEInstance* inst = new TDEInstance( TDECmdLineArgs::about ); + if (configUnique) + { + TDEConfigGroupSaver saver( inst->config(), "KDE" ); + s_multipleInstances = inst->config()->readBoolEntry("MultipleInstances", false); + } + if( !start()) + // Already running + ::exit( 0 ); + return inst; +} + +void TDEUniqueApplication::newInstanceNoFork() +{ + if (dcopClient()->isSuspended()) + { + // Try again later. + TQTimer::singleShot( 200, this, TQ_SLOT(newInstanceNoFork()) ); + return; + } + + s_handleAutoStarted = false; + newInstance(); + d->firstInstance = false; +#if defined TQ_WS_X11 + // KDE4 remove + // A hack to make startup notification stop for apps which override newInstance() + // and reuse an already existing window there, but use KWin::activateWindow() + // instead of TDEStartupInfo::setNewStartupId(). Therefore KWin::activateWindow() + // for now sets this flag. Automatically ending startup notification always + // would cause problem if the new window would show up with a small delay. + if( s_handleAutoStarted ) + TDEStartupInfo::handleAutoAppStartedSending(); +#endif + // What to do with the return value ? +} + +bool TDEUniqueApplication::process(const TQCString &fun, const TQByteArray &data, + TQCString &replyType, TQByteArray &replyData) +{ + if (fun == "newInstance()") + { + delayRequest(fun, data); + return true; + } else + return DCOPObject::process(fun, data, replyType, replyData); +} + +void +TDEUniqueApplication::delayRequest(const TQCString &fun, const TQByteArray &data) +{ + DCOPRequest *request = new DCOPRequest; + request->fun = fun; + request->data = data; + request->transaction = dcopClient()->beginTransaction(); + d->requestList.append(request); + if (!d->processingRequest) + { + TQTimer::singleShot(0, this, TQ_SLOT(processDelayed())); + } +} + +void +TDEUniqueApplication::processDelayed() +{ + if (dcopClient()->isSuspended()) + { + // Try again later. + TQTimer::singleShot( 200, this, TQ_SLOT(processDelayed())); + return; + } + d->processingRequest = true; + while( !d->requestList.isEmpty() ) + { + DCOPRequest *request = d->requestList.take(0); + TQByteArray replyData; + TQCString replyType; + if (request->fun == "newInstance()") { + dcopClient()->setPriorityCall(false); + TQDataStream ds(request->data, IO_ReadOnly); + TDECmdLineArgs::loadAppArgs(ds); + if( !ds.atEnd()) // backwards compatibility + { + TQCString asn_id; + ds >> asn_id; + setStartupId( asn_id ); + } + s_handleAutoStarted = false; + int exitCode = newInstance(); + d->firstInstance = false; +#if defined TQ_WS_X11 + if( s_handleAutoStarted ) + TDEStartupInfo::handleAutoAppStartedSending(); // KDE4 remove? +#endif + TQDataStream rs(replyData, IO_WriteOnly); + rs << exitCode; + replyType = "int"; + } + dcopClient()->endTransaction( request->transaction, replyType, replyData); + delete request; + } + + d->processingRequest = false; +} + +bool TDEUniqueApplication::restoringSession() +{ + return d->firstInstance && isRestored(); +} + +int TDEUniqueApplication::newInstance() +{ + if (!d->firstInstance) + { + + if ( mainWidget() ) + { + mainWidget()->show(); +#if defined TQ_WS_X11 + // This is the line that handles window activation if necessary, + // and what's important, it does it properly. If you reimplement newInstance(), + // and don't call the inherited one, use this (but NOT when newInstance() + // is called for the first time, like here). + TDEStartupInfo::setNewStartupId( mainWidget(), kapp->startupId()); +#endif + } + } + return 0; // do nothing in default implementation +} + +void TDEUniqueApplication::setHandleAutoStarted() +{ + s_handleAutoStarted = false; +} + +void TDEUniqueApplication::virtual_hook( int id, void* data ) +{ TDEApplication::virtual_hook( id, data ); + DCOPObject::virtual_hook( id, data ); } + +#include "tdeuniqueapplication.moc" |