diff --git a/applets/kimpanel/backend/CMakeLists.txt b/applets/kimpanel/backend/CMakeLists.txt index df2d6e564..967898a0b 100644 --- a/applets/kimpanel/backend/CMakeLists.txt +++ b/applets/kimpanel/backend/CMakeLists.txt @@ -1,2 +1,3 @@ + add_subdirectory(scim) add_subdirectory(ibus) diff --git a/applets/kimpanel/backend/ibus/CMakeLists.txt b/applets/kimpanel/backend/ibus/CMakeLists.txt index 8ca930eed..f1ef112cb 100644 --- a/applets/kimpanel/backend/ibus/CMakeLists.txt +++ b/applets/kimpanel/backend/ibus/CMakeLists.txt @@ -1,43 +1,52 @@ find_package(IBus) find_package(GLIB2) find_package(GIO) find_package(GObject) if(IBUS_FOUND AND GLIB2_FOUND AND GIO_FOUND AND GOBJECT_FOUND) + configure_file(config-kimpanel.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kimpanel.h) include_directories(${IBUS_INCLUDE_DIR}) include_directories(${GLIB2_INCLUDE_DIR}) include_directories(${GIO_INCLUDE_DIR}) include_directories(${GOBJECT_INCLUDE_DIR}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") if (${IBUS_VERSION} VERSION_LESS "1.5.0") set(kimpanel_ibus_panel_SRCS ibus14/panel.c ibus14/main.c) add_executable(kimpanel-ibus-panel ${kimpanel_ibus_panel_SRCS}) target_link_libraries(kimpanel-ibus-panel ${IBUS_LIBRARIES} ${GLIB2_LIBRARIES} ${GIO_LIBRARIES} ${GOBJECT_LIBRARIES}) - install(TARGETS kimpanel-ibus-panel DESTINATION ${LIBEXEC_INSTALL_DIR}) # configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kimpanel.xml.in ${CMAKE_CURRENT_BINARY_DIR}/kimpanel.xml @ONLY) # install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kimpanel.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/ibus/component) else() find_package(Qt5X11Extras) find_package(XCB COMPONENTS XCB KEYSYMS) if (Qt5X11Extras_FOUND AND XCB_XCB_FOUND AND XCB_KEYSYMS_FOUND) include_directories(${Qt5X11Extras_INCLUDE_DIRS}) include_directories(${XCB_XCB_INCLUDE_DIRS}) include_directories(${XCB_KEYSYMS_INCLUDE_DIRS}) set(kimpanel_ibus_panel_SRCS ibus15/xkblayoutmanager.cpp ibus15/gtkaccelparse_p.c ibus15/gdkkeynames_p.c ibus15/app.cpp ibus15/enginemanager.cpp ibus15/main.cpp ibus15/panel.cpp ibus15/propertymanager.cpp) add_definitions(-DQT_NO_KEYWORDS) add_executable(kimpanel-ibus-panel ${kimpanel_ibus_panel_SRCS}) target_link_libraries(kimpanel-ibus-panel ${IBUS_LIBRARIES} ${GLIB2_LIBRARIES} ${GIO_LIBRARIES} ${GOBJECT_LIBRARIES} Qt5::Core Qt5::DBus Qt5::Gui Qt5::X11Extras XCB::KEYSYMS) - install(TARGETS kimpanel-ibus-panel DESTINATION ${LIBEXEC_INSTALL_DIR}) # configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kimpanel.xml.in ${CMAKE_CURRENT_BINARY_DIR}/kimpanel.xml @ONLY) # install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kimpanel.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/ibus/component) endif() endif() + + if (TARGET kimpanel-ibus-panel) + target_include_directories(kimpanel-ibus-panel PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + + add_executable(kimpanel-ibus-panel-launcher launcher.cpp) + set_target_properties(kimpanel-ibus-panel-launcher PROPERTIES AUTOMOC TRUE) + target_link_libraries(kimpanel-ibus-panel-launcher Qt5::Core Qt5::DBus) + target_include_directories(kimpanel-ibus-panel-launcher PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + install(TARGETS kimpanel-ibus-panel kimpanel-ibus-panel-launcher DESTINATION ${LIBEXEC_INSTALL_DIR}) + endif() endif() diff --git a/applets/kimpanel/backend/ibus/config-kimpanel.h.cmake b/applets/kimpanel/backend/ibus/config-kimpanel.h.cmake new file mode 100644 index 000000000..9e15f963c --- /dev/null +++ b/applets/kimpanel/backend/ibus/config-kimpanel.h.cmake @@ -0,0 +1,6 @@ +#ifndef _KIMPANEL_CONFIG_KIMPANEL_H_CMAKE_ +#define _KIMPANEL_CONFIG_KIMPANEL_H_CMAKE_ + +#define KIMPANEL_LIBEXEC_DIR "${KDE_INSTALL_FULL_LIBEXECDIR}" + +#endif // _KIMPANEL_CONFIG_KIMPANEL_H_CMAKE_ diff --git a/applets/kimpanel/backend/ibus/ibus15/app.cpp b/applets/kimpanel/backend/ibus/ibus15/app.cpp index 44dc3edc7..9aebe2473 100644 --- a/applets/kimpanel/backend/ibus/ibus15/app.cpp +++ b/applets/kimpanel/backend/ibus/ibus15/app.cpp @@ -1,471 +1,478 @@ /* * Copyright (C) 2013-2014 Weng Xuetian * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "app.h" #include "gtkaccelparse_p.h" #include "gdkkeysyms_p.h" #include #include #include +#include +#include #include #define USED_MASK (XCB_MOD_MASK_SHIFT | XCB_MOD_MASK_CONTROL | XCB_MOD_MASK_1 | XCB_MOD_MASK_4) bool XcbEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) { Q_UNUSED(result); if (eventType != "xcb_generic_event_t") { return false; } return qobject_cast(qApp)->nativeEvent(static_cast(message)); } // callback functions from glib code static void name_acquired_cb (GDBusConnection* connection, const gchar* sender_name, const gchar* object_path, const gchar* interface_name, const gchar* signal_name, GVariant* parameters, gpointer self) { Q_UNUSED(connection); Q_UNUSED(sender_name); Q_UNUSED(object_path); Q_UNUSED(interface_name); Q_UNUSED(signal_name); Q_UNUSED(parameters); App* app = (App*) self; app->nameAcquired(); } static void name_lost_cb (GDBusConnection* connection, const gchar* sender_name, const gchar* object_path, const gchar* interface_name, const gchar* signal_name, GVariant* parameters, gpointer self) { Q_UNUSED(connection); Q_UNUSED(sender_name); Q_UNUSED(object_path); Q_UNUSED(interface_name); Q_UNUSED(signal_name); Q_UNUSED(parameters); App* app = (App*) self; app->nameLost(); } static void ibus_connected_cb (IBusBus *m_bus, gpointer user_data) { Q_UNUSED(m_bus); App* app = (App*) user_data; app->init(); } static void ibus_disconnected_cb (IBusBus *m_bus, gpointer user_data) { Q_UNUSED(m_bus); App* app = (App*) user_data; app->finalize(); } static void initIconMap(QMap& iconMap) { iconMap["gtk-about"] = "help-about"; iconMap["gtk-add"] = "list-add"; iconMap["gtk-bold"] = "format-text-bold"; iconMap["gtk-cdrom"] = "media-optical"; iconMap["gtk-clear"] = "edit-clear"; iconMap["gtk-close"] = "window-close"; iconMap["gtk-copy"] = "edit-copy"; iconMap["gtk-cut"] = "edit-cut"; iconMap["gtk-delete"] = "edit-delete"; iconMap["gtk-dialog-authentication"] = "dialog-password"; iconMap["gtk-dialog-info"] = "dialog-information"; iconMap["gtk-dialog-warning"] = "dialog-warning"; iconMap["gtk-dialog-error"] = "dialog-error"; iconMap["gtk-dialog-question"] = "dialog-question"; iconMap["gtk-directory"] = "folder"; iconMap["gtk-execute"] = "system-run"; iconMap["gtk-file"] = "text-x-generic"; iconMap["gtk-find"] = "edit-find"; iconMap["gtk-find-and-replace"] = "edit-find-replace"; iconMap["gtk-floppy"] = "media-floppy"; iconMap["gtk-fullscreen"] = "view-fullscreen"; iconMap["gtk-goto-bottom"] = "go-bottom"; iconMap["gtk-goto-first"] = "go-first"; iconMap["gtk-goto-last"] = "go-last"; iconMap["gtk-goto-top"] = "go-top"; iconMap["gtk-go-back"] = "go-previous"; iconMap["gtk-go-down"] = "go-down"; iconMap["gtk-go-forward"] = "go-next"; iconMap["gtk-go-up"] = "go-up"; iconMap["gtk-harddisk"] = "drive-harddisk"; iconMap["gtk-help"] = "help-browser"; iconMap["gtk-home"] = "go-home"; iconMap["gtk-indent"] = "format-indent-more"; iconMap["gtk-info"] = "dialog-information"; iconMap["gtk-italic"] = "format-text-italic"; iconMap["gtk-jump-to"] = "go-jump"; iconMap["gtk-justify-center"] = "format-justify-center"; iconMap["gtk-justify-fill"] = "format-justify-fill"; iconMap["gtk-justify-left"] = "format-justify-left"; iconMap["gtk-justify-right"] = "format-justify-right"; iconMap["gtk-leave-fullscreen"] = "view-restore"; iconMap["gtk-missing-image"] = "image-missing"; iconMap["gtk-media-forward"] = "media-seek-forward"; iconMap["gtk-media-next"] = "media-skip-forward"; iconMap["gtk-media-pause"] = "media-playback-pause"; iconMap["gtk-media-play"] = "media-playback-start"; iconMap["gtk-media-previous"] = "media-skip-backward"; iconMap["gtk-media-record"] = "media-record"; iconMap["gtk-media-rewind"] = "media-seek-backward"; iconMap["gtk-media-stop"] = "media-playback-stop"; iconMap["gtk-network"] = "network-workgroup"; iconMap["gtk-new"] = "document-new"; iconMap["gtk-open"] = "document-open"; iconMap["gtk-page-setup"] = "document-page-setup"; iconMap["gtk-paste"] = "edit-paste"; iconMap["gtk-preferences"] = "preferences-system"; iconMap["gtk-print"] = "document-print"; iconMap["gtk-print-error"] = "printer-error"; iconMap["gtk-properties"] = "document-properties"; iconMap["gtk-quit"] = "application-exit"; iconMap["gtk-redo"] = "edit-redo"; iconMap["gtk-refresh"] = "view-refresh"; iconMap["gtk-remove"] = "list-remove"; iconMap["gtk-revert-to-saved"] = "document-revert"; iconMap["gtk-save"] = "document-save"; iconMap["gtk-save-as"] = "document-save-as"; iconMap["gtk-select-all"] = "edit-select-all"; iconMap["gtk-sort-ascending"] = "view-sort-ascending"; iconMap["gtk-sort-descending"] = "view-sort-descending"; iconMap["gtk-spell-check"] = "tools-check-spelling"; iconMap["gtk-stop"] = "process-stop"; iconMap["gtk-strikethrough"] = "format-text-strikethrough"; iconMap["gtk-underline"] = "format-text-underline"; iconMap["gtk-undo"] = "edit-undo"; iconMap["gtk-unindent"] = "format-indent-less"; iconMap["gtk-zoom-100"] = "zoom-original"; iconMap["gtk-zoom-fit"] = "zoom-fit-best"; iconMap["gtk-zoom-in"] = "zoom-in"; iconMap["gtk-zoom-out"] = "zoom-out"; } App::App(int argc, char** argv): QGuiApplication(argc, argv) ,m_eventFilter(new XcbEventFilter) ,m_init(false) ,m_bus(0) ,m_impanel(0) ,m_keyboardGrabbed(false) ,m_doGrab(false) ,m_syms(0) + ,m_watcher(new QDBusServiceWatcher(this)) { + m_watcher->setConnection(QDBusConnection::sessionBus()); + m_watcher->addWatchedService("org.kde.impanel"); + connect(m_watcher, &QDBusServiceWatcher::serviceUnregistered, this, &App::finalize); m_syms = xcb_key_symbols_alloc(QX11Info::connection()); installNativeEventFilter(m_eventFilter.data()); ibus_init (); m_bus = ibus_bus_new (); g_signal_connect (m_bus, "connected", G_CALLBACK (ibus_connected_cb), this); g_signal_connect (m_bus, "disconnected", G_CALLBACK (ibus_disconnected_cb), this); if (ibus_bus_is_connected (m_bus)) { init(); } initIconMap(m_iconMap); } uint App::getPrimaryModifier(uint state) { const GdkModifierType masks[] = { GDK_MOD5_MASK, GDK_MOD4_MASK, GDK_MOD3_MASK, GDK_MOD2_MASK, GDK_MOD1_MASK, GDK_CONTROL_MASK, GDK_LOCK_MASK, GDK_LOCK_MASK }; for (size_t i = 0; i < sizeof(masks) / sizeof(masks[0]); i++) { GdkModifierType mask = masks[i]; if ((state & mask) == mask) return mask; } return 0; } bool App::nativeEvent(xcb_generic_event_t* event) { if ((event->response_type & ~0x80) == XCB_KEY_PRESS) { auto keypress = reinterpret_cast(event); if (keypress->event == QX11Info::appRootWindow()) { auto sym = xcb_key_press_lookup_keysym(m_syms, keypress, 0); uint state = keypress->state & USED_MASK; bool forward; if ((forward = m_triggersList.contains(qMakePair(sym, state))) || m_triggersList.contains(qMakePair(sym, state & (~XCB_MOD_MASK_SHIFT)))) { if (m_keyboardGrabbed) { ibus_panel_impanel_navigate(m_impanel, false, forward); } else { if (grabXKeyboard()) { ibus_panel_impanel_navigate(m_impanel, true, forward); } else { ibus_panel_impanel_move_next(m_impanel); } } } } } else if ((event->response_type & ~0x80) == XCB_KEY_RELEASE) { auto keyrelease = reinterpret_cast(event); if (keyrelease->event == QX11Info::appRootWindow()) { keyRelease(keyrelease); } } return false; } void App::keyRelease(const xcb_key_release_event_t* event) { unsigned int mk = event->state & USED_MASK; // ev.state is state before the key release, so just checking mk being 0 isn't enough // using XQueryPointer() also doesn't seem to work well, so the check that all // modifiers are released: only one modifier is active and the currently released // key is this modifier - if yes, release the grab int mod_index = -1; for (int i = XCB_MAP_INDEX_SHIFT; i <= XCB_MAP_INDEX_5; ++i) if ((mk & (1 << i)) != 0) { if (mod_index >= 0) return; mod_index = i; } bool release = false; if (mod_index == -1) release = true; else { auto cookie = xcb_get_modifier_mapping(QX11Info::connection()); auto reply = xcb_get_modifier_mapping_reply(QX11Info::connection(), cookie, NULL); if (reply) { auto keycodes = xcb_get_modifier_mapping_keycodes(reply); for (int i = 0; i < reply->keycodes_per_modifier; i++) { if (keycodes[reply->keycodes_per_modifier * mod_index + i] == event->detail) { release = true; } } } free(reply); } if (!release) { return; } if (m_keyboardGrabbed) { accept(); } } void App::init() { // only init once if (m_init) { return; } GDBusConnection* connection = ibus_bus_get_connection (m_bus); g_dbus_connection_signal_subscribe (connection, "org.freedesktop.DBus", "org.freedesktop.DBus", "NameAcquired", "/org/freedesktop/DBus", IBUS_SERVICE_PANEL, G_DBUS_SIGNAL_FLAGS_NONE, name_acquired_cb, this, NULL); g_dbus_connection_signal_subscribe (connection, "org.freedesktop.DBus", "org.freedesktop.DBus", "NameLost", "/org/freedesktop/DBus", IBUS_SERVICE_PANEL, G_DBUS_SIGNAL_FLAGS_NONE, name_lost_cb, this, NULL); ibus_bus_request_name (m_bus, IBUS_SERVICE_PANEL, IBUS_BUS_NAME_FLAG_ALLOW_REPLACEMENT | IBUS_BUS_NAME_FLAG_REPLACE_EXISTING); m_init = true; } void App::nameAcquired() { if (m_impanel) { g_object_unref(m_impanel); } m_impanel = ibus_panel_impanel_new (ibus_bus_get_connection (m_bus)); ibus_panel_impanel_set_bus(m_impanel, m_bus); ibus_panel_impanel_set_app(m_impanel, this); } void App::nameLost() { + setDoGrab(false); if (m_impanel) { g_object_unref(m_impanel); } m_impanel = NULL; } QByteArray App::normalizeIconName(const QByteArray& icon) const { if (m_iconMap.contains(icon)) { return m_iconMap[icon]; } return icon; } void App::setTriggerKeys(QList< TriggerKey > triggersList) { if (m_doGrab) { ungrabKey(); } m_triggersList = triggersList; if (m_doGrab) { grabKey(); } } void App::setDoGrab(bool doGrab) { if (m_doGrab != doGrab) {; if (doGrab) { grabKey(); } else { ungrabKey(); } m_doGrab = doGrab; } } void App::grabKey() { Q_FOREACH(const TriggerKey& key, m_triggersList) { xcb_keysym_t sym = key.first; uint modifiers = key.second; xcb_keycode_t* keycode = xcb_key_symbols_get_keycode(m_syms, sym); if (!keycode) { - g_warning ("Can not convert keyval=%lu to keycode!", sym); + g_warning ("Can not convert keyval=%u to keycode!", sym); } else { xcb_grab_key(QX11Info::connection(), true, QX11Info::appRootWindow(), modifiers, keycode[0], XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); if ((modifiers & XCB_MOD_MASK_SHIFT) == 0) { xcb_grab_key(QX11Info::connection(), true, QX11Info::appRootWindow(), modifiers | XCB_MOD_MASK_SHIFT, keycode[0], XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); } } free(keycode); } } void App::ungrabKey() { Q_FOREACH(const TriggerKey& key, m_triggersList) { xcb_keysym_t sym = key.first; uint modifiers = key.second; xcb_keycode_t* keycode = xcb_key_symbols_get_keycode(m_syms, sym); if (!keycode) { - g_warning ("Can not convert keyval=%lu to keycode!", sym); + g_warning ("Can not convert keyval=%u to keycode!", sym); } else { xcb_ungrab_key(QX11Info::connection(), keycode[0], QX11Info::appRootWindow(), modifiers); if ((modifiers & XCB_MOD_MASK_SHIFT) == 0) { xcb_ungrab_key(QX11Info::connection(), keycode[0], QX11Info::appRootWindow(), modifiers | XCB_MOD_MASK_SHIFT); } } free(keycode); } } bool App::grabXKeyboard() { if (m_keyboardGrabbed) return false; auto w = QX11Info::appRootWindow(); auto cookie = xcb_grab_keyboard(QX11Info::connection(), false, w, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); auto reply = xcb_grab_keyboard_reply(QX11Info::connection(), cookie, NULL); if (reply && reply->status == XCB_GRAB_STATUS_SUCCESS) { m_keyboardGrabbed = true; } free(reply); return m_keyboardGrabbed; } void App::ungrabXKeyboard() { if (!m_keyboardGrabbed) { // grabXKeyboard() may fail sometimes, so don't fail, but at least warn anyway qDebug() << "ungrabXKeyboard() called but keyboard not grabbed!"; } m_keyboardGrabbed = false; xcb_ungrab_keyboard(QX11Info::connection(), XCB_CURRENT_TIME); } void App::accept() { if (m_keyboardGrabbed) { ungrabXKeyboard(); } ibus_panel_impanel_accept(m_impanel); } void App::finalize() { clean(); App::exit(0); } void App::clean() { if (m_impanel) { g_object_unref(m_impanel); m_impanel = 0; } if (m_bus) { g_signal_handlers_disconnect_by_func(m_bus, (gpointer) ibus_disconnected_cb, this); g_signal_handlers_disconnect_by_func(m_bus, (gpointer) ibus_connected_cb, this); g_object_unref(m_bus); m_bus = 0; } ungrabKey(); } App::~App() { clean(); if (m_syms) { xcb_key_symbols_free(m_syms); } } diff --git a/applets/kimpanel/backend/ibus/ibus15/app.h b/applets/kimpanel/backend/ibus/ibus15/app.h index 01b1f744a..3014bcef4 100644 --- a/applets/kimpanel/backend/ibus/ibus15/app.h +++ b/applets/kimpanel/backend/ibus/ibus15/app.h @@ -1,76 +1,78 @@ /* * Copyright (C) 2014 Weng Xuetian * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef APP_H #define APP_H #include #include #include #include #include #include #include #include #include "panel.h" +class QDBusServiceWatcher; class XcbEventFilter : public QAbstractNativeEventFilter { public: virtual bool nativeEventFilter(const QByteArray &eventType, void* message, long int* result) override; }; class App : public QGuiApplication { Q_OBJECT public: typedef QPair< uint, uint > TriggerKey; App(int argc, char** argv); virtual ~App(); - void finalize(); void setTriggerKeys(QList< TriggerKey > triggersList); void setDoGrab(bool doGrab); bool keyboardGrabbed() { return m_keyboardGrabbed; } bool nativeEvent(xcb_generic_event_t* event); void init(); void nameAcquired(); void nameLost(); QByteArray normalizeIconName(const QByteArray& icon) const; -private Q_SLOTS: +public Q_SLOTS: + void finalize(); void clean(); void grabKey(); void ungrabKey(); uint getPrimaryModifier(uint state); void keyRelease(const xcb_key_release_event_t* event); void accept(); void ungrabXKeyboard(); bool grabXKeyboard(); private: QScopedPointer m_eventFilter; bool m_init; IBusBus *m_bus; IBusPanelImpanel *m_impanel; QList< QPair< uint, uint > > m_triggersList; bool m_keyboardGrabbed; bool m_doGrab; xcb_key_symbols_t* m_syms; QMap m_iconMap; + QDBusServiceWatcher *m_watcher; }; #endif // APP_H diff --git a/applets/kimpanel/backend/ibus/launcher.cpp b/applets/kimpanel/backend/ibus/launcher.cpp new file mode 100644 index 000000000..056f827fd --- /dev/null +++ b/applets/kimpanel/backend/ibus/launcher.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017~2017 by CSSlayer + * wengxt@gmail.com + * + * 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) 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 + * Lesser 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. If not, + * see . + */ + + +#include +#include +#include +#include +#include +#include "config-kimpanel.h" + +class IBusPanelLauncher : public QCoreApplication { + Q_OBJECT +public: + IBusPanelLauncher(int argc, char *argv[]) : QCoreApplication(argc, argv), m_watcher(new QDBusServiceWatcher(this)) { + m_watcher->setConnection(QDBusConnection::sessionBus()); + QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); + } + +public Q_SLOTS: + void init() { + // already a launcher running + if (!QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.impanel.IBusPanelLauncher"))) { + quit(); + return; + } + // Check if applet is still there. + connect(m_watcher, &QDBusServiceWatcher::serviceUnregistered, this, &IBusPanelLauncher::serviceUnregistered); + connect(m_watcher, &QDBusServiceWatcher::serviceRegistered, this, &IBusPanelLauncher::serviceRegistered); + m_watcher->addWatchedService("org.kde.impanel"); + m_watcher->addWatchedService("org.freedesktop.IBus"); + + // check if panel is already created + QDBusConnection::sessionBus().connect("org.kde.impanel", "/org/kde/impanel", "org.kde.impanel2", "PanelRegistered", this, SLOT(quit())); + if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.impanel")) { + quit(); + return; + } + if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.freedesktop.IBus")) { + serviceRegistered("org.freedesktop.IBus"); + } + } + + void serviceRegistered(const QString &service) { + if (service == "org.freedesktop.IBus") { + launchIBusPanel(); + } + } + + void serviceUnregistered(const QString &service) { + if (service == "org.kde.impanel") { + quit(); + } + } + +private: + void launchIBusPanel() { + const QString panelPath = QStringLiteral(KIMPANEL_LIBEXEC_DIR"/kimpanel-ibus-panel"); + QProcess::startDetached(panelPath); + quit(); + } + +private: + QDBusServiceWatcher *m_watcher; + +}; + +int main(int argc, char *argv[]) { + IBusPanelLauncher app(argc, argv); + + return app.exec(); +} + +#include "launcher.moc" diff --git a/dataengines/kimpanel/CMakeLists.txt b/dataengines/kimpanel/CMakeLists.txt index eb47004a5..850b7c0f5 100644 --- a/dataengines/kimpanel/CMakeLists.txt +++ b/dataengines/kimpanel/CMakeLists.txt @@ -1,32 +1,35 @@ add_definitions(-DTRANSLATION_DOMAIN="plasma_engine_kimpanel") +configure_file(config-kimpanel.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kimpanel.h) + set(plasma_engine_kimpanel_SRCS kimpaneldataengine.cpp kimpanelagent.cpp kimpanelservice.cpp kimpaneljob.cpp kimpanelinputpanelcontainer.cpp kimpanelstatusbarcontainer.cpp ) QT5_ADD_DBUS_ADAPTOR(plasma_engine_kimpanel_SRCS org.kde.impanel.xml kimpanelagent.h PanelAgent) add_library(plasma_engine_kimpanel ${plasma_engine_kimpanel_SRCS}) +target_include_directories(plasma_engine_kimpanel PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) set_target_properties(plasma_engine_kimpanel PROPERTIES PREFIX "") target_link_libraries(plasma_engine_kimpanel KF5::Plasma KF5::Service Qt5::DBus ) kcoreaddons_desktop_to_json(plasma_engine_kimpanel plasma-dataengine-kimpanel.desktop) install(TARGETS plasma_engine_kimpanel DESTINATION ${PLUGIN_INSTALL_DIR}/plasma/dataengine) install(FILES plasma-dataengine-kimpanel.desktop DESTINATION ${SERVICES_INSTALL_DIR}) install(FILES kimpanel.operations DESTINATION ${PLASMA_DATA_INSTALL_DIR}/services) diff --git a/dataengines/kimpanel/config-kimpanel.h.cmake b/dataengines/kimpanel/config-kimpanel.h.cmake new file mode 100644 index 000000000..9e15f963c --- /dev/null +++ b/dataengines/kimpanel/config-kimpanel.h.cmake @@ -0,0 +1,6 @@ +#ifndef _KIMPANEL_CONFIG_KIMPANEL_H_CMAKE_ +#define _KIMPANEL_CONFIG_KIMPANEL_H_CMAKE_ + +#define KIMPANEL_LIBEXEC_DIR "${KDE_INSTALL_FULL_LIBEXECDIR}" + +#endif // _KIMPANEL_CONFIG_KIMPANEL_H_CMAKE_ diff --git a/dataengines/kimpanel/kimpanelagent.cpp b/dataengines/kimpanel/kimpanelagent.cpp index 7c062e445..5c9826dfd 100644 --- a/dataengines/kimpanel/kimpanelagent.cpp +++ b/dataengines/kimpanel/kimpanelagent.cpp @@ -1,293 +1,296 @@ /*************************************************************************** * Copyright (C) 2009 by Wang Hoi * * Copyright (C) 2011 by CSSlayer * * * * 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. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "kimpanelagent.h" #include "impaneladaptor.h" // Qt #include #include #include #include #include #include #include PanelAgent::PanelAgent(QObject *parent) : QObject(parent) ,adaptor(new ImpanelAdaptor(this)) ,adaptor2(new Impanel2Adaptor(this)) ,watcher(new QDBusServiceWatcher(this)) { watcher->setConnection(QDBusConnection::sessionBus()); watcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration); QDBusConnection::connectToBus(QDBusConnection::SessionBus, "kimpanel_bus").registerObject("/org/kde/impanel", this); QDBusConnection::connectToBus(QDBusConnection::SessionBus, "kimpanel_bus").registerService("org.kde.impanel"); // directly connect to corresponding signal QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "Enable", this, SIGNAL(enable(bool))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "ShowPreedit", this, SIGNAL(showPreedit(bool))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "ShowAux", this, SIGNAL(showAux(bool))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "ShowLookupTable", this, SIGNAL(showLookupTable(bool))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "UpdateLookupTableCursor", this, SIGNAL(updateLookupTableCursor(int))); // do some serialization QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "UpdateLookupTable", this, SLOT(UpdateLookupTable(QStringList, QStringList, QStringList, bool, bool))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "UpdatePreeditCaret", this, SIGNAL(updatePreeditCaret(int))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "UpdatePreeditText", this, SLOT(UpdatePreeditText(QString, QString))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "UpdateAux", this, SLOT(UpdateAux(QString, QString))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "UpdateSpotLocation", this, SIGNAL(updateSpotLocation(int, int))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "UpdateScreen", this, SLOT(UpdateScreen(int))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "UpdateProperty", this, SLOT(UpdateProperty(QString))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "RegisterProperties", this, SLOT(RegisterProperties(QStringList))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "ExecDialog", this, SLOT(ExecDialog(QString))); QDBusConnection("kimpanel_bus").connect("", "", "org.kde.kimpanel.inputmethod", "ExecMenu", this, SLOT(ExecMenu(QStringList))); connect(watcher, SIGNAL(serviceUnregistered(QString)), this, SLOT(serviceUnregistered(QString))); } PanelAgent::~PanelAgent() { // destructor QDBusConnection::disconnectFromBus("kimpanel_bus"); } void PanelAgent::serviceUnregistered(const QString& service) { if (service == m_currentService) { watcher->setWatchedServices(QStringList()); cached_props.clear(); m_currentService = QString(); emit showAux(false); emit showPreedit(false); emit showLookupTable(false); emit registerProperties(QList()); } } void PanelAgent::configure() { emit Configure(); } void PanelAgent::lookupTablePageDown() { emit LookupTablePageDown(); } void PanelAgent::lookupTablePageUp() { emit LookupTablePageUp(); } void PanelAgent::movePreeditCaret(int pos) { emit MovePreeditCaret(pos); } void PanelAgent::triggerProperty(const QString& key) { emit TriggerProperty(key); } void PanelAgent::selectCandidate(int idx) { emit SelectCandidate(idx); } static QList String2AttrList(const QString &str) { QList result; if (str.isEmpty()) { return result; } foreach(const QString & s, str.split(';')) { TextAttribute attr; QStringList list = s.split(':'); if (list.size() < 4) continue; switch (list.at(0).toInt()) { case 0: attr.type = TextAttribute::None; break; case 1: attr.type = TextAttribute::Decorate; break; case 2: attr.type = TextAttribute::Foreground; break; case 3: attr.type = TextAttribute::Background; break; default: attr.type = TextAttribute::None; } attr.start = list.at(1).toInt(); attr.length = list.at(2).toInt(); attr.value = list.at(3).toInt(); result << attr; } return result; } static KimpanelProperty String2Property(const QString &str) { KimpanelProperty result; QStringList list = str.split(':'); if (list.size() < 4) return result; result.key = list.at(0); result.label = list.at(1); result.icon = list.at(2); result.tip = list.at(3); result.hint = list.size() > 4 ? list.at(4) : ""; return result; } static KimpanelLookupTable Args2LookupTable(const QStringList &labels, const QStringList &candis, const QStringList &attrs, bool has_prev, bool has_next) { Q_ASSERT(labels.size() == candis.size()); Q_ASSERT(labels.size() == attrs.size()); KimpanelLookupTable result; for (int i = 0; i < labels.size(); i++) { KimpanelLookupTable::Entry entry; entry.label = labels.at(i); entry.text = candis.at(i); entry.attr = String2AttrList(attrs.at(i)); result.entries << entry; } result.has_prev = has_prev; result.has_next = has_next; return result; } void PanelAgent::created() { emit PanelCreated(); emit PanelCreated2(); } void PanelAgent::exit() { emit Exit(); } void PanelAgent::reloadConfig() { emit ReloadConfig(); } void PanelAgent::UpdateLookupTable(const QStringList &labels, const QStringList &candis, const QStringList &attrlists, bool has_prev, bool has_next) { emit updateLookupTable(Args2LookupTable(labels, candis, attrlists, has_prev, has_next)); } void PanelAgent::UpdatePreeditText(const QString &text, const QString &attr) { emit updatePreeditText(text, String2AttrList(attr)); } void PanelAgent::UpdateAux(const QString &text, const QString &attr) { emit updateAux(text, String2AttrList(attr)); } void PanelAgent::UpdateScreen(int screen_id) { Q_UNUSED(screen_id); } void PanelAgent::UpdateProperty(const QString &prop) { emit updateProperty(String2Property(prop)); } void PanelAgent::RegisterProperties(const QStringList &props) { const QDBusMessage& msg = message(); if (msg.service() != m_currentService) { watcher->removeWatchedService(m_currentService); + if (m_currentService.isEmpty()) { + emit PanelRegistered(); + } m_currentService = msg.service(); watcher->addWatchedService(m_currentService); } if (cached_props != props) { cached_props = props; QList list; foreach(const QString & prop, props) { list << String2Property(prop); } emit registerProperties(list); } } void PanelAgent::ExecDialog(const QString &prop) { emit execDialog(String2Property(prop)); } void PanelAgent::ExecMenu(const QStringList &entries) { QList list; foreach(const QString & entry, entries) { list << String2Property(entry); } emit execMenu(list); } void PanelAgent::SetSpotRect(int x, int y, int w, int h) { emit updateSpotRect(x, y, w, h); } void PanelAgent::SetLookupTable(const QStringList& labels, const QStringList& candis, const QStringList& attrlists, bool hasPrev, bool hasNext, int cursor, int layout) { emit updateLookupTableFull(Args2LookupTable(labels, candis, attrlists, hasPrev, hasNext), cursor, layout); } diff --git a/dataengines/kimpanel/kimpanelagent.h b/dataengines/kimpanel/kimpanelagent.h index 31f4ba619..57bb5bede 100644 --- a/dataengines/kimpanel/kimpanelagent.h +++ b/dataengines/kimpanel/kimpanelagent.h @@ -1,121 +1,122 @@ /*************************************************************************** * Copyright (C) 2009 by Wang Hoi * * Copyright (C) 2011 by CSSlayer * * * * 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. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #ifndef KIMPANEL_AGENT_H #define KIMPANEL_AGENT_H #include "kimpanelagenttype.h" // Qt #include #include #include class QDBusServiceWatcher; class Impanel2Adaptor; class ImpanelAdaptor; class PanelAgent: public QObject, protected QDBusContext { Q_OBJECT public: PanelAgent(QObject *parent); virtual ~PanelAgent(); void configure(); void created(); void exit(); void reloadConfig(); void selectCandidate(int idx); void lookupTablePageUp(); void lookupTablePageDown(); void movePreeditCaret(int pos); void triggerProperty(const QString& key); public: // PROPERTIES public Q_SLOTS: // METHODS void UpdateLookupTable(const QStringList &labels, const QStringList &candis, const QStringList &attrlists, bool has_prev, bool has_next); void UpdatePreeditText(const QString &text, const QString &attr); void UpdateAux(const QString &text, const QString &attr); void UpdateScreen(int screen_id); void UpdateProperty(const QString &prop); void RegisterProperties(const QStringList &props); void ExecDialog(const QString &prop); void ExecMenu(const QStringList &entries); void SetSpotRect(int x, int y, int w, int h); void SetLookupTable(const QStringList &labels, const QStringList &candis, const QStringList &attrlists, bool hasPrev, bool hasNext, int cursor, int layout); void serviceUnregistered(const QString& service); Q_SIGNALS: // signals that from kimpanel void Configure(); void MovePreeditCaret(int position); void SelectCandidate(int index); void LookupTablePageUp(); void LookupTablePageDown(); void TriggerProperty(const QString &key); void PanelCreated(); void PanelCreated2(); + void PanelRegistered(); void Exit(); void ReloadConfig(); // signals to inform kimpanel void enable(bool to_enable); void updatePreeditCaret(int pos); void updatePreeditText(const QString &text, const QList &attr); void updateAux(const QString &text, const QList &attr); void updateProperty(const KimpanelProperty &prop); void updateLookupTable(const KimpanelLookupTable &lookup_table); void updateLookupTableFull(const KimpanelLookupTable& lookup_table, int cursor, int layout); void updateSpotLocation(int x, int y); void updateSpotRect(int x, int y, int w ,int h); void registerProperties(const QList &props); void execDialog(const KimpanelProperty &prop); void execMenu(const QList &prop_list); void showPreedit(bool to_show); void showAux(bool to_show); void showLookupTable(bool to_show); void updateLookupTableCursor(int pos); private: bool m_show_aux; bool m_show_preedit; bool m_show_lookup_table; int m_spot_x; int m_spot_y; QString m_currentService; QStringList cached_props; ImpanelAdaptor* adaptor; Impanel2Adaptor* adaptor2; QDBusServiceWatcher* watcher; }; #endif // KIMPANEL_AGENT_H diff --git a/dataengines/kimpanel/kimpaneldataengine.cpp b/dataengines/kimpanel/kimpaneldataengine.cpp index 0d92dd8c9..339012c49 100644 --- a/dataengines/kimpanel/kimpaneldataengine.cpp +++ b/dataengines/kimpanel/kimpaneldataengine.cpp @@ -1,64 +1,78 @@ /*************************************************************************** * Copyright (C) 2011 by CSSlayer * * * * 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. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "kimpaneldataengine.h" #include "kimpanelagent.h" #include "kimpanelservice.h" #include "kimpanelinputpanelcontainer.h" #include "kimpanelstatusbarcontainer.h" +#include "config-kimpanel.h" + +#include +#include // Plasma #include KimpanelEngine::KimpanelEngine(QObject* parent, const QVariantList& args) : Plasma::DataEngine(parent, args), m_panelAgent(0) { init(); } +static void ibusPanelLauncher() { + // lets just blindly start the launcher. no need to use ifdef + const QString path = QStringLiteral(KIMPANEL_LIBEXEC_DIR"/kimpanel-ibus-panel-launcher"); + if (QFile::exists(path)) { + QProcess::startDetached(path); + } +} + void KimpanelEngine::init() { m_panelAgent = new PanelAgent(this); KimpanelInputPanelContainer* inputpanelSource = new KimpanelInputPanelContainer(this, m_panelAgent); inputpanelSource->setObjectName(INPUTPANEL_SOURCE_NAME); KimpanelStatusBarContainer* statusbarSource = new KimpanelStatusBarContainer(this, m_panelAgent); statusbarSource->setObjectName(STATUSBAR_SOURCE_NAME); addSource(inputpanelSource); addSource(statusbarSource); this->m_panelAgent->created(); + + ibusPanelLauncher(); } Plasma::Service* KimpanelEngine::serviceForSource(const QString& source) { if (source == INPUTPANEL_SOURCE_NAME) { KimpanelInputPanelContainer* container = qobject_cast< KimpanelInputPanelContainer* >(containerForSource(source)); if (container) return container->service(this); } else if (source == STATUSBAR_SOURCE_NAME) { KimpanelStatusBarContainer* container = qobject_cast< KimpanelStatusBarContainer* >(containerForSource(source)); if (container) return container->service(this); } return Plasma::DataEngine::serviceForSource(source); } K_EXPORT_PLASMA_DATAENGINE_WITH_JSON(kimpanel, KimpanelEngine, "plasma-dataengine-kimpanel.json") #include "kimpaneldataengine.moc" diff --git a/dataengines/kimpanel/org.kde.impanel.xml b/dataengines/kimpanel/org.kde.impanel.xml index 8911be103..63a75dbc7 100644 --- a/dataengines/kimpanel/org.kde.impanel.xml +++ b/dataengines/kimpanel/org.kde.impanel.xml @@ -1,38 +1,39 @@ +