I'm trying to make an application like Launchy/Enso/etc., which pops up when the user presses the Caps Lock key.
To do this, I have needed to install a low-level keyboard hook (WH_KEYBOARD_LL), from which I subsequently spawn a thread to display the dialog to present to the user.
The trouble is, when I somehow steal focus (e.g. by clicking on another window) and subsequently press Caps Lock with a short delay, my window doesn't get the keyboard input: the input goes to the background window, even though my window is "active" (from looking at the title bar).
Of course, this gets pretty annoying, since I then end up typing something like "visu" (for "Visual Studio") inside a text processor (or a chat box...) and pressing Enter, withotu realizing that it didn't do what I intended.
(Funny thing is, after a 3-second (or so) pause, my window's title bar suddenly becomes "inactive", even though it was never active in the first place!)
How can I bypass this "protection" mechanism to actually activate my window?
Okay, I finally figured out a hack. (Microsoft employers: please look away...)
I intercept Caps Lock with a low-level keyboard hook, then when I detect VK_CAPITAL, I call
keybd_event(
VK_OEM_8,
(BYTE)MapVirtualKey(pKBDLLHook->vkCode, MAPVK_VK_TO_VSC),
(wParam == WM_KEYUP || wParam == WM_SYSKEYUP) ? KEYEVENTF_KEYUP : 0,
0
);
from within the handler.
Essentially, I just change the request to VK_OEM_8.
However, Notice that the virtual-key code does not correspond to the scan code. This is intentional -- VK_OEM_8 doesn't have a scan code (as far as I could tell, anyway) so I didn't have much of an option.
Then I just program based on VK_OEM_8 instead -- which is easy to intercept/handle/etc.
Hope this helps out other people.
Related
I have an unrepeatable bug of unknown origin in my single threaded window manager that occurs fairly infrequently (once every 2-3 weeks). Something happens that causes me to lose keyboard input. Mouse events are still handled properly so I know the event loop is still running, but the key press event is no longer triggered. Actually, the key is no longer grabbed. When I press XCB_MOD_MASK_4+2 to switch to desktop 2, the 2 will show up in the text editor or terminal that currently has the input focus, instead of being grabbed by the window manager. I thought maybe it was related to xcb_allow_events, so via IPC I can execute these three tests (from within the window manager, cmd is received from an external process):
if (strcmp(cmd,"test0")==0)
xcb_allow_events(wm.conn, XCB_ALLOW_ASYNC_KEYBOARD, XCB_CURRENT_TIME);
else if (strcmp(cmd,"test1")==0)
xcb_allow_events(wm.conn, XCB_ALLOW_SYNC_KEYBOARD, XCB_CURRENT_TIME);
else if (strcmp(cmd,"test2")==0)
keyboard();
void keyboard()
{
int i,m,k;
xcb_void_cookie_t cookie;
spawn("/usr/bin/xmodmap -e 'keycode 108 = Super_L'");
spawn("/usr/bin/xmodmap -e 'remove mod1 = Super_L'");
for (i=0; i<LENGTH(key_bindings); i++)
{
m = key_bindings[i].mod;
k = keysc(key_bindings[i].keysym);
info("grabbing key: %s (%d), mod: %d",key_bindings[i].keysym,k,m);
cookie = xcb_grab_key_checked(wm.conn, 0, wm.root, m, k, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
if (xcb_request_check (wm.conn, cookie))
error("can't grab key");
}
}
None of these tests help. I know the keyboard function works properly because it works on window manager startup. Also I can see in the log file that the key grabs in the keyboard function are actually being executed (without error) when prompted via IPC. The current workaround is to send sigterm to the window manager process, and then restart the wm. At that point everything works fine again.
I'm looking for techniques that might be helpful in tracking down the source of this problem, or in correcting the problem once it occurs (another test). Unfortunately, since I have no clue of the source of this problem, or what triggers it, I cannot make a simple test case to demonstrate. BTW I check the log files when this happens, and I don't see any pattern leading up to the problem. Each function logs an entry on entrance and exit.
Update 2021-02-12: I thought a restart would be a good workaround until I found the root cause of this problem. My restart function contains only one line:
execvp(lwm_argv[0], lwm_argv);
where lwm_argv is the argv provided as an argument to main.
I was very surprised to see that this did not alleviate the problem. I have to completely kill the old process then launch an new one to alleviate the problem. So this problem is PID dependant??? Further, I'm fairly convinced that this problem is somehow related to the stdout/stderr output of other applications launched from within the window manager using execvp. I've stopped launching applications from within the window manager and the problem went away. Any ideas of how launching other applications (and their output) could be affecting the keygrabs within the window manager would be appreciated.
You could try using strace or perf trace on the X server to see what it is doing with the key events. It ought to read them from somewhere in /dev/input and send them as events to connected clients.
If it isn't sending you events, then you might need to dig into its internal state, perhaps by building a debug server and connecting to it with GDB, to see why it isn't sending those events.
But if it is sending events to your WM then they're getting lost somewhere in the library stack.
Is there a way to detect if the mouse has been moved ANYWHERE on the X Server or a keyboard event occured? I need to react on the user doing anything with the X11 input devices.
I only managed to detect events on my own window using GTK.
I am thankful for every information (it does not have to be full code, an entry point would be good enough!)
Yes, you can do this using the Xinput2 extension. A complete, but rather small, tool which does this for cursor events can be found here (unclutter-xfixes). As a disclaimer, I am the author of that tool.
Another good resource in tutorial form can be found here.
Using XInput2 has multiple benefits:
No need to constantly poll the position (resource efficient)
Does not interfere with / break applications like selecting mouse events on all windows would.
What you don't get easily using Xinput2 is the exact position (but you can query it when you need it), but my understanding is that you don't need it anyway.
Once you loaded the extension, which I won't show here, you can select all events like this:
XIEventMask masks[1];
unsigned char mask[(XI_LASTEVENT + 7)/8];
memset(mask, 0, sizeof(mask));
XISetMask(mask, XI_RawMotion);
XISetMask(mask, XI_RawButtonPress);
XISetMask(mask, XI_RawKeyPress);
masks[0].deviceid = XIAllMasterDevices;
masks[0].mask_len = sizeof(mask);
masks[0].mask = mask;
XISelectEvents(display, DefaultRootWindow(display), masks, 1);
XFlush(display);
In your event queue, you can now look for the corresponding events.
For modern X11 implementations, xinput --test-xi2 --root will display great detail about all X11 input events available on your root window. I use this in a shell script that needs to wait on any input event:
echo "DEBUG $(date) waiting on X event"
xinput --test-xi2 --root | head -n 15 >/dev/null
echo "DEBUG $(date) got X event"
I want to log if a customer tries to force close the application. I'm aware of having no chance to catch a process kill. But it should be possible through the main form closing event to get informed about the 'CloseReason.TaskManagerClosing' reason.
But any tests I did under Windows 8.1 I always got a CloseReason.UserClosing reason. But in this case (compared to a normals CloseReason.UserClosing) I've about 0.2s to run user code afterwards my program is killed!
Is this a new behavior in Windows 8.1?
Yes, I see this. And yes, this is a Windows change, previous versions of Task Manager sent the window the WM_CLOSE notification directly. I now see it issue the exact same command that's issued when you close the window with the Close button (WM_SYSCOMMAND, SC_CLOSE). Or press Alt+F4 or use the system menu. So Winforms can no longer tell the difference between the Task Manager and the user closing the window and you do get CloseReason.UserClosing.
What happens next is expected, if you don't respond to the close command quickly enough then Task Manager summarily assassinates your program with TerminateProcess().
Do keep in mind that trying to save data when the user aborts your program through Task Manager is a bad practice. Your user will normally use this if your program is malfunctioning, you can't really trust the data anymore and you risk writing garbage. This is now compounded by your saving code being aborted, high odds for a partially written file or dbase data that isn't usable anymore.
There is no simple workaround for this, the odds that Windows is going to be patched to restore old behavior are very close to zero. It is very important that you save your data in a transactional way so you don't destroy valuable data if the saving code is aborted. Use File.Replace() for file data, use a dbase transaction for dbase writes.
An imperfect way to detect this condition is by using the Form.Deactivate and Activate events. If you saw the Deactivate event and the FormClosing event fires then reasonable odds that another program is terminating yours.
But the normal way you deal with this is the common one, if the user ends the program without saving data then you display a dialog that asks whether to save data. Task Manager ensures that this doesn't go further than that.
Another solution for determining when the Task Manager is closing the program is by checking if the main form, or any of its controls, has focus. When you close via the Task Manager, the application is not focused, whereas if you close via the close button or Alt+F4, the application does have focus. I used a simple if check:
private void MyForm_Closing(object sender, FormClosingEventArgs e)
{
if (this.ContainsFocus)
{
// Put user close code here
}
}
I wrote a large complex C program around 20(!) years go. As far as I can recall it worked fine at the time in all respects - it was probably running on windows 95.
Now I need to use it again. Unfortunately the radio buttons in it do not appear to work properly any more (the ordinary push buttons are all behaving correctly). As I click on the radio buttons, I get some feedback that windows is acknowledging my click in as much as I see a dotted line appear around the button's text and the circle of the button goes grey for as long as my finger is on the button, but when I take my finger off I see that the selected button has not changed.
My suspicion is that I was perhaps getting away with some bad practice at the time which worked with windows 95 but no longer works on newer versions of windows, but I'm struggling work out what I did wrong. Any ideas?
EDIT: Its difficult to extract the relevant code because the message handling in this program was a tangled nightmare. Many buttons were created programatically at runtime and there were different message loops working when the program was in different modes of operation. The program was a customisable environment for running certain types of experiment. It even had its own built-in interpreted language! So I'm not expecting an answer like "you should have a comma instead of a semicolon at line 47", but perhaps something more like "I observed similar symptoms once in my program and it turned out to be ..... " .. or perhaps "the fact that the dotted rectangle is appearing means that process AAA has happened, but maybe step BBB has gone wrong".
EDIT: I've managed to extract some key code which my contain an error...
char *process_messages_one_at_a_time()
{
MSG msg;
int temp;
temp = PeekMessage(&msg,winh,0,0,PM_NOREMOVE);
if (temp)
{
GetMessage (&msg, NULL, 0, 0);
if (msg.message == WM_LBUTTONUP)
{
mouse_just_released_somewhere = TRUE;
}
TranslateMessage (&msg);
DispatchMessage (&msg);
}
if (button_command_waiting)
{
button_command_waiting = FALSE;
return (button_command_string);
}
else
{
return (NULL);
}
}
There are two simple things to check when using radio buttons. First is to make sure that each has the BS_AUTORADIOBUTTON property set. The second is to make sure that the first button in the tab order and the next control after the set of buttons (typically a group box) have the WS_GROUP property set, while the other buttons have it clear.
A few suggestions:
I'd try to use spy++ to monitor the messages in that dialog box, particularly to and from the radiobutton controls. I wonder if you'll see a BM_SETCHECK that your program is sending (ie, somewhere you're unchecking the button programatically).
Any chance your code ever checks the Windows version number? I've been burned a few times with an == where I should have used a >= to ensure version checking compatibility.
Do you sub-class any controls? I don't remember, but it seems to me there were a few ways sub-classing could go wrong (and the effects weren't immediately noticeable until newer versions of Windows rolled in).
Owner-drawing the control? It's really easy to for the owner-draw to not work with newer Windows GUI styles.
Working with old code like that, the memories come back to me in bits and pieces, rather than a flood, so it usually takes some time before it dawns on me what I was doing back then.
If you just want to get the program running to use it, might I suggest "compatibility mode".
http://www.howtogeek.com/howto/windows-vista/using-windows-vista-compatibility-mode/
However, if you have a larger, expected useful life of the software, you might want to consider rewriting it. Rewriting it is not anywhere near the complexity or work of the initial write because of a few factors:
Developing the requirements of a program is a substantial part of the required work in making a software package (the requirements are already done)
A lot of the code is already written and only parts may need to be slightly refactored in order to be updated
New library components may be more stable alternatives to parts of the existing codebase
You'll learn how to write current applications with current library facilities
You'll have an opportunity to comment or just generally refactor and cleanup the code (thus making it more maintainable for the anticipated, extended life)
The codebase will be more maintainable/compatible going forward for additional changes in both requirements and operating systems (both because it's updated and because you've had the opportunity to re-understand the entire codebase)
Hope that helps...
Almost every tutorial I find tells me to do this for my event loop:
XEvent event;
while (true)
{
XNextEvent(display, &event);
switch (event.type)
{
case Expose:
printf("Expose\n");
break;
default:
break;
}
}
However, clicking the X to close the program results in this message.
XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
after 10 requests (10 known processed) with 0 events remaining.
It is indeed strange to me that the examples suggest using an infinite loop. That doesn't sound natural, and my other X11 programs don't do that. So I searched around. I found out how to capture the window close event.
Atom wmDeleteMessage = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &wmDeleteMessage, 1);
XEvent event;
bool running = true;
while (running)
{
XNextEvent(display, &event);
switch (event.type)
{
case Expose:
printf("Expose\n");
break;
case ClientMessage:
if (event.xclient.data.l[0] == wmDeleteMessage)
running = false;
break;
default:
break;
}
}
That works. It exits without errors. ... But I refuse to believe this is the normal way to do things. I mean, is this the only way to properly exit an X11 app? It seems like a lot of work just to capture the close event. How do I make a 'proper' event loop? Why is the close event so deeply buried? What am I missing?
The problem lays in the communication between X Server and the Window Manager.
When you call XCreateWindow or XCreateSimpleWindow, the X Server creates your window (not showing it until you explicitly map it on the screen by calling XMapWindow), and then the Window Manager is responsible for attaching all the decorations and buttons and system menu around your window.
You can call XDestroyWindow on your own to remove the window, and this usually means it just disappears from the screen, but your program is still running and the connection to the X Server is still open, so you can send it some more requests.
The problem begins when the user clicks that little X button attached to your window by the Window Manager, because it is not created by the X Server and it is not his business to decide what to do then. Now it's all in hands of Window Manager.
If the Window Manager simply called XDestroyWindow on your window, it would cause a problem if your application wanted to capture the closing event to do something before the window gets destroyed. So the convention has been established between the X Server and the Window Managers to handle this process.
The default behavior of most Window Managers is to destroy the window and close the connection with the X server, because this is what most users of Window Managers would expect: that when they close the window, the program will end (and the connection to the X Server will close with the closed window). And then, when you try to call XCloseDisplay(display), it will cause the IO error you've mentioned, because the connection to the server is already closed and the display structure is invalid.
Here's an excerpt from the Xlib documentation which explains this:
Clients that choose not to include WM_DELETE_WINDOW in the WM_PROTOCOLS property may be disconnected from the server if the user asks for one of the client's top-level windows to be deleted.
Yeah, it would be great if they didn't hide it so deep in their docs, though :-P
But when you already find it, fortunately it also hints for the solution.
If you want a different behavior (that is, to capture the closing event from the Window Manager), you need to use the WM_DESTROY_WINDOW protocol.
Another excerpt from the docs:
Clients, usually those with multiple top-level windows, whose server connection must survive the deletion of some of their top-level windows, should include the atom WM_DELETE_WINDOW in the WM_PROTOCOLS property on each such window. They will receive a ClientMessage event as described above whose data[0] field is WM_DELETE_WINDOW.
I had the same error and I wanted to know exactly what causes it and why. It took me some time to figure it out and find the proper explanation in the doc, so I put my explanation here to save the time of others uninformed.
There are no such things as "exit button" or "application" or "close event" in X11. This is by design.
Window decorations, exit buttons and many the other things we depend upon are not built into X11. They are implemented on top of the core X11 instead. The name of the particular set of conventions responsible for wmDeleteMessage is ICCCM, look it up.
Xlib only deals with the core X11 protocol. No built-in close event there.
There are toolkits that make dealing with ICCCM and all other things that are not built into X11 easier (GTK, wxWindows, Qt, ...) You probably want to use one of those.