diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index bf1d74f2..4d68eff1 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -44,7 +44,9 @@ from debug import logger # Helper Functions import helper_bootstrap import helper_generic - + +# singleton lock instance +thisapp = None def connectToStream(streamNumber): shared.streamsInWhichIAmParticipating[streamNumber] = 'no data' @@ -139,11 +141,13 @@ if shared.useVeryEasyProofOfWorkForTesting: class Main: def start(self, daemon=False): + global thisapp + _fixWinsock() shared.daemon = daemon # is the application already running? If yes then exit. - thisapp = singleton.singleinstance() + thisapp = singleton.singleinstance("", daemon) # get curses flag curses = False diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 9ac08d46..64a8d0bf 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -10,6 +10,7 @@ try: from PyQt4 import QtCore, QtGui from PyQt4.QtCore import * from PyQt4.QtGui import * + from PyQt4.QtNetwork import QLocalSocket, QLocalServer except Exception as err: print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download it from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\' (without quotes).' @@ -4483,8 +4484,70 @@ class UISignaler(QThread): sys.stderr.write( 'Command sent to UISignaler not recognized: %s\n' % command) + +app = None +myapp = None + +class MySingleApplication(QApplication): + """ + Listener to allow our Qt form to get focus when another instance of the + application is open. + + Based off this nice reimplmentation of MySingleApplication: + http://stackoverflow.com/a/12712362/2679626 + """ + + # Unique identifier for this application + uuid = '6ec0149b-96e1-4be1-93ab-1465fb3ebf7c' + + def __init__(self, *argv): + super(MySingleApplication, self).__init__(*argv) + id = MySingleApplication.uuid + + self.server = None + self.is_running = False + + socket = QLocalSocket() + socket.connectToServer(id) + self.is_running = socket.waitForConnected() + + # Cleanup past crashed servers + if not self.is_running: + if socket.error() == QLocalSocket.ConnectionRefusedError: + socket.disconnectFromServer() + QLocalServer.removeServer(id) + + socket.abort() + + # Checks if there's an instance of the local server id running + if self.is_running: + # This should be ignored, singleton.py will take care of exiting me. + pass + else: + # Nope, create a local server with this id and assign on_new_connection + # for whenever a second instance tries to run focus the application. + self.server = QLocalServer() + self.server.listen(id) + self.server.newConnection.connect(self.on_new_connection) + + def __del__(self): + if self.server: + self.server.close() + + def on_new_connection(self): + global myapp + if myapp: + myapp.appIndicatorShow() + +def init(): + global app + if not app: + app = MySingleApplication(sys.argv) + return app + def run(): - app = QtGui.QApplication(sys.argv) + global myapp + app = init() change_translation(l10n.getTranslationLanguage()) app.setStyleSheet("QStatusBar::item { border: 0px solid black }") myapp = MyForm() diff --git a/src/singleton.py b/src/singleton.py index ee5c3077..f3124424 100644 --- a/src/singleton.py +++ b/src/singleton.py @@ -3,22 +3,26 @@ import sys import os import errno -import tempfile +import shared from multiprocessing import Process - class singleinstance: """ - Implements a single instance application by creating a lock file based on the full path to the script file. + Implements a single instance application by creating a lock file at appdata. This is based upon the singleton class from tendo https://github.com/pycontribs/tendo which is under the Python Software Foundation License version 2 """ - def __init__(self, flavor_id=""): + def __init__(self, flavor_id="", daemon=False): import sys self.initialized = False - basename = os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace("/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock' - self.lockfile = os.path.normpath(tempfile.gettempdir() + '/' + basename) + self.daemon = daemon; + self.lockfile = os.path.normpath(os.path.join(shared.appdata, 'singleton%s.lock' % flavor_id)) + + if not self.daemon: + # Tells the already running (if any) application to get focus. + import bitmessageqt + bitmessageqt.init() if sys.platform == 'win32': try: