Программист, реверс-инженер
0,0
рейтинг
27 февраля 2012 в 15:04

Разработка → Простой индикатор раскладки клавиатуры в курсоре на С++

Тема отображения текущей раскладки беспокоила хабрасообщество уже не раз. Я опробовал множество существующих решений, но по разным причинам они меня не устроили. Чтобы подобрать для себя наиболее удобный вариант отображения текущей раскладки, я написал небольшое приложение на C++, которое при нажатии на левый Shift отображает язык в системном курсоре редактирования текста. Менее 100 строк кода и около 4 килобайт в скомпилированном виде — на основе этого вы можете достаточно просто реализовать свой взгляд на то, как на самом деле должна выглядеть индикация текущей раскладки.

Как это сделано?


Поскольку ничего кроме WinAPI нам не нужно, программа компилируется без RTL в Visual Studio 2010. Таким образом мы получаем очень маленький объём исполняемого файла. Для отслеживания нажатия на Shift устанавливается клавиатурный хук без использования dll. Для изменения системного курсора редактирования текста используется функция SetSystemCursor — она позволяет заменить необходимый тип стандартного курсора сразу во всех приложениях. Таким образом мы избегаем необходимости тратить дополнительные ресурсы на отображение индикатора на экране — этим занимается сама система. По умолчанию курсор редактирования текста без тени и инвертирует цвет каждого пикселя под собой. Такое возможно только в двухцветных курсорах, поэтому чтобы сохранить это полезное свойство, мы должны сделать наш индикатор чёрно-белым.

Где можно скачать результат?


Исходный код можно увидеть в файле main.cpp (на bitbucket.org) или скачать в виде проекта langcursor-1.0-src.zip (5 кб). Внимание! Проект компилируется только в Release-версии (из-за отсутствия RTL).
Готовый исполняемый файл в архиве: langcursor-1.0-bin.zip (1 кб).

Что я могу изменить?


Если вы хотите добавить отображение других раскладок, достаточно добавить ресурс курсора с идентификатором раскладки в качестве имени (можно задать в файле resource.h, значение из последней колонки таблицы идентификаторов языков). Если вам не нравится маленькая чёрно-белая индикация языка, вы можете заменить курсоры на свои, например, с цветными флагами. Однако, при использовании цветных курсоров вы не сможете добиться инвертирования цветов под курсором и курсор начнёт отбрасывать тень (если её не выключить в настройках).
Более того, если вы знакомы с C/С++, вы можете полностью изменить код под себя! Пробуйте, экспериментируйте, делитесь вашими наработками! Все люди разные, что подходит одним — не факт, что подойдёт другим. Но вместе мы, надеюсь, сможем придумать наиболее удобное решение :)

Модифицированные версии


Постоянное отображение иконки языка без необходимости нажимать Shift здесь.
Версия с постоянным отображением и поддержкой украинского языка здесь.
Евгений Врублевский @VEG
карма
352,0
рейтинг 0,0
Программист, реверс-инженер
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (73)

  • –8
    Хорошо) Но почему ".cpp"?
    • +4
      Чтобы использовать некоторые удобные возможности C++, которые доступны при отключении RTL.
      • +1
        Вынесли бы куда-нибудь повыше, что это решение Windows-only? А то первая видимая строчка про WinAPI (которая наводит на такие мысли) — под катом…
        • 0
          Скриншот для лого сделан в Windows 7, мне кажется очертания курсора узнаваемы :)
    • 0
      А почему бы и нет? И кто же, если не он? :)
    • +3
      Сразу в заголовке было написано просто C, я позже исправил на C++ — чтобы было понятно, что означает этот комментарий.
  • 0
    Win7 Ult SP1 x64 RUS. Щито-то не арбайтен. В процессах висит, толку ноль. Не в редакторах, ни в эксплорере винды.
    • 0
      У меня Windows 7 SP Professional (английская, x86), плюс тестировал на Windows XP — работало. Вы нажимаете Shift, когда курсор находится над полем для ввода? Дело в том, что индикация появляется только по нажатию на Shift. Я это сделал для того, чтобы в браузере при чтении текста индикация не отображалась на этом же курсоре.
      • +1
        у меня Win7 Ult x86 Rus — работает. Наверно дело в x64…
        Кстати, довольно удобно. Спасибо за программку
      • +2
        Почему-то подумалось, что будет реагировать на alt + shift / ctrl + shift. Так что звиняйте ради Бога.

        Имхо, не совсем удобно для проверки нажимать лишний раз шифт. Понятно, что привыкнуть можно, но все же, было бы лучше отслеживать системное изменение курсора со стандартного на «I» и тут уже на постоянку подсовывать отображение идентификатора текущей раскладки. Но это так, мысли в слух.
        • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            Соглашусь с тем, что постоянно отображать — удобнее.
            А тем, кто в силу каких-то причин не пользуется мышкой, было бы удобно отображать прямо в поле ввода.
            Можно добавить иконку в трей и настройки
          • 0
            Ниже в комментариях есть реализация подобного поведения.
            • 0
              Как вариант можно генерировать изображение курсора при запуске программы для разных раскладок и хранить их в памяти.

              Реализовать, думаю можно через сплайсиг SetCursor, но возможно есть получше вариант.
  • 0
    WinXP x86 Работает отлично. Спасибо за маленькую полезную программку, которая вовсе не мешает.
  • +9
    Чтобы язык постоянно отображался рядом с курсором курсором редактирования текста, достаточно просто убрать хук на клавиатуру и запустить таймер без ограничений по времени. Кода получится ещё меньше.
    #define OEMRESOURCE
    #include <windows.h>
    
    HINSTANCE   g_instance;
    HCURSOR     g_hc_ibeam;
    UINT_PTR    g_timer = NULL;
    DWORD       g_layout = 0;
    
    void CALLBACK UpdateTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
    {
        int layout = (int) GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), NULL)) & 0xFFFF;
        if (g_layout != layout)
        {
            HCURSOR hc_new = LoadCursor(g_instance, MAKEINTRESOURCE(layout));
            if (hc_new)
            {
                SetSystemCursor(hc_new, OCR_IBEAM);
            }
            else
            {
                SetSystemCursor(CopyCursor(g_hc_ibeam), OCR_IBEAM);
            }
        }
    }
    
    int Main()
    {
        HANDLE mutex = CreateMutex(NULL, FALSE, "LangCursor");
        if (GetLastError() == ERROR_ALREADY_EXISTS || GetLastError() == ERROR_ACCESS_DENIED) return 1;
    
        g_hc_ibeam = CopyCursor(LoadCursor(NULL, IDC_IBEAM));
        if (!g_hc_ibeam) return 1;
    
        g_instance = GetModuleHandle(NULL);
        g_timer = SetTimer(NULL, g_timer, 200, UpdateTimer);
        if (!g_timer) return 1;
    
        MSG msg;
        while (GetMessage(&msg,0,0,0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        
        DestroyCursor(g_hc_ibeam);
        return 0;
    }
    
    EXTERN_C void WINAPI WinMainCRTStartup()
    {
        ExitProcess(Main());
    }
    

    Правда это не очень красивое решение. Более грамотно было бы внедрять во все процессы dll, которая отлавливала бы событие изменения языка в приложении и меняла соответствующим образом курсор. Заметка на будущее.
    • +7
      Скомпилированная версия: langcursor-permanent-bin.zip (1 кб).
      • +3
        Спасибо!
      • 0
        А будьте любезны, скомпилируйте для трех языков: английский, русский, украинский.

        PS: VEG, спасибо за отличную утилиту!
    • +1
      После
          if (g_layout != layout)
          {
      
      забыл строчку
              g_layout = layout;
      
      Из-за этой ошибки курсор устанавливается даже тогда, когда это не нужно. В архиве уже исправленная версия.
    • 0
      А зачем внедряться во все процессы? Может быть проще и не менее грамотно ставить хук на WH_SHELL, событие HSHELL_LANGUAGE?
      Сам не пробовал, но мне кажется, что должно работать как следует.
      • +1
        К сожалению, не удалось найти внятной документации об этом на MSDN. Судя по этому обсуждению событие HSHELL_LANGUAGE можно отловить только для своего процесса. Хотя это необходимо проверить.
  • +5
    А для linux(ubuntu) есть аналогичное решение?
    • +3
      Ну что, написать что ли? Какой оболочкой пользуемся?
      • 0
        Если это не в напряг, было бы шикарно. Unity
        • 0
          Обещать ничего не могу: в первую очередь мне просто интересно разобраться с аналогичной темой. Если выйдет что-то интересное — поделюсь.
  • 0
    Еще бы как-то сделать, чтобы для каждой закладки хрома сохранялась раскладка ;)
  • 0
    Отличное дополнение к capswitch.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Перед этим вы запускали перманентную версию? Если да, то после её убийства до перезагрузки остаётся курсор с последним языком, и обычная версия использует этот курсор как основной. Чтобы сбросить без перезагрузки, зайдите в настройки курсоров мыши и обновите там курсор редактирования текста сперва на какой-нибудь левый, затем опять на стандартный.
      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          Программа при загрузке запоминает текущий системный курсор, при изменении языка она его подменяет, и если в этот момент убить программу, то системный курсор останется подменённым на версию с иконкой языка.
          Если вы пытаетесь выбрать язык, для которого нет курсора (а курсор сейчас есть только для русского и для английского), то программа отображает стандартный курсор, который она сохранила при загрузке.
          Для нормальной работы после убийства программы нужно сперва сбросить курсор на нормальный, и только после этого запускать новый экземпляр.
          Чтобы этого избежать, нужно будет реализовать нормальную процедуру выхода, когда курсор восстанавливался бы сам. В ближайшем обновлении реализуем.
  • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      К сожалению, не знаю с чем может быть связана эта проблема :( Попробуйте добавить такой ключ в реестр и перезагрузиться (должно отключить сервисы для восточноазиатских языков).
      Windows Registry Editor Version 5.00

      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IMM]
      "LoadIMM"=dword:00000000
      • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Рядом с мышиным курсором что-то показывать не очень практично, он может быть где угодно во время ввода текста, а может быть даже скрыт (некоторые программы умеют его прятать когда начинается набор текста). Я когда-то пробовал возле каретки эту информацию выводить, но к сожалению многие приложения не используют стандартную каретку (например тот же офис или приложения на Qt) и как в них вклиниваться осталось для меня непонятным.
    • 0
      В среде, где достаточно RU+EN+Compose, достаточно прицепить переключение раскладки, скажем, на CapsLock, и по светодиоду на границе поля зрения всегда можно определить текущую раскладку.
      • +1
        У меня CapsLock в поле зрения не попадает :)
        • 0
          особенно это касается ноутюуков, где индикатор CapsLock, паример может быть возле самой клавиши CapsLock, которая практически всегда прикрыта рукой при наборе. а убирать руку и косить взгляд как-то неудобно. Вот на Compaq 615 для примера.
  • 0
    У меня всегда один и тот же язык, но с двумя раскладками — RU и US International. Соответственно, возле курсора висит всегда RU.
    • +3
      Если не секрет, зачем такое понадобилось? :)
      • 0
        Семерка, при установке, спрашивает про язык и раскладку.
        Автоматом выбираю раскладку «США международная», а остальные поля не трогаю. Получаю в результате два языка — русский и английский, а в русском еще и две раскладки. Мне проще удалить лишний язык, который не стоит основным и оставить две раскладки в одном языке.
        Потом привык и всегда теперь так ставлю… Ничем не мешает в жизни — раскладки переключаются по CTRL+SHIFT, никакой разницы не замечаю.
        • +2
          Может быть тогда это повод сделать как положено :)
        • +1
          Вам не кажется, что вы как-то неправильно используете концепцию языков и раскладок? :)
          • 0
            Строго говоря — кажется, но разницы я не замечал до сих пор.
          • 0
            А в чем она?
            • 0
              В том, что в русском языке нет латиницы. А раскладка есть.
            • 0
              Язык – это язык, например русский, английский. А раскладка – это конкретное отображение алфавита языка на клавишы: например виндовый йцукен, или вообще дворак.

              У меня например на рабочей винде стоит два языка, причем на русском по умолчанию Mac'овая раскладка, при этом можно переключиться и на обычную виндовую, итого 2 языка и 3 раскладки.
  • –1
    а под Мак?
  • 0
    Я вот никак не пойму,… нафига мьютекс.
    • +2
      Чтобы гарантировать единственный инстанс приложения.
  • +1
    Отличная вещь.
    Правда попытка приладить к дикой связке с терминалом не дала нужного результата, но и без этого стало удобно на обычной машине.
  • 0
    Если кто-то пытается добавить курсоры со своими языками, небольшая памятка:
    Не забывайте в файле resource.h установить правильный идентификатор курсора, он должен соответствовать идентификатору языка из этой таблицы: msdn.microsoft.com/en-us/goglobal/bb964664 (последняя колонка). Сейчас уже есть идентификаторы русского и американского английского:
    #define IDC_CURSOR1                     1033
    #define IDC_CURSOR2                     1049
    
  • +1
    Есть такая утилита, Aml Maple с гораздо более продвинутым функционалом. Существует она, кстати, со времён Windows 9x.
    • 0
      Перед тем, как писать свой велосипед, я пробовал эту программу. При отображении языка рядом с курсором он меняет свой вид, отбрасывает тень и не инвертирует под собой цвета, что не очень удобно.
  • 0
    А про пунто-свитчер тут правда никто не слышал?
    В IDE автоматическую смену раскладки отключаем, т.к. там пунто часто тупит, однако возможность менять раскладку только что написанного текста нажатием одной кнопки остается и в выключенном режиме. Зато исправлять уже написанное мне кажется удобнее, чем превентивно проверять раскладку.
    • 0
      Видимо слышали, видимо пользовались, видимо очень не понравилось. Если что — говорю за себя и некоторых из своего окружения от кого слышал отзывы. да, список исключений куда вносятся putty, есть, но все равно, мне на рус/укр/англ жутко неудобно. Проще оказалось переключать раскладку только по Ctrl+1,2,3 рус, англ, укр в самой винде.
      • 0
        Если нравится переключать «на» язык, то могу поделиться своей программкой, в которой делал настройку Caps — англ, Shift+Caps — рус, Ctrl+Caps — укр. Если быть совсем точным, то сочетания там могут быть любыми. Давно была идея довести эту прожку до ума да выложить в паблик под какой-нибудь свободной лицензией, да всё никак со временем :)
        • 0
          Я сейчас C# изучаю активно, было бы интересно посмотреть…
          • 0
            Эм… Я только не понял почему C#, если честно. Там всё на C++.
            • 0
              есть кому подсказать и перевести :)
  • 0
    Отличная утилита! Приходится использовать эстонскую раскладку — почти все буквы те-же, но спецсимволы совершенно на других местах.
  • +1
    А можно попросить под x64 собрать?
  • 0
    Было бы круто менять цвет курсора (не текстового а обычного) в зависимости от языка
    • 0
      В принципе это не особо сложно сделать, немного изменив приведённый код.
  • 0
    А можно менять цвет мигающей «вертикальной черты» что в поле ввода.
    Скажем русский красный английский синий. Ну и пожирней чуть-чуть сделать.
    • 0
      Это затруднительно сделать универсальным способом, потому что очень многие программы используют собственный рендеринг каретки. По сути для каждого случая придётся писать отдельный код.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.