You've already forked ukui-settings-daemon
mirror of
https://github.com/ukui/ukui-settings-daemon.git
synced 2026-03-09 09:24:39 -07:00
526 lines
15 KiB
C++
526 lines
15 KiB
C++
/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
* -*- coding: utf-8 -*-
|
|
*
|
|
* Copyright (C) 2020 KylinSoft Co., Ltd.
|
|
*
|
|
* 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 3 of the License, or
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <QDebug>
|
|
#include "keybindings-manager.h"
|
|
#include "config.h"
|
|
#include "clib-syslog.h"
|
|
#include "dconf-util.h"
|
|
#include <gio/gdesktopappinfo.h>
|
|
#include <QMessageBox>
|
|
#include <QScreen>
|
|
|
|
|
|
#define DESKTOP_APP_DIR "/usr/share/applications/"
|
|
#define GSETTINGS_KEYBINDINGS_DIR "/org/ukui/desktop/keybindings/"
|
|
#define CUSTOM_KEYBINDING_SCHEMA "org.ukui.control-center.keybinding"
|
|
|
|
KeybindingsManager *KeybindingsManager::mKeybinding = nullptr;
|
|
|
|
KeybindingsManager::KeybindingsManager()
|
|
{
|
|
|
|
}
|
|
|
|
KeybindingsManager::~KeybindingsManager()
|
|
{
|
|
|
|
}
|
|
|
|
KeybindingsManager *KeybindingsManager::KeybindingsManagerNew()
|
|
{
|
|
if(nullptr == mKeybinding)
|
|
mKeybinding = new KeybindingsManager();
|
|
return mKeybinding;
|
|
}
|
|
|
|
/**
|
|
* @brief parse_binding
|
|
* Whether the binding exists
|
|
* 获取此绑定的条目
|
|
* @return
|
|
*/
|
|
static bool
|
|
parse_binding (Binding *binding)
|
|
{
|
|
gboolean success;
|
|
|
|
if(!binding)
|
|
return false;
|
|
|
|
binding->key.keysym = 0;
|
|
binding->key.state = 0;
|
|
g_free (binding->key.keycodes);
|
|
binding->key.keycodes = NULL;
|
|
|
|
if (binding->binding_str == NULL ||
|
|
binding->binding_str[0] == '\0' ||
|
|
g_strcmp0 (binding->binding_str, "Disabled") == 0 ||
|
|
g_strcmp0 (binding->binding_str, "disabled") == 0 ) {
|
|
return false;
|
|
}
|
|
success = egg_accelerator_parse_virtual (binding->binding_str,
|
|
&binding->key.keysym,
|
|
&binding->key.keycodes,
|
|
(EggVirtualModifierType *)&binding->key.state);
|
|
if (!success)
|
|
qWarning ("Key binding (%s) is invalid", binding->settings_path);
|
|
|
|
return success;
|
|
}
|
|
|
|
static gint
|
|
compare_bindings (gconstpointer a,
|
|
gconstpointer b)
|
|
{
|
|
Binding *key_a = (Binding *) a;
|
|
char *key_b = (char *) b;
|
|
|
|
return g_strcmp0 (key_b, key_a->settings_path);
|
|
}
|
|
|
|
/**
|
|
* @brief KeybindingsManager::bindings_get_entry
|
|
* Gets binding shortcut data
|
|
* 获取绑定快捷键数据
|
|
* @return
|
|
*/
|
|
bool KeybindingsManager::bindings_get_entry (KeybindingsManager *manager,const char *settings_path)
|
|
{
|
|
GSettings *settings;
|
|
Binding *new_binding;
|
|
GSList *tmp_elem;
|
|
char *action = nullptr;
|
|
char *key = nullptr;
|
|
|
|
if (!settings_path) {
|
|
return false;
|
|
}
|
|
|
|
/* Get entries for this binding
|
|
* 获取此绑定的条目
|
|
*/
|
|
settings = g_settings_new_with_path (CUSTOM_KEYBINDING_SCHEMA, settings_path);
|
|
action = g_settings_get_string (settings, "action");
|
|
key = g_settings_get_string (settings, "binding");
|
|
g_object_unref (settings);
|
|
|
|
if (action==nullptr || key==nullptr)
|
|
{
|
|
qWarning ("Key binding (%s) is incomplete", settings_path);
|
|
return false;
|
|
}
|
|
|
|
tmp_elem = g_slist_find_custom (manager->binding_list,
|
|
settings_path,
|
|
compare_bindings);
|
|
|
|
if (!tmp_elem) {
|
|
new_binding = g_new0 (Binding, 1);
|
|
} else {
|
|
new_binding = (Binding *) tmp_elem->data;
|
|
g_free (new_binding->binding_str);
|
|
g_free (new_binding->action);
|
|
g_free (new_binding->settings_path);
|
|
|
|
new_binding->previous_key.keysym = new_binding->key.keysym;
|
|
new_binding->previous_key.state = new_binding->key.state;
|
|
new_binding->previous_key.keycodes = new_binding->key.keycodes;
|
|
new_binding->key.keycodes = NULL;
|
|
}
|
|
|
|
new_binding->binding_str = key;
|
|
new_binding->action = action;
|
|
new_binding->settings_path = g_strdup (settings_path);
|
|
|
|
if (parse_binding (new_binding)) {
|
|
if (!tmp_elem)
|
|
manager->binding_list = g_slist_prepend (manager->binding_list, new_binding);
|
|
} else {
|
|
g_free (new_binding->binding_str);
|
|
g_free (new_binding->action);
|
|
g_free (new_binding->settings_path);
|
|
g_free (new_binding->previous_key.keycodes);
|
|
g_free (new_binding);
|
|
if (tmp_elem)
|
|
manager->binding_list = g_slist_delete_link (manager->binding_list, tmp_elem);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief KeybindingsManager::bindings_clear
|
|
* Clear binding cache
|
|
* 清除绑定缓存
|
|
*/
|
|
void KeybindingsManager::bindings_clear (KeybindingsManager *manager)
|
|
{
|
|
GSList *l;
|
|
|
|
if (manager->binding_list != NULL)
|
|
{
|
|
for (l = manager->binding_list; l; l = l->next) {
|
|
Binding *b = (Binding *)l->data;
|
|
g_free (b->binding_str);
|
|
g_free (b->action);
|
|
g_free (b->settings_path);
|
|
g_free (b->previous_key.keycodes);
|
|
g_free (b->key.keycodes);
|
|
g_free (b);
|
|
}
|
|
g_slist_free (manager->binding_list);
|
|
manager->binding_list = NULL;
|
|
}
|
|
}
|
|
|
|
void KeybindingsManager::bindings_get_entries (KeybindingsManager *manager)
|
|
{
|
|
gchar **custom_list = NULL;
|
|
gint i;
|
|
|
|
bindings_clear(manager);
|
|
custom_list = dconf_util_list_subdirs (GSETTINGS_KEYBINDINGS_DIR, FALSE);
|
|
|
|
if (custom_list != NULL)
|
|
{
|
|
for (i = 0; custom_list[i] != NULL; i++)
|
|
{
|
|
gchar *settings_path;
|
|
settings_path = g_strdup_printf("%s%s", GSETTINGS_KEYBINDINGS_DIR, custom_list[i]);
|
|
bindings_get_entry (manager,settings_path);
|
|
g_free (settings_path);
|
|
}
|
|
g_strfreev (custom_list);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief same_keycode
|
|
* Is it the same key code
|
|
* 是否为相同的键码
|
|
* @return
|
|
*/
|
|
static bool
|
|
same_keycode (const Key *key, const Key *other)
|
|
{
|
|
if (key->keycodes != NULL && other->keycodes != NULL) {
|
|
unsigned int *c;
|
|
|
|
for (c = key->keycodes; *c; ++c) {
|
|
if (key_uses_keycode (other, *c))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief same_key
|
|
* Compare whether the keys are shortcuts
|
|
* 对比按键是否为快捷键
|
|
* @return
|
|
*/
|
|
static bool same_key (const Key *key, const Key *other)
|
|
{
|
|
if (key->state == other->state) {
|
|
if (key->keycodes != NULL && other->keycodes != NULL) {
|
|
unsigned int *c1, *c2;
|
|
|
|
for (c1 = key->keycodes, c2 = other->keycodes;
|
|
*c1 || *c2; ++c1, ++c2) {
|
|
if (*c1 != *c2)
|
|
return false;
|
|
}
|
|
} else if (key->keycodes != NULL || other->keycodes != NULL)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief KeybindingsManager::key_already_used
|
|
* @English Compare the shortcuts already used
|
|
* @简体 已经使用的快捷键 进行比对
|
|
* @return
|
|
*/
|
|
bool KeybindingsManager::key_already_used (KeybindingsManager*manager,Binding *binding)
|
|
{
|
|
GSList *li;
|
|
|
|
for (li = manager->binding_list; li != NULL; li = li->next) {
|
|
Binding *tmp_binding = (Binding*) li->data;
|
|
|
|
if (tmp_binding != binding &&
|
|
same_keycode (&tmp_binding->key, &binding->key) &&
|
|
tmp_binding->key.state == binding->key.state) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief KeybindingsManager::binding_unregister_keys
|
|
* Unbind key
|
|
* 注销绑定按键
|
|
*/
|
|
void KeybindingsManager::binding_unregister_keys (KeybindingsManager *manager)
|
|
{
|
|
GSList *li;
|
|
bool need_flush = FALSE;
|
|
gdk_x11_display_error_trap_push (gdk_display_get_default());
|
|
try {
|
|
for (li = manager->binding_list; li != NULL; li = li->next) {
|
|
Binding *binding = (Binding *) li->data;
|
|
|
|
if (binding->key.keycodes) {
|
|
need_flush = TRUE;
|
|
grab_key_unsafe (&binding->key, FALSE, manager->screens);
|
|
}
|
|
}
|
|
if (need_flush)
|
|
gdk_display_flush(gdk_display_get_default());
|
|
} catch (...) {
|
|
|
|
}
|
|
gdk_x11_display_error_trap_pop_ignored(gdk_display_get_default());
|
|
}
|
|
|
|
/**
|
|
* @brief KeybindingsManager::binding_register_keys
|
|
* Bind register key
|
|
* 绑定寄存器按键
|
|
*/
|
|
void KeybindingsManager::binding_register_keys (KeybindingsManager *manager)
|
|
{
|
|
GSList *li;
|
|
bool need_flush = false;
|
|
gdk_x11_display_error_trap_push (gdk_display_get_default());
|
|
/* Now check for changes and grab new key if not already used
|
|
* 现在检查更改并获取新密钥(如果尚未使用)
|
|
*/
|
|
for (li = manager->binding_list; li != NULL; li = li->next) {
|
|
Binding *binding = (Binding *) li->data;
|
|
if (!same_key (&binding->previous_key, &binding->key)) {
|
|
|
|
/* Ungrab key if it changed and not clashing with previously set binding */
|
|
if (!key_already_used (manager,binding)) {
|
|
gint i;
|
|
need_flush = true;
|
|
if (binding->previous_key.keycodes) {
|
|
grab_key_unsafe (&binding->previous_key, FALSE, manager->screens);
|
|
}
|
|
grab_key_unsafe (&binding->key, TRUE, manager->screens);
|
|
binding->previous_key.keysym = binding->key.keysym;
|
|
binding->previous_key.state = binding->key.state;
|
|
g_free (binding->previous_key.keycodes);
|
|
|
|
for (i = 0; binding->key.keycodes&&binding->key.keycodes[i]; ++i);
|
|
binding->previous_key.keycodes = g_new0 (guint, i);
|
|
|
|
for (i = 0; binding->key.keycodes&&binding->key.keycodes[i]; ++i)
|
|
binding->previous_key.keycodes[i] = binding->key.keycodes[i];
|
|
|
|
} else
|
|
qDebug ("Key binding (%s) is already in use", binding->binding_str);
|
|
}
|
|
}
|
|
|
|
if (need_flush)
|
|
gdk_display_flush (gdk_display_get_default());
|
|
if(gdk_x11_display_error_trap_pop (gdk_display_get_default()))
|
|
qWarning("Grab failed for some keys, another application may already have access the them.");
|
|
}
|
|
|
|
/**
|
|
* @brief keybindings_filter 键盘事件回调函数
|
|
* @param gdk_xevent GDK XEvent事件
|
|
* @param event GDK Event事件
|
|
* @param manager 类
|
|
* @return 未处理事件,请继续处理
|
|
*/
|
|
GdkFilterReturn
|
|
keybindings_filter (GdkXEvent *gdk_xevent,
|
|
GdkEvent *event,
|
|
KeybindingsManager *manager)
|
|
{
|
|
XEvent *xevent = (XEvent *) gdk_xevent;
|
|
GSList *li;
|
|
|
|
if (xevent->type != KeyPress) {
|
|
return GDK_FILTER_CONTINUE;
|
|
}
|
|
|
|
for (li = manager->binding_list; li != NULL; li = li->next) {
|
|
Binding *binding = (Binding *) li->data;
|
|
|
|
if (match_key (&binding->key, xevent)) {
|
|
GError *error = NULL;
|
|
gboolean retval;
|
|
gchar **argv = NULL;
|
|
|
|
if (binding->action == NULL)
|
|
return GDK_FILTER_CONTINUE;
|
|
|
|
if (!g_shell_parse_argv (binding->action,
|
|
NULL, &argv,
|
|
&error)) {
|
|
return GDK_FILTER_CONTINUE;
|
|
}
|
|
|
|
GDesktopAppInfo *info = g_desktop_app_info_new_from_filename(binding->action);
|
|
retval = g_app_info_launch_uris((GAppInfo *)info, NULL, NULL, NULL);
|
|
|
|
g_strfreev (argv);
|
|
|
|
/* Run failed popup
|
|
* 运行失败弹窗 */
|
|
if (!retval) {
|
|
QString strs = QObject::tr("Error while trying to run \"%1\";\n which is linked to the key \"%2\"").
|
|
arg(binding->action).arg(binding->binding_str);
|
|
QMessageBox *msgbox = new QMessageBox();
|
|
msgbox->setWindowTitle(QObject::tr("Shortcut message box"));
|
|
msgbox->setText(strs);
|
|
msgbox->setStandardButtons(QMessageBox::Yes);
|
|
msgbox->setButtonText(QMessageBox::Yes,QObject::tr("Yes"));
|
|
msgbox->exec();
|
|
delete msgbox;
|
|
}
|
|
return GDK_FILTER_REMOVE;
|
|
}
|
|
}
|
|
return GDK_FILTER_CONTINUE;
|
|
}
|
|
|
|
/**
|
|
* @brief KeybindingsManager::bindings_callback dconf监听快捷键改变回调函数
|
|
* @param client
|
|
* @param prefix
|
|
* @param changes
|
|
* @param tag
|
|
* @param manager 类
|
|
*/
|
|
void KeybindingsManager::bindings_callback (DConfClient *client,
|
|
gchar *prefix,
|
|
GStrv changes,
|
|
gchar *tag,
|
|
KeybindingsManager *manager)
|
|
{
|
|
qDebug ("keybindings: received 'changed' signal from dconf");
|
|
|
|
binding_unregister_keys (manager);
|
|
bindings_get_entries (manager);
|
|
binding_register_keys (manager);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief get_screens_list 获取gdkscreen屏幕并存放链表
|
|
* @return 返回列表
|
|
*/
|
|
void KeybindingsManager::get_screens_list (void)
|
|
{
|
|
GdkScreen *screen = gdk_screen_get_default ();
|
|
screens->append(screen);
|
|
}
|
|
|
|
bool KeybindingsManager::KeybindingsManagerStart()
|
|
{
|
|
qDebug("Keybindings Manager Start");
|
|
QList<GdkScreen*>::iterator l, begin, end;
|
|
GdkDisplay *dpy;
|
|
GdkScreen *screen;
|
|
GdkWindow *window;
|
|
Display *xdpy;
|
|
Window xwindow;
|
|
XWindowAttributes atts;
|
|
|
|
gdk_init(NULL,NULL);
|
|
dpy = gdk_display_get_default ();
|
|
|
|
xdpy = QX11Info::display();
|
|
|
|
screen = gdk_display_get_default_screen(dpy);
|
|
window = gdk_screen_get_root_window (screen);
|
|
xwindow = GDK_WINDOW_XID (window);
|
|
|
|
/* Monitor keyboard events
|
|
* 监听键盘事件
|
|
*/
|
|
gdk_window_add_filter (window,
|
|
(GdkFilterFunc) keybindings_filter,
|
|
this);
|
|
|
|
|
|
try {
|
|
/* Add KeyPressMask to the currently reportable event masks
|
|
* 将KeyPressMask添加到当前可报告的事件掩码
|
|
*/
|
|
gdk_x11_display_error_trap_push (gdk_display_get_default());
|
|
XGetWindowAttributes (xdpy, xwindow, &atts);
|
|
XSelectInput (xdpy, xwindow, atts.your_event_mask | KeyPressMask);
|
|
gdk_x11_display_error_trap_pop_ignored(gdk_display_get_default());
|
|
} catch (int) {
|
|
|
|
}
|
|
screens = new QList<GdkScreen*>();
|
|
get_screens_list ();
|
|
|
|
binding_list = NULL;
|
|
bindings_get_entries (this);
|
|
binding_register_keys(this);
|
|
|
|
/* Link to dconf, receive a shortcut key change signal from dconf
|
|
* 链接dconf, 从dconf收到更改快捷键信号
|
|
*/
|
|
client = dconf_client_new ();
|
|
dconf_client_watch_fast (client, GSETTINGS_KEYBINDINGS_DIR);
|
|
g_signal_connect (client, "changed", G_CALLBACK (bindings_callback), this);
|
|
|
|
return true;
|
|
}
|
|
|
|
void KeybindingsManager::KeybindingsManagerStop()
|
|
{
|
|
CT_SYSLOG(LOG_DEBUG,"Stopping keybindings manager");
|
|
|
|
if (client != NULL) {
|
|
g_object_unref (client);
|
|
client = NULL;
|
|
}
|
|
QList<GdkScreen*>::iterator l,end;
|
|
|
|
l = screens->begin();
|
|
GdkScreen *screen = *l;
|
|
gdk_window_remove_filter (gdk_screen_get_root_window (screen),
|
|
(GdkFilterFunc) keybindings_filter,
|
|
this);
|
|
|
|
|
|
binding_unregister_keys (this);
|
|
bindings_clear (this);
|
|
|
|
screens->clear();
|
|
delete screens;
|
|
screens = NULL;
|
|
}
|