Эта тема очень интересная, поэтому именно ей я посвящу этот топик после длительного молчания. Думаю, за свою практику программисту не может не потребоваться использование глобальных клавиш (hotkeys, shortcuts) в приложении. Благодаря им пользователь может сэкономить своё бесценное время, да и функционал программы повышается. Моей задачей было найти способ регистрирования в системе комбинации клавиш и их дальнейшее транслирование. Задача не очень тяжелая, если выполнять под конкретную ОС, а вот сделать кроссплатформенность уже не так легко, нужно учесть некоторые факторы.

Давайте возьмемся за X11. Существует функция XGrabKey, с помощью которой и можно зарегистрировать сочетания клавиш в X11. Для её использования нужно подключить X11-headers. Вот пример кода, который должен работать:

#include <QtGui>
#include <QX11Info>
 
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
 
#ifdef KeyPress
  const int XKeyPress   = KeyPress;
  const int XKeyRelease = KeyRelease;
#undef KeyPress
#undef KeyRelease
#endif
 
class X11ShortCut :QMainWindow
{
  Q_OBJECT
 
  public:
    X11ShortCut(QWidget *parent)
    : QMainWindow(parent)
    {
      qApp->installEventFilter(this);
 
      m_keyCode = XKeysymToKeycode(QX11Info::display(), XK_F11);
      XGrabKey(QX11Info::display(), m_keyCode, ControlMask|ShiftMask, QX11Info::appRootWindow(), False, GrabModeAsync, GrabModeAsync);
      XFlush(QX11Info::display());
    }
 
    ~X11ShortCut()
    {
      XUngrabKey(QX11Info::display(), m_keyCode, ControlMask|ShiftMask, QX11Info::appRootWindow());
    }
 
    bool X11ShortCut::eventFilter(QObject *, QEvent *event)
    {
      if(event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        int qtKey = keyEvent->key();
        if(keyEvent->modifiers() == (Qt::ControlModifier|Qt::ShiftModifier) && qtKey == Qt::Key_F11) {
          insertPlainText("Ctrl+Shift+F11 pressed!\n");
          return false;
        }
      }
      return true;
    }
 
  private:
    int m_keyCode;
};

Здесь и нечего комментировать.

m_keyCode = XkeysymToKeycode(QX11Info::display(), XK_F11);

Получаем код клавиши F11. Далее регистрируем клавишу в системе с помощью главной функции — XGrabKey. В деструкторе сниманием хоткей. А далее у нас идет фильтр событий, где мы ожидаем известия о нажатии нужного нам сочетания. Более подробнее можно почитать здесь: http://www.xfree.org/current/XGrabKey.3.html.

Теперь о регистрации “горячих” клавиш в OS Windows. Будем использовать WinApi. Суть та же, мы регистрируем комбинацию с помощью ф-ции WinApi RegisterHotKey() и ожидаем прибытия сообщения WM_HOTKEY (http://msdn.microsoft.com/en-us/library/ms646279(VS.85).aspx).

BOOL RegisterHotKey
(
  HWND hWnd,
  int id,
  UINT fsModifiers,
  UINT vk
);
  • hWnd — окно, которое должно получать сообщения о активировании глобальной комбинации
  • id — уникальный идентификатор, чтобы различать hotkeys между собой
  • fsModifiers — модификаторы: MOD_ALT, MOD_CONTROL, MOD_SHIFT
  • vk — код виртуальной клавиши
#DEFINE HOTKEYHIDE 0×000001
RegisterHotKey(hwndMain,HOTKEYHIDE,MOD_CONTROL,VK_F12);

Как отлавливать сообщения я уже и не помню, это будет Вашим домашним заданием :D.

Ещё есть популярная ОС — MacOS, но под неё я ни разу ничего не программировал, поэтому и не раскажу. Читайте далее о кроссплатформенном решении.

Так вот, понадобилось мне в одном проекте использовать hotkeys +программа кроссплатформенная, поэтому желательно было найти соответственный фреймворк. Гугл, как всегда, помог мне (хоть и потратил я немало времени). Есть отличное дополнение для Qt, которое называется libQxt (http://libqxt.org). Качаем от туда архив, ставим. Устанавливается это “сокровище” довольно легко:

$ ./configure && make && make install

Единственное, я использую QtCreator. В его поставке идет “своя” утилита qmake, поэтому вам нужно её явно указать. Установка немного изменяется:

$ ./configure -qmake-bin /home/%user%/%QtSDk%/qt/bin/qmake
$ make && make install

Для интеграции в проект нужно в .pro-файл дописать:

CONFIG += qxt
QXT += core gui

Документация здесь: http://doc.libqxt.org.

Переходим к делу. В версии 0.5, для использования глобальных клавиш, вам надо создавать объект QxtApplication, а не Qapplication. По словам разработчиков, в версии 0.6 они уберут эту зависимость. Поэтому файл main.cpp изменяется:

#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QxtApplication>
int main(int argc, char *argv[])
{
  QxtApplication a(argc, argv);
  MainWindow w;
  w.show();
  return a.exec();
}

Далее переходим в класс MainWindow. В .h-файле дописываем:

#include <QxtGlobalShortcut>
 
class MainWindow : public QMainWindow
{private:
  QString m_shortcutDef;
  QxtGlobalShortcut m_hotkeyHandle;private slots:
  void hotkeyPressed();
}

в .cpp-файле (в конструкторе инициализируем все):

m_shortcutDef = QString("Ctrl+Shift+X");
m_hotkeyHandle.setShortcut( QKeySequence(m_shortcutDef) );
m_hotkeyHandle.setEnabled(true);
connect( &m_hotkeyHandle, SIGNAL(activated()), this, SLOT(hotkeyPressed()) );

И, собственно, сам обработчик сообщения:

void MainWindow::hotkeyPressed()
{
  // Achtung
}

Вот и всё. Этот код вы можете использовать для X11, Windows, MacOS.

Удачи!