diff --git a/src/kiod/CMakeLists.txt b/src/kiod/CMakeLists.txt --- a/src/kiod/CMakeLists.txt +++ b/src/kiod/CMakeLists.txt @@ -1,4 +1,9 @@ +include(ECMMarkNonGuiExecutable) + set(kiod_SRCS kiod_main.cpp) +if (APPLE) + set(kiod_SRCS ${kiod_SRCS} kiod_agent.mm) +endif() add_executable(kiod5 ${kiod_SRCS}) @@ -11,6 +16,13 @@ Qt5::Widgets # QApplication ) +if (APPLE) + target_link_libraries(kiod5 "-framework AppKit -framework CoreFoundation") + + # Mark it as non-gui so we won't create an app bundle on Mac OS X + ecm_mark_nongui_executable(kiod5) +endif () + install(TARGETS kiod5 DESTINATION ${KDE_INSTALL_LIBEXECDIR_KF5}) configure_file(org.kde.kiod5.service.in diff --git a/src/kiod/kiod_agent.mm b/src/kiod/kiod_agent.mm new file mode 100644 --- /dev/null +++ b/src/kiod/kiod_agent.mm @@ -0,0 +1,47 @@ +/* This file is part of the KDE libraries + Copyright (C) 2017 René J.V. Bertin + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License or (at + your option) version 3 or, at the discretion of KDE e.V. (which shall + act as a proxy as in section 14 of the GPLv3), 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 Lesser 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. +*/ + +#import +#import + +// Set the LaunchServices LSUIElement property programmatically in unbundled +// ("nongui") executables, instead of in the app bundle's Info.plist file. +// This function has to be called as early as possible in main(), and before +// creating the QApplication instance. +void makeAgentApplication() +{ + CFBundleRef mainBundle = CFBundleGetMainBundle(); + if (mainBundle) { + // get the application's Info Dictionary. For app bundles this would live in the bundle's Info.plist, + // for regular executables it is obtained in another way. + CFMutableDictionaryRef infoDict = (CFMutableDictionaryRef) CFBundleGetInfoDictionary(mainBundle); + if (infoDict) { + // Add or set the "LSUIElement" key with/to value "1". This can simply be a CFString. + CFDictionarySetValue(infoDict, CFSTR("LSUIElement"), CFSTR("1")); + // That's it. We're now considered as an "agent" by the window server, and thus will have + // neither menubar nor presence in the Dock or App Switcher. + } + } +} + +void setAgentActivationPolicy() +{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; +} diff --git a/src/kiod/kiod_main.cpp b/src/kiod/kiod_main.cpp --- a/src/kiod/kiod_main.cpp +++ b/src/kiod/kiod_main.cpp @@ -94,6 +94,12 @@ int main(int argc, char *argv[]) { +#ifdef Q_OS_MACOS + // do the "early" step to make this an "agent" application: + // set the LSUIElement InfoDict key programmatically. + extern void makeAgentApplication(); + makeAgentApplication(); +#endif qunsetenv("SESSION_MANAGER"); // disable session management QApplication app(argc, argv); // GUI needed for kpasswdserver's dialogs @@ -120,6 +126,14 @@ self(); // create it in this thread qDBusAddSpyHook(messageFilter); +#ifdef Q_OS_MACOS + // In the case of kiod5 we need to confirm the agent nature, + // possibly because of how things have been set up after creating + // the QApplication instance. Failure to do this will disable + // text input into dialogs we may post. + extern void setAgentActivationPolicy(); + setAgentActivationPolicy(); +#endif return app.exec(); } diff --git a/src/kpasswdserver/kpasswdserver.cpp b/src/kpasswdserver/kpasswdserver.cpp --- a/src/kpasswdserver/kpasswdserver.cpp +++ b/src/kpasswdserver/kpasswdserver.cpp @@ -894,11 +894,15 @@ if (info.getExtraField(AUTHINFO_EXTRAFIELD_ANONYMOUS).isValid () && password.isEmpty() && username.isEmpty()) dlg->setAnonymousMode(info.getExtraField(AUTHINFO_EXTRAFIELD_ANONYMOUS).toBool()); +#ifndef Q_OS_MACOS #ifndef Q_WS_WIN KWindowSystem::setMainWindow(dlg, request->windowId); #else KWindowSystem::setMainWindow(dlg, (HWND)request->windowId); #endif +#else + KWindowSystem::forceActiveWindow(dlg->winId(), 0); +#endif qCDebug(category) << "Showing password dialog" << dlg << ", window-id=" << request->windowId; m_authInProgress.insert(dlg, request);