Get Capslock state on Linux with Wayland - c++

I am struggling with the following task: for our cross-platform application I want to enable a capslock warning for the user. This works perfectly on Windows and macOS and is a bit unnecessarily complicated but doable on Linux with X11, though I cannot find out how to do it properly on Wayland.
We are using Qt5, so the more Qt APIs I can use for this, the better. I see that Qt has a very extensive Wayland framework, but it seems to be designed primarily for writing your own compositor and not for accessing specifics of the underlying platform plugin.
Here's the code as far as I have it:
#include <QGuiApplication>
#include <qpa/qplatformnativeinterface.h>
// namespace required to avoid name clashes with declarations in XKBlib.h
namespace X11
{
#include <X11/XKBlib.h>
}
void checkCapslockState()
{
// ... Windows and macOS one-liners
// Here starts the Linux mess.
// At least I can query the display with this for both X11 and Wayland.
QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface();
auto* display = native->nativeResourceForWindow("display", nullptr);
if (!display) {
return;
}
const QString platform = QGuiApplication::platformName();
if (platform == "xcb") {
unsigned state = 0;
if (X11::XkbGetIndicatorState(reinterpret_cast<X11::Display*>(display), XkbUseCoreKbd, &state) == Success) {
// works fine
newCapslockState = ((state & 1u) != 0);
}
} else if (platform == "wayland") {
// but how to proceed here?
// struct wl_display* waylandDisplay = reinterpret_cast<struct wl_display*>(display);
}
// ...
}
My understanding is that I have to get hold of the Wayland wl_seat object somehow, which holds information about the wl_keyboard. However, I cannot find a way to access these objects from the wl_display object alone without instantiating all sorts of contexts. The Qt application itself is already running as a Wayland client, so there should be a way to access these objects, I would assume. Unfortunately, the Wayland documentation on this is very sparse and quite opaque for someone not familiar with the whole architecture and Wayland's user base is still too small that things pop up on Google.

I found a solution, but I am far from satisfied with it.
I am using KWayland here, but it is possible to use the plain Wayland C API, of course. Be prepared to write 200-300 additional lines of boilerplate code, though. KWayland abstracts that a bit, but it is still pretty verbose. Even the X11 solution is shorter, let alone the one-liners of Windows and macOS.
I don't think this is a state in which Wayland will be pretty successful in the long-term. It's okay to have that much control on the lowest level, but there need to be proper high-level abstractions in place.
Long story short, here's the full code for all platforms:
#include <QGuiApplication>
#if defined(Q_OS_WIN)
#include <windows.h>
#elif defined(Q_OS_MACOS)
#include <CoreGraphics/CGEventSource.h>
#elif defined(Q_OS_UNIX)
#include <qpa/qplatformnativeinterface.h>
// namespace required to avoid name clashes with declarations in XKBlib.h
namespace X11
{
#include <X11/XKBlib.h>
}
#include <KF5/KWayland/Client/registry.h>
#include <KF5/KWayland/Client/seat.h>
#include <KF5/KWayland/Client/keyboard.h>
#endif
void MyCls::checkCapslockState()
{
const QString platform = QGuiApplication::platformName();
#if defined(Q_OS_WIN)
newCapslockState = (GetKeyState(VK_CAPITAL) == 1);
#elif defined(Q_OS_MACOS)
newCapslockState = ((CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState) & kCGEventFlagMaskAlphaShift) != 0);
#elif defined(Q_OS_UNIX)
// get platform display
QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface();
auto* display = native->nativeResourceForWindow("display", nullptr);
if (!display) {
return;
}
if (platform == "xcb") {
unsigned state = 0;
if (X11::XkbGetIndicatorState(reinterpret_cast<X11::Display*>(display), XkbUseCoreKbd, &state) == Success) {
newCapslockState = ((state & 1u) != 0);
}
} else if (platform == "wayland") {
if (!m_wlRegistry) {
auto* wlDisplay = reinterpret_cast<struct wl_display*>(display);
m_wlRegistry.reset(new KWayland::Client::Registry());
m_wlRegistry->create(wlDisplay);
m_wlRegistry->setup();
// wait for a seat to be announced
connect(m_wlRegistry.data(), &KWayland::Client::Registry::seatAnnounced, [this](quint32 name, quint32 version) {
auto* wlSeat = new KWayland::Client::Seat(m_wlRegistry.data());
wlSeat->setup(m_wlRegistry->bindSeat(name, version));
// wait for a keyboard to become available in the seat
connect(wlSeat, &KWayland::Client::Seat::hasKeyboardChanged, [wlSeat, this](bool hasKeyboard) {
if (hasKeyboard) {
auto* keyboard = wlSeat->createKeyboard(wlSeat);
// listen for a modifier change
connect(keyboard, &KWayland::Client::Keyboard::modifiersChanged,
[this](quint32 depressed, quint32 latched, quint32 locked, quint32 group) {
Q_UNUSED(depressed)
Q_UNUSED(latched)
Q_UNUSED(group)
newCapslockState = (locked & 2u) != 0;
// emit signals etc. here to notify outer non-callback
// context of the new value of newCapslockState
});
}
});
});
}
}
// do something with the newCapslockState state for any
// platform other than Wayland
}
m_wlRegistry is defined as a QScopedPointer<KWayland::Client::Registry> member in the header file.
This solution basically works, but suffers from either a bug in KWin or a weirdness of the protocol (I don't know which). Pressing the Capslock key will trigger the inner lambda callback twice: first time with bit 2 in locked set and a second time on key release with it unset. There is no reliable way of filtering out this second activation based on the other parameters passed (I tried ignoring when depressed != 0, but it's not working as expected). Pressing any other key after that will trigger the callback a third time with the bit set again. So when you are actually typing, the code works, but when you are just pressing Capslock, the behaviour is weird and less reliable than the solutions for other platforms. Since the Capslock indicator in the Plasma tray has the same issue, I would assume this is a bug.
As a KDE-specific solution, there appears to be another interface one could listen to called org_kde_kwin_keystate (https://github.com/KDE/kwayland/blob/master/src/client/protocols/keystate.xml). However, when I was testing it in a KDE Neon VM, the compositor did not announce this protocol extension, so I was unable to use it.

Related

Listen close event of iexplorer in my application

I am writing a win32 application by C++, and I want it to do something when all iexplorer.exe were closed.
I know that SetWindowsHook() may be useful in my case.
But if I have no idea about the process or thread ID of IE, because every time open IE would get a different thread ID.
If I do not use timer to check the process list to get the ID of iexplorer, does there have another approach to listen close event of IE in my win32 application?
The object for IE is called InternetExplorer. TheShellWindows object is a collection of InternetExplorer objects. But here it gets complicated. Not all InternetExplorer objects are what you would call an IE window. Some of them are "Windows Explorer" windows. See About the Browser (Internet Explorer).
The following is a managed C++ console program that lists the existing windows and sets a count of the number of existing windows. Then it uses WindowRegistered and WindowRevoked events to monitor creation and closing of windows. Those event are not documented very well. The sample below uses the Document member of each InternetExplorer object to determine if the window has HTML. However see the comment in c# - Distinguishing IE windows from other windows when using SHDocVw; it is possible for a IE window to not have HTML in it.
Note that the following sample is using an AutoResetEvent to keep the program going since it is a console program.
The following is the header:
#pragma once
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <ShlObj.h>
#include <comdef.h>
#include <vcclr.h>
The following is the program:
#include "stdafx.h"
using namespace System;
using namespace System::Threading;
static int Browsers = 0;
static gcroot<AutoResetEvent^> Event;
bool IsBrowser(SHDocVw::InternetExplorer ^ ie)
{
MSHTML::IHTMLDocument2^ Document;
try { Document = (MSHTML::IHTMLDocument2^)ie->Document; }
catch (Exception^ ex)
{
return false;
}
return Document != nullptr;
}
static void WindowRegistered(int lCookie) {
++Browsers;
Console::WriteLine("WindowRegistered");
}
static void WindowRevoked(int lCookie) {
--Browsers;
Console::WriteLine("WindowRevoked");
if (Browsers <= 0)
Event->Set();
}
int main(array<System::String ^> ^args)
{
SHDocVw::ShellWindows^ swList = gcnew SHDocVw::ShellWindowsClass();
Console::WriteLine(L"{0} instances", swList->Count);
for each (SHDocVw::InternetExplorer ^ ie in swList) {
Console::WriteLine(ie->LocationURL);
if (IsBrowser(ie)) {
Console::WriteLine("HTML document");
++Browsers;
}
else
Console::WriteLine("Not HTML");
}
if (Browsers == 0)
{
Console::WriteLine("No browsers");
return 0;
}
Event = gcnew AutoResetEvent(false);
swList->WindowRegistered += gcnew SHDocVw::DShellWindowsEvents_WindowRegisteredEventHandler(WindowRegistered);
swList->WindowRevoked += gcnew SHDocVw::DShellWindowsEvents_WindowRevokedEventHandler(WindowRevoked);
Event->WaitOne();
Console::WriteLine("No more browsers");
return 0;
}
Now I just realized that there is a problem with the way this works. The WindowRegistered and WindowRevoked handlers are incrementing the Browsers count even if the window is not an IE window. I don't know how to determine what window that the cookie passed to WindowRegistered and WindowRevoked represents. A few years ago I spent a couple of days or more tryinig to figure that out. So what you should do is to somehow re-list all the windows after each WindowRegistered and WindowRevoked event.
You need to add references for "Microsoft Internet Controls" (SHDocVw.dll) and "Microsoft HTML Object Library" (mshtml.dll) to the project. They are COM objects that should be in your "C:\Windows\System32" directory.

get_accChildCount returns 0 when it shouldn't

I'm trying to enumerate tabs of IE from an extension and from a standalone application. For one of the MSAA nodes get_accChildCount returns 0 when called from extension, while it should return 1 according to inspect and a call from standalone application.
The problem was described previously at StackOverflow, yet it was solved via a hack that doesn't work for me. /clr and /MT are incompatible.
Also there was a topic on MSDN with the same issue. There's no single answer there.
If you run IE with administrator privileges, it works properly.
API Monitor shows several thousand calls in a minimal example, and it's unclear which of these are related. The minimal example is attached below.
What are the undocumented cases when get_accChildCount returns wrong child count?
What other method could I use to activate the tab by URL in most versions of IE?
#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
#include <atltypes.h>
#include <atlsafe.h>
#include <io.h>
#include <fcntl.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#include <boost/format.hpp>
#include <fstream>
using namespace std;
CComPtr<IAccessible> get_acc_by_hwnd(HWND hwnd) {
CComPtr<IAccessible> ret;
HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**) &ret);
if (FAILED(hr) || !ret) {
wcout << L"Accessible::Accessible invalid hwnd" << endl;
}
return ret;
}
std::vector<CComPtr<IAccessible>> get_acc_children(CComPtr<IAccessible> acc) {
std::vector<CComPtr<IAccessible>> ret;
long count;
if (FAILED(acc->get_accChildCount(&count))) return ret;
long count_obtained = 0;
if (!count) return ret;
std::vector<CComVariant> accessors(count);
if (FAILED(::AccessibleChildren(acc, 0, count, &*accessors.begin(), &count_obtained))) return ret;
accessors.resize(count_obtained);
for (auto vtChild : accessors) {
if (vtChild.vt != VT_DISPATCH) continue;
CComQIPtr<IAccessible> pChild = vtChild.pdispVal;
if (pChild) ret.push_back(pChild);
}
return ret;
}
bool is_client(CComPtr<IAccessible> acc) {
CComVariant var;
HRESULT hr = acc->get_accRole(CComVariant(CHILDID_SELF), &var);
return SUCCEEDED(hr) && var.vt == VT_I4 && var.lVal == 0xA;
}
std::wstring get_descr(CComPtr<IAccessible> acc) {
CComBSTR str;
HRESULT hr = acc->get_accDescription(CComVariant(CHILDID_SELF), &str);
return SUCCEEDED(hr) && str ? std::wstring(str) : L"";
}
int main() {
::CoInitialize(nullptr);
_setmode(_fileno(stdout), _O_U16TEXT);
// put HWND of the window that contains tab labels
// it's hardcoded to minimize quantity of API calls
HWND hwnd = reinterpret_cast<HWND>(0x002D0696);
CComPtr<IAccessible> iaccessible;
HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**) &iaccessible);
if (FAILED(hr) || !iaccessible) {
wcout << L"AccessibleBrowser::activate_tab " L"failed to get IAccessible for IE" << endl;
return EXIT_FAILURE;
}
wstring const sentinel = L"\r\n";
for (auto child : get_acc_children(iaccessible)) if (is_client(child)) {
for (auto child1 : get_acc_children(child)) { // fails here in extension
for (auto child2 : get_acc_children(child1)) {
std::wstring descr = get_descr(child2);
auto pos = descr.find(sentinel);
if (pos == string::npos) continue;
auto tab_url = descr.substr(pos + sentinel.size());
wcout << tab_url << endl;
}
}
}
}
I poked at your program for a while without having much to show for it. Perhaps I realized too late that it was not supposed to reproduce the problem :( I can only provide some possibly helpful hints to get you to look under the right kind of rock.
yet it was solved via a hack that doesn't work for me
These programmers made a simple mistake, they forgot to call CoInitialize/Ex(). A very common oversight. Using the /clr build option works around that mistake because it is now the CLR that calls it. You can easily repro this mishap, just comment out the CoInitialize() call. Unfortunately it works for a while without any loud errors being produced, but you do get 0 for certain accobjects. You'll notice your program no longer finds the tabs.
Not so sure I can cleanly explain this, certain COM-style object models don't actually use the COM infrastructure. DirectX is the best example, we can add UIAutomation to that list. I assume it will silently fail like this when the client app's component is COM-based. Unclear if it is, DirectUIHWnd is quite undocumented.
So stop looking for that as a workaround, you did not forget to call CoInitialize() and it will be taken care of by IE before it activates your extension.
If you run IE with administrator privileges, it works properly.
That's a better rock. Many programmers run VS elevated all the time, the roles might be reversed in the case of UIA. Do keep in mind that your extension runs in a sandbox inside IE. No real idea how elevating IE affects this. One thing you cannot do from code running in the low-integrity sandbox is poke at UI components owned by a code that runs in a higher integrity mode. Google "disable IE enhanced protected mode" and follow the guideline to see what effect that has on your addin.

Clipboard Shortcut/Hotkey binding with Qt outside of application

I've tried googling, and searching on this site about this but to no avail.
I am building a Clipboard related application for Windows using Qt, and one of the requirements for it to work right is to be able to register for keyboard events outside my Qt application, like ctrl + c, ctrl + v. (copy/paste). The only thing that I have found online is using an external plugin for Qt but the entire concept was not explained properly, so I hit a dead end.
Does anyone have an idea how I can do this? Again, I want to register shortcuts to my application that will occur outside the application itself.
Thanks in advance!
Binding clipboard shortcuts and binding shortcuts in general are as I have discovered, two different things. Related to Clipboard events, Qt provides access to a dataChanged() signal through its QClipboard class. Using that, you are able to know when the clipboard data has changed, and act accordingly, and should eliminate the need to perform a system-wide binding of the Copy/Paste shortcuts.
In order to register a global shortcut (in this case the need for ctrl + v), and this is platform specific like in my needs, one can use the RegisterHotKey function under Windows. The HWND requested as the first parameter can be obtained from the winId function that is provided by QWidget.
In order to accept the WM_HOTKEY event, one would have to implement the winEvent virtual protected function under Qt <= 5.0, and nativeEvent on >= 5.0.
This depends on the Desktopenvironment the application runs in and it is OS-specific. If you intend to run the application within KDE you can register global hotkeys easyly by deploying a .desktop File with your Application or by adding things to /usr/share/kde4/apps/khotkeys .
Within Windows, the easiest way would probably be adding a registry key to the registry in order to register a global Hotkey. See MSDN
I think you're just confused about how the clipboard works. You never need to register clipboard-related hotkeys outside of your application. Those are handled by other applications. What those applications do is interact with the system-wide clipboard. Your application needs to interact with the same clipboard and get notifications about new clipboard data being available etc.
You might get more helpful answers if you tell us what you mean by a "clipboard related" application. Is it used to sell wooden clipboards? Or to calibrate clipboard springs? Or to manage inventory of clipboards? Or does it run on a digital clipboard? Sigh.
This should get you up and running in Windows+Qt and HotKeys pretty quickly.
I haven't tried the Qt eXTension library with QxtGlobalShortcut, but it sounds like it may be a more elegant complete solution for more platforms. (like in #TimMeyer's comment to your question)
https://stackoverflow.com/a/3154652/808151
I wrote up this function to listen for a single system wide hotkey in windows.
#ifndef HOTKEYTHREAD_H
#define HOTKEYTHREAD_H
#include <QThread>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
class HotKeyThread : public QThread
{
Q_OBJECT
public:
HotKeyThread(QObject *parent);
~HotKeyThread();
signals:
void hot_key_event(int);
public slots:
void run();
void stop();
private:
volatile bool m_stopped;
DWORD m_thread_id;
};
#endif // HOTKEYTHREAD_H
.cpp file
#include "hotkeythread.h"
#include <QDebug>
#include <process.h>
#define WM_END_THREAD (WM_USER+2)
HotKeyThread::HotKeyThread(QObject *parent)
: QThread(parent)
{
this->m_thread_id = 0;
}
HotKeyThread::~HotKeyThread()
{
}
void HotKeyThread::stop()
{
if(this->m_thread_id != 0)
::PostThreadMessage(this->m_thread_id, WM_END_THREAD, 0, 0);
}
//
void HotKeyThread::run()
{
// store a thread id so we can exit later
m_thread_id = ::GetCurrentThreadId();
qDebug() << "ThreadIDs" << QString::number(m_thread_id, 16) << QString::number((int) this->currentThreadId(), 16);
// register an atom, and a hotkey
BOOL retVal;
int counter = 0;
int magic_num = 1128;
ATOM id = ::GlobalAddAtom(MAKEINTATOM(magic_num + counter++));
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
int modifier = 0x0;// modify this line
int key = VK_NUMPAD0;// modify this line
if(QSysInfo::windowsVersion() > QSysInfo::WV_VISTA)
{
retVal = ::RegisterHotKey(NULL, id, modifier | MOD_NOREPEAT, key);
}
else
{
// No repeat is only supported in 7 and later
retVal = ::RegisterHotKey(NULL, id, modifier, key);
}
if(retVal)
{
qDebug() << "Successfully added a HotKey!";
}
else
{
qDebug() << "Failed to add a hotkey!";
return;
}
// wait on hotkeys
MSG msg = {0};
while (0 < ::GetMessage(&msg, NULL, 0, 0))
{
if(msg.message == WM_HOTKEY)
{
bool control = LOWORD(msg.lParam) & MOD_CONTROL;
bool shift = LOWORD(msg.lParam) & MOD_SHIFT;
bool alt = LOWORD(msg.lParam) & MOD_ALT;
bool win = LOWORD(msg.lParam) & MOD_WIN;
qDebug() << "HotKey!" << (control ? "Ctrl +": "")
<< (alt ? "Alt +": "")
<< (shift ? "Shift +":"")
<< (win ? "Win +":"") << QString::number(HIWORD(msg.lParam),16);
// TODO Notify MainWindow of the event
emit hot_key_event(msg.lParam);
}
else if(msg.message == WM_END_THREAD)
{
// exit
break;
}
}
// Clean up Hotkey
::UnregisterHotKey(NULL, id);
::GlobalDeleteAtom(id);
}
Usage in your GUI
// Start HotKey Thread!
m_hot_key_thread = new HotKeyThread(this);
QObject::connect(m_hot_key_thread, SIGNAL(hot_key_event(int)),
this, SLOT(handle_hot_key_event(int)), Qt::QueuedConnection);
m_hot_key_thread->start();
and when your program is closing use
m_hot_key_thread->stop();
Hope that helps.

C++ Finding Index for Font temporarily added to System Font Table with AddFontResource() to use in Console

I am trying to temporarily install a font to use in the win32 console with
int AddFontResource(LPCTSTR lpszFilename);
and
BOOL WINAPI SetConsoleFont(HANDLE hOutput, DWORD fontIndex)
I got hold of this function from this site.
Although both functions seem to work fine I have no idea how to find the added font index to use with SetConsoleFont.
AddFontResource returns no index value or key to the temporary font.
Here is my relevant code:
#include "Level.h"
#include "ConsoleFont.h" //acquired from above mentioned site
#include <Windows.h>
//-------------------------------------------------------------------------------
void init();
void cleanup();
int main()
{
FileManager *pFileManager = new FileManager(); //unrelated
Level *lvl1 = new Level("filename",pFileManager); //unrelated
///TEMPORARY PLANNING
// using add font resource. how can i get this fonts index value?
int err = AddFontResource(L"Files/gamefont.fnt");
if (err == 0)
{
MessageBox(NULL,L"loading font failed",L"Error",0);
}
else
{
wchar_t message[100];
swprintf_s(message,100,L"AddFontResource returned: %d",err);
MessageBox(NULL,LPTSTR(message),L"error",0);
}
SendMessage(HWND_BROADCAST, WM_FONTCHANGE,0,0);
//acquiring handle to current active screen buffer
HANDLE tempHandle = GetStdHandle(STD_OUTPUT_HANDLE);
if (tempHandle == INVALID_HANDLE_VALUE)
{
MessageBox(NULL,L"Failed to aquire Screen Buffer handle",L"Error",0);
}
//I dont know what to set this to. this is the crux of the problem.
DWORD fontIndex = 1;
if (FALSE == SetConsoleFont(tempHandle,fontIndex))
{
MessageBox(NULL,L"loading console font failed",L"Error",0);
}
//draws a house when in correct font
std::cout<<"!!!!!!!!#\n"
<<"!!!!!!!!!\n"
<<"! !! !! !\n"
<<"!!!!!!!!!\n"
<<"! !! !! !\n"
<<"!!!!!!!!!\n"
<<"! !! !! !\n"
<<"!!!!!!!!!\n"
<<"! !! !! !#\n"
<<"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"<<std::endl;
///PLANNING OVERS
bool quit = false;
while(!quit)
{
//still to be implemented
}
err = RemoveFontResource(L"Files/gamefont.fnt");
if (err==0)
{
MessageBox(NULL,L"removing font failed",L"Error",0);
}
return 0;
}
I don't know how to go about finding my new font's index value or even if this is possible using my current method.
If someone knows or has a better method please help me out.
any help or hints are appreciated. It must possible to use a custom font in the win32 Console without fiddling with the registry. I'm sure of it :)
Unfortunately you entered the dark world on Win APIs. There is no documentation (or atleast I could never find it) for a console font table lookup. You can try the method "GetNumberOfConsoleFonts()" to see what is returned. I think the font at index 10 is Lucida Console. You'll have to play around a little. Also, this may not work for the OS version you have. Worked for me on XP. Never had to try on anything else. And honestly, never got it fully working on XP too.
For the registry,
Fonts registries are here:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts
Console registries are here:
HKEY_CURRENT_USER\Console
If you end up modifying the registry, the changes may not be reflected immediately. You need to either restart the console or send a special WM_* message (sorry don't remember the name).
Will be great if you can find a solution/workaround :)
int err = AddFontResource(L"Files/gamefont.fnt");
if (err == 0)
{
MessageBox(NULL,L"loading font failed",L"Error",0);
}
else
{
wchar_t message[100];
swprintf_s(message,100,L"AddFontResource returned: %d",err);
MessageBox(NULL,LPTSTR(message),L"error",0);
}
this is wrong AddFontResource returns the number of fonts loaded, so the code in the ELSE doesn't make sense.

How to get a num lock state using C/C++?

I have read Gdk forum link which says that getting num lock state api is implemented since version 3.0. But I am using version 2.4 and I cannot update to version 3.0 as I need to support lower Linux version. Here is the discussion link:
http://mail.gnome.org/archives/commits-list/2010-July/msg00259.html
SO, is there any other way to get the num lock state using internal Linux command?
Regards,
iSight
Sample code to get the NumLock state. Let foo.c be:
#include <stdio.h>
#include <X11/Xlib.h>
int main(void) {
Display *dpy = XOpenDisplay(":0");
XKeyboardState x;
XGetKeyboardControl(dpy, &x);
XCloseDisplay(dpy);
printf("led_mask=%lx\n", x.led_mask);
printf("NumLock is %s\n", (x.led_mask & 2) ? "On" : "Off");
return 0;
}
Then this gives, tested with CentOS 5 on a Dell laptop:
gcc foo.c -o foo -lX11
foo
led_mask=2
NumLock is On
Or you could do something with popen("xset q | grep LED");.
The second bit of the mask is fairly common for NumLock, but I don't believe it is guaranteed.
Original answer: A good starting point is xev, available for about 20 years:
xev
And you can decode key events via:
foobar (XKeyEvent *bar) {
char dummy[20];
KeySym key;
KeySym keyKeypad;
XLookupString(bar, dummy, sizeof dummy, &key, 0);
keyKeypad = XKeycodeToKeysym(..., bar->keycode, NUMLOCK_Mask);
if (IsKeypadKey(keyKeypad))
...;
// ...
}
If you don't care about the Numlock state "while nothing is happening", and only when e.g. a keypress happens, the lowest overhead way is this.
For some XKeyEvent *xke
bool numlock = ((xke->state & Mod2Mask) == Mod2Mask);
For GDK, you might need something like Gdk.FilterFunc to get the xevent. Check xevent->type.
#include <Xlib.h>
XEvent = (XEvent *) &xevent // from Gdk.FilterFunc
int type = event ->type;
switch(type) {
case KeyPress:
case KeyRelease:
do_something_with((XKeyEvent *) event);
break;
}
I did some sniffing around, and I found a possible implementation with ioctl.h that polls the keyboard state and tests that against a couple of flags.
Take a look at this form post's implementation, and replace K_CAPSLOCK with K_NUMLOCK*. It's pretty fugly, but it can easily be wrapped in a function and tucked away.
*The reason for the replacement on the post was because of an old bug where caps lock and num lock were accidentally reversed. It should be fixed now.
I have checked the hard ware key code. Whenever num lock is on and pressed the number key at num pad i compare the hard ware key code which is universally constant to all manufacturer. Hence, I don't need to use ioctl.h header.
You can use this linux command to do it
{
if (num_lock == 0) system("setleds -F +num");
else if num_lock == 1) ; //do nothing
}