Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling Mac OS X file open event BEFORE C++ main() executes

I've googled a lot, but still unable to find nice solution :/
So, i'm porting one complicated Qt5 application (client of some network service) to Mac OS X (10.7.0 "Lion" and higher).

I need to handle custom file like *.xyz and custom URL scheme like xyz://.
Okay, Qt5 has the QFileOpenEvent class to handle OS X appropriate event.
BUT: this event arrives only after the application event loop starts (obviously)!

And i need to "catch" and handle OS X' file open event BEFORE main starts, because the program logic was designed to take care of the command line argument handling only.

The simplified main function code:

int main(int argc, char[]* argv)
{
     QApplication app( argc, argv );

     QStringList arguments = app.arguments();
     if( arguments.count() == argc ) arguments.removeFirst();

     Logic appLogic( NULL, &app );
     app.installMessageHandler( &appLogic );

     // The problem:
     // **This function will always called earlier than the any event**
     if( ! appLogic.start( arguments ) ) return 0;

     // Start processing of events
     // Only after this call Logic class get the desired event
     return app.exec();
}

Is there a way to get an OS X' file opening event before the C++ main function starts, or to get "my" file/url in the argv parameter?
Maybe, some Objective-C black magic does the job?

NOTE: start does many complicated - and asynchronous - things. Event arrives during it's execution, so it's hard to handle it when the async stuff is already working. So looks like i just need to prevent start execution, if the event will arrive.

If the application is already opened, there is no problem.

like image 687
eraxillan Avatar asked Apr 16 '15 09:04

eraxillan


1 Answers

I've found one, propably weird, solution - use Qt' event system.

int main(int argc, char[]* argv)
{
    QApplication app( argc, argv );

    QStringList arguments = app.arguments();
    if( arguments.count() == argc ) arguments.removeFirst();

    Logic appLogic( NULL, &app );
    #ifdef Q_OS_MAC
    app.installMessageHandler( &appLogic );

    // Here we should alreasy get FileOpenEvent, if it occurs
    // NOTE: without this FileOpenEvent will arrive LATER
    //       than the DryRunEvent!
    app.processEvents();

    // If there is no file open event in the queue,
    // we should just open the blank program window
    // NOTE: Qt takes ownership of this event object,
    //       so you should not delete it manually
    DryRunEvent* runEv = new DryRunEvent( p );
    a.postEvent( &l, runEv, Qt::LowEventPriority );
    #endif

    ...

    #ifndef Q_OS_MAC
    if( ! appLogic.start( arguments ) ) return 0;
    #endif

    return app.exec();
}

Custom event header:

class BaseEvent : public QEvent
{
public:
    BaseEvent( QEvent::Type& eType ) : QEvent( getEventType( eType ) )
    {
    }

    ~BaseEvent() {}

    QEvent::Type getEventType( QEvent::Type& eType )
    {
        if( eType == QEvent::None )
        {
            eType = static_cast<QEvent::Type>( QEvent::registerEventType() );
        }
        return eType;
    }
};

class DryRunEvent : public BaseEvent
{
    QStringList m_params;

    public:
         DryRunEvent( const Parameters& params ) :
             BaseEvent( eventType ), m_params( params )
         {
         }

         ~DryRunEvent(){}

         QStringList GetCmdLineParams() const { return m_params; }

    public:
        static QEvent::Type eventType;
};

Custom event source:

QEvent::Type ViewerDryRunEvent::eventType = QEvent::None;

Logic class header:

class Logic : public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY( Logic )

    public:
        explicit Logic(QObject *parent, QApplication* application);
        virtual ~Logic();

    public slots:
        bool Start( QStringList parameters );
        void ReceiveParameters( QStringList parameters );
        void Stop();

        #ifdef Q_OS_MAC
        bool Logic::WasStarted() const
        {
            ... Determine wether logic was started or not ...
        }
        #endif

    private:
        #ifdef OS_MACOSX
        // Virtual overrided functions
         bool eventFilter( QObject* obj, QEvent* event )
         {
             if( event->type() == QEvent::FileOpen )
             {
                 QFileOpenEvent* fileEvent = static_cast< QFileOpenEvent* >(event);
                 Q_ASSERT( fileEvent != NULL );

                 QString uri;
                 if( fileEvent->file().isEmpty() == false )
                 {
                     uri = fileEvent->file();
                 }
                 else if( fileEvent->url().isEmpty() == false )
                 {
                     uri = fileEvent->url().toString();
                 }

                 if( uri.isEmpty() == false )
                 {
                     if( WasStarted() ) ReceiveParameters( uri );
                     else Start( uri );
                 }

                 return false;
             }
             else if( event->type() == DryRunEvent::eventType )
             {
                 DryRunEvent* myEvent = static_cast< DryRunEvent* >( event );
                 Q_ASSERT( myEvent != NULL );
                 QStringList cmdLineParams = myEvent->GetCmdLineParams();

                 Q_ASSERT( !WasStarted() );
                 if( WasStarted() ) return false;

                 if( !Start( cmdLineParams ) ) m_application->exit( 0 );
                 return false;
             }

             // Standard event processing
             return QObject::eventFilter( obj, event );
        }
        #endif
    };

I hope someone find this stuff useful :)

like image 140
eraxillan Avatar answered Oct 10 '22 11:10

eraxillan