如何在Unity面板中添加键盘修改器状态小程序?


18

我是一个KDE用户,正在考虑迁移到Unity。由于手动禁用,我使用了粘滞键,并且在KDE中,系统面板中有一个applet,其中显示了哪些修改器键处于活动状态。我记得Gnome也具有此功能,Windows和OS X也具有此功能。

如何将一个键盘修改器状态的小程序广告到Unity中的面板?

澄清:我已经启用了粘滞键。我在问如何添加一个指示修改键状态的小程序。当按下Shift键,按下Alt键,按下Tux键以及按下Ctrl键时,此指示器将显示。此小程序存在于所有主要的桌面环境(KDE,Windows,Mac OSX和Gnome)中。桌面的可访问性是必需的。

这是键盘修改器状态小程序的图像,位于键盘布局指示器小程序的旁边。代表的改性剂,由左到右,ShiftCtrlAltI-dont-know-this-oneTux/WinNumLock,和CapsLock。可以看到NumLock键处于活动状态。

在此处输入图片说明


Errr ...在此方面似乎有很大的差距...但是,当按下修改键时会发出哔哔声。
Wilf 2014年

@wilf如果我没记错的话,Unity中有一个可访问性图标(与Gnome 3相同),但是没有“额外”图标来通知用户状态。
Braiam 2014年

4
@Takkat:谢谢你的建议。indicator-keylock只显示有传统键盘本身上的状态指示灯这些键的状态:CapsLockScrollLockNumLock。我需要一个指示器,其显示了标准修改键的状态:ShiftCtrlTuxAlt。所有主要台式机(KDE,Windows,Mac OSX)均具有此指示小程序。
dotancohen 2014年

1
我相信您所引用的KDE工具的数据包名称也是如此,plasma-widget-kbstate并且在软件中心中进行快速搜索确实并没有引起任何等效结果
Daniel W.

2
@shengy:我使用的是KB State等离体。如果您在Kubuntu上,请使用进行安装sudo apt-get install plasma-widget-kbstate
dotancohen 2015年

Answers:


7

这是Unity中的一个突出问题:

下面的代码已更新,现在可以使用图标显示状态。但有时可能会变慢,因为我必须更新硬盘驱动器上的图标文件,然后再次重新加载它。(请参阅中有关此问题/限制的注释libappindicator

在webupd8 ppa上提供了打包好的版本(感谢Alin Andrei / Andrew /)

sudo add-apt-repository ppa:nilarimogard/webupd8
sudo apt-get update
sudo apt-get install indicator-xkbmod

参考:适用于Ubuntu的键盘修改器状态指示器:Xkbmod指示器


原始答案:

这并非是对该问题的规范答案。它可以算作解决方法。跳一个人为此编写复杂的解决方案。

这是Unity的简单原型键盘修饰符指示符。

从左开始的图像:图标,Shift,锁定大写,Ctrl,Alt,超级,锁定AltGr(小圆圈表示锁定状态)

unity-xkbmod原型的屏幕截图

源文件unity-xkbmod.c

/*
 * unity-xkbmod.c
 *
 * Copyright 2014 Sneetsher <sneetsher@localhost>
 *
 * 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 <string.h>

#include <X11/XKBlib.h>

#include <glib/gprintf.h>
#include <gtk/gtk.h>
#include <libappindicator/app-indicator.h>

//callback data structure
typedef struct _AppData {
  Display *_display;
  int *_deviceId;
  AppIndicator *indicator;
} AppData;

//menu ui
static GtkActionEntry entries[] = {
  { "Quit",     "application-exit", "_Quit", "<control>Q",
    "Exit the application", G_CALLBACK (gtk_main_quit) },
};

static guint n_entries = G_N_ELEMENTS (entries);

static const gchar *ui_info =
"<ui>"
"  <popup name='IndicatorPopup'>"
"    <menuitem action='Quit' />"
"  </popup>"
"</ui>";

//callback function, get xkb state, update indicator label (icon have limitation)
static gboolean update_xkb_state (gpointer data)
{
  //get xkb state
  XkbStateRec xkbState;
  XkbGetState(((AppData*) data)->_display, *(((AppData*) data)->_deviceId), &xkbState);

  //construct label
  GString *label = g_string_new("");

  register int i;
  unsigned bit;

  //loop taken from xkbwatch source
  for (i = XkbNumModifiers - 1, bit = 0x80; i >= 0; i--, bit >>= 1)
  {
    //printf("base%d %s  ", i, (xkbState.base_mods & bit) ? "on " : "off");
    //printf("latched%d %s  ", i, (xkbState.latched_mods & bit) ? "on " : "off");
    //printf("locked%d %s  ", i, (xkbState.locked_mods & bit) ? "on " : "off");
    //printf("effective%d %s  ", i, (xkbState.mods & bit) ? "on " : "off");
    //printf("compat%d %s\n", i, (xkbState.compat_state & bit) ? "on " : "off");

    //todo: change constant with xkb modifier constant (defined in the headers)
    // show effective modifier stat
    switch (i)
    {
      case 7:
        g_string_prepend (label,  ((xkbState.mods & bit) ? "⎇" : ""));
        break;
      case 6:
        g_string_prepend (label,  ((xkbState.mods & bit) ? "⌘" : ""));
        break;
      case 5:
        g_string_prepend (label,  ((xkbState.mods & bit) ? "5" : ""));
        break;
      case 4:
        g_string_prepend (label,  ((xkbState.mods & bit) ? "①" : ""));
        break;
      case 3:
        g_string_prepend (label,  ((xkbState.mods & bit) ? "⌥" : ""));
        break;
      case 2:
        g_string_prepend (label,  ((xkbState.mods & bit) ? "⋀" : ""));
        break;
      case 1:
        g_string_prepend (label,  ((xkbState.mods & bit) ? "⇬" : ""));
        break;
      case 0:
        g_string_prepend (label,  ((xkbState.mods & bit) ? "⇧" : ""));
        break;
      default:
        break;
    };

    // show if modifier is locked
    g_string_prepend (label,  ((xkbState.locked_mods & bit) ? " ˳" : " "));
  }

  //g_string_prepend (label,  "");
  app_indicator_set_label (((AppData*) data)->indicator, label->str, NULL);

  //g_free(label);
  return TRUE;
}


int main (int argc, char **argv)
{
  AppData appdata;
  Display *_display;
  int _deviceId;

  char* displayName = strdup("");
  int eventCode;
  int errorReturn;
  int major = XkbMajorVersion;
  int minor = XkbMinorVersion;;
  int reasonReturn;


  AppIndicator *indicator;
  GtkWidget *indicator_menu;
  GtkUIManager *uim;
  GtkActionGroup *action_group;
  GError *error = NULL;

  gtk_init (&argc, &argv);


  XkbIgnoreExtension(False);

  g_printf("Xkb client lib ver: %d.%d\n" , major , minor );
  _display = XkbOpenDisplay(displayName, &eventCode, &errorReturn,
                            &major, &minor, &reasonReturn);
  g_printf("Xkb server lib ver: %d.%d\n" , major , minor );

  if (reasonReturn != XkbOD_Success)
  {
    g_printf("Unable to open display!\n");
    return 1;
  }

  XkbDescRec* kbdDescPtr = XkbAllocKeyboard();
  if (kbdDescPtr == NULL)
  {
    g_printf ("Failed to get keyboard description.\n");
    return 2;
  }
  kbdDescPtr->dpy = _display;
  _deviceId = kbdDescPtr->device_spec;

  /*
  //no need for event listener, used gtk_timeout timer
  XkbSelectEventDetails(_display, XkbUseCoreKbd, XkbStateNotify,
                     XkbAllStateComponentsMask, XkbGroupStateMask);
  */


  action_group = gtk_action_group_new ("AppActions");
  gtk_action_group_add_actions (action_group, entries, n_entries, NULL);

  indicator = app_indicator_new_with_path (
                                        "Simple XKB Modifier Indicator",
                                        "icon",
                                        APP_INDICATOR_CATEGORY_HARDWARE,
                                        g_get_current_dir());

  uim = gtk_ui_manager_new ();
  gtk_ui_manager_insert_action_group (uim, action_group, 0);
  if (!gtk_ui_manager_add_ui_from_string (uim, ui_info, -1, &error))
  {
    g_printf ("Failed to build menus: %s\n", error->message);
    g_error_free (error);
    error = NULL;
    return 3;
  }

  indicator_menu = gtk_ui_manager_get_widget (uim, "/ui/IndicatorPopup");
  app_indicator_set_menu (indicator, GTK_MENU (indicator_menu));
  app_indicator_set_status (indicator, APP_INDICATOR_STATUS_ACTIVE);

  //app_indicator_set_label (indicator, " ⇧ ⋀ ⌥ ⎇  ⌘ ", NULL);
  //symbols: shift U21E7 ctrl U22C0 alt/altgr U2325 U2387  cmd U2318
  //from font: DejaVu Sans

  appdata._display = _display;
  appdata._deviceId = &_deviceId;
  appdata.indicator = indicator;
  gtk_timeout_add (120, (GtkFunction) update_xkb_state, &appdata);
  //nice for realtime tasks, to replace gtk_timeout
  //gtk_idle_add ((GtkFunction) idle_func, &appdata);

  gtk_main ();

  XCloseDisplay (_display);
  return 0;
}
  1. 安装所需的标题/库:(不确定是否遗漏任何内容)

    sudo apt-get install libx11-dev libappindicator-dev libgtk2.0-dev
    
  2. 编译:

    gcc -Wall unity-xkbmod.c -o unity-xkbmod `pkg-config --cflags --libs appindicator-0.1` -lX11
    
  3. 跑:

    ./unity-xkbmod
    

注意:

  • libappindicator用于Unity指示器的功能缺少重要的功能,因此无法轻松移植其他台式机指示器。请参阅Bug#812067需要的API:pixbuf图标设置支持

    如果没有此功能,我们需要在启用粘滞键的情况下(Shift,Ctrl,Alt,AltGr,Super);我们每个都有3个主要状态(关闭,开启/锁定,锁定)。因此,应生成3 ^ 5个组合图标。(一般情况下 3x5单个图标)

    这就是为什么我将指示符标签与DejaVu Sans字体的符号一起使用的原因。

  • 要放置图标,请将其放置在同一文件夹中并命名 icon.*。接受的格式:png,svg,ico,xpm ...

    如果您不喜欢任何图标,只需创建一个1x1 px的图像即可。

参考文献:


好的谢谢!我将尝试一下,并尽快回复您。伟大的工作,Sneetsher!
dotancohen 2014年

你在偷笑!
dotancohen 2014年

是的,谢谢@dotancohen。所以至少您可以使用Unity,如果没有固定位置的图标可能会很难看吗?
user.dz 2014年

1
我不介意丑陋,你应该看到我的照片!我已经在Ubuntu bugtracker上提到了这个问题。
dotancohen 2014年

1
Sneetsher,我想再次感谢您。我现在已经搬到了Unity,如果没有这个出色的应用程序,搬迁是不可能的。我还从代码中学到了很多东西。谢谢!
dotancohen

2

另一个不是完美的解决方案,但是有些解决方案可能会发现它很有用,因为它可以像在KDE中一样具有完整的功能,例如通过单击激活mod。

  1. 安装kbstate小程序

    sudo apt-get install plasma-widget-kbstate kde-workspace-bin
    
  2. plasma-windowed播放器中运行

    • 常规窗口

      plasma-windowed kbstate
      

      Xubuntu中plasma-widget-kbstate的屏幕截图

    • 无边窗

      plasma-windowed --noborder kbstate
      

      Unity中无边界plasma-widget-kbstate的屏幕截图

我没有很多时间玩,但是 wmctrl可能有助于定位,调整大小并使其在发布时居于首位。

参考:用 什么命令启动kde质体和kickstart菜单


1

我搜索了Ubuntu粘滞键监控器,发现可能对您有所帮助:http : //code.google.com/p/key-mon/

展示案例截图

尝试跑步

key-mon --sticky 支持粘滞键。

参考:http : //code.google.com/p/key-mon/

请注意,可通过软件中心获得的版本是1.6-0ubuntu1。2011年6月发布,不支持--sticky开关。如果指示器与上述指示器完全相同,则您的版本较旧,请尝试在http://code.google.com/p/key-mon/上使用最新版本。撰写本文时,它是keymon_1.17-1_all.deb 229 KB已发布2014年1月3日。测试和确认了对--sticky开关的支持。


1
很不错的发现,但是key-mon显示了按下哪个按钮,而不显示修改键的状态。区别在于,在key-mon按下按钮1秒钟后,显示屏返回到未按下状态。当下一次按键发生时,键盘修改器状态小程序将使显示返回到未按下状态,从而禁用“按下”状态
dotancohen 2014年

重新编辑答案。
极客长老

对困惑感到抱歉。我已经(再次)编辑了答案,以准确提供您所需要的。
年长者极客2014年

1
谢谢极客长老。该--sticky选项似乎通过观察其他键的状态来猜测修饰键的状态,而不是像正确的键盘修饰符状态小程序一样查询正确的接口。这可以通过按两次修改键来看到:第一次启用小程序中的指示器,但是第二次不释放它。因此,错误地将状态报告为处于活动状态。此相关的错误部分解决了该问题,我将在此处填写详细信息以及其他错误。谢谢。
dotancohen

1
@dotancohen我根据您的陈述进行了测试:“当下一次按键发生时,键盘修改器状态小程序将使显示返回到未按下状态,从而禁用'已按下'状态。” 从来没有想到,有人会连续两次连续按下修改键。我对监督表示歉意。
极客长老
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.