How to prevent window from loosing focus when receiving a grabbed key from X11 - qt5

My window calls hide() when a QEvent::FocusOut is received. Simultaniously I want its visibility to be toggled if a hotkey is pressed. Now I have the following problem: Pressing the hotkey, registered with XGrabKex(...) seems to steel the focus of my app. Resulting in an unwanted behaviour. If my app is visible the hotkeyevent steels focus, which results in a QEvent::FocusOut, which hides my app, and after that the hotkey is received which toggles visibility (shows) my app. I.e. my app does not hide when pressing the hotkey.
Is there a way to tell the x window system to not steel the focus when a grabbed key is pressed? Or are there other possible solutions to this problem?

A couple of different methods.
Use XQueryKeymap to see which keys are pressed. For instance, when you get a FocusOut event, call XQueryKeymap and see if your hotkey is pressed. If it is not, hide the window; if it is, don't hide it and wait for the hotkey event.
Delay hiding on FocusOut by 100 or so milliseconds. Cancel hiding if you either get the hot key or get your focus back during this time interval.
Look also here for useful info.

Finally got it to work in a "proper" way:
bool MainWidget::nativeEvent(const QByteArray &eventType, void *message, long *)
{
#ifdef Q_OS_LINUX
if (eventType == "xcb_generic_event_t")
{
xcb_generic_event_t* event = static_cast<xcb_generic_event_t *>(message);
switch (event->response_type & 127)
{
case XCB_FOCUS_OUT: {
xcb_focus_out_event_t *fe = (xcb_focus_out_event_t *)event;
if ((fe->mode==XCB_NOTIFY_MODE_GRAB && fe->detail==XCB_NOTIFY_DETAIL_NONLINEAR)
|| (fe->mode==XCB_NOTIFY_MODE_NORMAL && fe->detail==XCB_NOTIFY_DETAIL_NONLINEAR ))
hide();
break;
}
}
}
#endif
return false;
}

Related

Gtk - Intercept Mouse Button Events in Top Level Window

Keyboard events in Gtk propagates bottom up, starting from the top level window and then up the Z order. However mouse button events are first delivered to the widget at the top of the Z order and it is propagated down only if it is not handled there.
I am trying to implementing a UI lock feature where mouse button / touch events are to be ignored unless the user is clicking on the unlock button.
Is there any way in which I can intercept button presses at the top level window itself? I tried connecting to the signal_button_press_event with the after argument set to false. Even then the events are delivered to the child widget first.
Earlier I was using gdk event filters (Gdk::Window::add_filter) to do this however that works only on X11 backend and does not work for Wayland.
Updated after comment for adding minimal code
CTestWindow is a top level window derived from Gtk::Window. The button press signal is intercepted as below in the class constructor.
signal_button_press_event().connect(sigc::mem_fun(*this,
&CTestWindow::HandleButtonPress),
false);
This is how the handler is implemented
bool CTestWindow::HandleButtonPress(GdkEventButton *pEvent)
{
bool bRet = false;
/*
* If the button press coordinate is outside the allocated area of the unlock button,
* discard the event (by returning as processed).
*/
if (!CCommon::IsWithinBounds(m_oUnlockButton.get_allocation(),
(int)pEvent->x,
(int)pEvent->y))
{
bRet = true;
}
return bRet;
}

Handle VueJS event only when key is pressed

I use VueJS component provided by 3rd-party library, which has its own events. I can handle such events as usual with "v-on" directive and specifying interesting event name, like that:
<TheComponent v-on:itemchanged="handler"/>
But is it possible to run handler only when 'itemchanged' occurs with CTRL key pressed? The handler should not run when CTRL key is not pressed.
I tried to play with click.crtl and keydown.ctrl but have no success.
Is it possible to run handler only when 'itemchanged' occurs with CTRL key pressed?
Assuming itemchanged is triggered by a change event: no, not without a workaround. Unlike keyboard and mouse events, change contains no keyboard info. You'll need to track the status of ctrl keypresses independently because JavaScript has no way of testing whether a key is down outside of those events.
Plugin
One encapsulated way to accomplish it is with a plugin:
const pluginKeypresses = function(Vue) {
const keyPresses = {
Control: false
}
window.onkeyup = function(e) { keyPresses[e.key] = false; }
window.onkeydown = function(e) { keyPresses[e.key] = true; }
Vue.prototype.$keyPresses = Vue.observable(keyPresses);
}
Vue.use(pluginKeypresses);
Note: The e.key detection here is the current standard as of 11/2020, but is not yet fully supported by IE and some other browsers. If IE matters to you, change the key detection implementation to your liking.
Usage
From anywhere in the app you can test this.$keyPresses.Control (boolean)
Demo
Don't you receive an event object with this event ? By adding a parameter to handler you should be able to catch the event, and check inside which key is pressed. Then just add a if statement at the beginning of you handler function.

ObjectiveC Accessibility API: UnMaximize Window

I’m not sure if I am referring to this correctly, but when I use the word “UnMaximize”, I’m referring to:
When you click on the green button which is third on the top left of a
Chrome Window, it Maximizes the Window. When I use the word
“UnMaximize” above, I’m referring to the behavior that clicks that
button again so that it is no longer in full screen.
(By the way, what is the correct word for this in MacOS Terminology?)
I enjoy using the Easy Move+Resize App. While it can move Windows around, unfortunately, it has no effect on windows that are Maximized. Fortunately, the code is available on Github.
I’m curious if anyone can point me how to UnMaximize a Window using the Accessibility API
Does anyone what is the UnMaximize equivalent to kAXCloseButtonAttribute
I’m using MacOs 10.12 if that helps.
I’m grateful to #Willeke - Willeke for pointing me in the correct direction.
As mentioned in my question, I was looking at the code of the Easy Move+Resize App on GitHub. The problem with this code/app is that it does not work for Windows that are currently Maximized i.e. it tries to move these Windows, but it cannot, because they are fixed. (Note: This only has use and is relevant in a multi-monitor setup.) This app works correctly for Windows that are not Maximized.
Here, I am trying to add code that would UnMaximize a window in order to move it, and then Maximize it again after it has been moved. Obviously, the code below is in the context of this app, but I’m sure would be useful to users in other contexts.
I first added a wasMaximized property to EMRMoveResize.h
//EMRMoveResize.h
#property bool wasMaximized;
Next, I moved to EMRAppDelegate.m where the actual Event Callback code is. It should be noted that we are only concerned with moving i.e. only concerned with the Left Mouse Button. (This app uses the Right Mouse Button for resizing, which is not relavent when the Window has been maximized.) So, we are only concerned with kCGEventLeftMouseDown, kCGEventLeftMouseDragged and finally with kCGEventLeftMouseUp. In pseudo code, I have done something like:
If (LeftMouseDown) {
Find out if Window is Maximized
If (Window is Maximized) {
set the wasMaximized property
Click FullScreen Button to UnMaximize the Window in Order to Move it
}
The Window is now UnMaximized would now move as other windows in the LeftMouseDragged event, which I have not made any changes to. Finally,
If(LeftMouseUp) {
If(wasMaximized value was set) {
Click FullScreen Button again to Maximize the Window (Since it started out as Maximized)
Reset the wasMaximized property
}
}
Now for the snippets of code changes to EMRAppDelegate.m
if (type == kCGEventLeftMouseDown
|| type == kCGEventRightMouseDown) {
//..
//Skipped Unchanged Code
//..
//Find out if Window is Maximized
CFTypeRef TypeRef = nil;
if (AXUIElementCopyAttributeValue((AXUIElementRef)_clickedWindow, CFSTR("AXFullScreen"), &TypeRef)) {
if(Debug) NSLog(#"Could not get wasMaximized Value");
} else {
[moveResize setWasMaximized: CFBooleanGetValue(TypeRef)];
if(Debug) NSLog(CFBooleanGetValue(TypeRef) ? #"Updated Maximized to True" : #"Updated Maximized to False");
}
//Click FullScreen Button to UnMaximize the Window in Order to Move it
if([moveResize wasMaximized]) {
AXUIElementRef buttonRef = nil;
AXUIElementCopyAttributeValue(_clickedWindow, kAXFullScreenButtonAttribute, (CFTypeRef*)&buttonRef);
if(Debug) NSLog(#"buttonRef: %p", buttonRef);
AXUIElementPerformAction(buttonRef, kAXPressAction);
CFRelease(buttonRef);
}
//..
//Skipped Unchanged Code
//..
}
if (type == kCGEventLeftMouseUp
|| type == kCGEventRightMouseUp) {
//..
//Skipped Unchanged Code
//..
//Click FullScreen Button again to Maximize the Window (Since it started out as Maximized)
AXUIElementRef _clickedWindow = [moveResize window];
if([moveResize wasMaximized]) {
AXUIElementRef buttonRef = nil;
AXUIElementCopyAttributeValue(_clickedWindow, kAXFullScreenButtonAttribute, (CFTypeRef*)&buttonRef);
if(Debug) NSLog(#"buttonRef: %p", buttonRef);
AXUIElementPerformAction(buttonRef, kAXPressAction);
CFRelease(buttonRef);
[moveResize setWasMaximized: false];
}
//..
//Skipped Unchanged Code
//..
}
This worked for me. But I'm not an expert in Objective C or MacOS, so if you feel something can be improved, feel free to edit this post.

Cannot enable a Ribbon button programmatically

I developed a VSTO 4 add-in for Excel. It works perfect, however, I have a button placed in the custom tab of its Ribbon control that is initially disabled.
After clicked other ribbon button in my custom tab, I need to enable the initially disabled button.
I tried with:
btnCancelar.Visible = true;
In the Click event of a button, but button is not shown. The strange thing is that when debugging, it still does not appear, but if a MessageBox is shown, the button get visible at last.
I don't understand this behaviour. How can I enable or disable a ribbon button dynamically by code?
I'm not sure what your language is used in your project, but I guess you can tranform it to your own language used. I'll show the example here in C#:
First you need to implement a so called Callback function in the RibbonXML definition:
<button id="buttonSomething" label="Content" size="large" getVisible="EnableControl"/>
then the next step is to implement the Callback function:
public bool EnableControl(IRibbonControl control)
{
return true; // visible ... false = invisible
}
VSTO will trigger the getVisible Callback and depending on the return value enable or disable the visible state (don't forget to remove any Visible property from the RibbonXML, otherwise the Callback is not triggered)
In case of the Ribbon Designer you need to make sure your Click signature is correct, the easies way to do that is by double clicking the button on the ribbon designer. This will create the Click method for you, for instance:
I created a Ribbon with the Ribbon designer and added two buttons. Double clicked the first button to get an empty method like below, and added the code.
private void button1_Click(object sender, RibbonControlEventArgs e)
{
// Toggle button visibility and make sure the button is enabled
// Visible (obviously) makes it visible, while Enabled is grayed if
// false. You don't need this it is Enabled by default, so just for
// demo purposes
button2.Visible = !button2.Visible;
button2.Enabled = button2.Visible;
// Force Ribbon Invalidate ...
this.RibbonUI.Invalidate();
// Long running proces
}
This worked perfectly for me, so if it doesn't work for you please provide more details of your coding.
I have created a workaround to this.
It was simple. Just started the long running process in different thread. That way, cancel button is shown when it should and then hidden after the process ends.
I used this code to launch the process in the Ribbon.cs code:
btnCancelar.Visible = true;
Action action = () => {
Formatter.GenerateNewSheet(Formatter.TargetType.ImpresionEtiquetas, frm.CustomerID, workbook, btnCancelar);
};
System.Threading.Tasks.Task.Factory.StartNew(action);
And inside the process method I have this code:
public static bool GenerateNewSheet(TargetType type, string customerID, Excel.Workbook workbook, Microsoft.Office.Tools.Ribbon.RibbonButton btnCancelar)
{
try
{
_cancelled = false;
InfoLog.ClearLog();
switch (type)
{
case TargetType.ImpresionEtiquetas:
return GenerateTagPrinting(customerID, workbook);
}
return false;
}
finally
{
btnCancelar.Visible = false;
}
}
The interesting thing here I have discovered is that Excel is thread safe, so it was not necessary to add a synchronization mechanism neither when adding rows in the new sheet nor when setting Visible property to false again.
Regards
Jaime

Objective-C: Listen to keyboard shortcuts and act on them

I am developing an app for Mac OS X and I need to listen to keyboard shortcut inputs so I can act on them.
For example:
Up and down keys move up and down on a table view.
⌘ + ⌦ drops an item.
⌘ + ⇧ + N creates a new item.
It shouldn't be restricted to events on the focused control.
Any ideas?
Thanks in advance.
I think your best option* would be +[NSEvent addLocalMonitorForEventsMatchingMask:handler:]. This creates an object which will call a block handler whenever your application receives an event of the specified type. The handling takes place right before your NSApplication dispatches the event to a window, and you have the opportunity to modify the event or stop it from proceeding further.
You can thus catch key down events as they get passed to your app and do whatever you like with them before any controls get a chance to see them. I posted this originally in another question, but here's a snippet for doing things with arrow key presses:
NSEvent * (^monitorHandler)(NSEvent *);
monitorHandler = ^NSEvent * (NSEvent * theEvent){
switch ([theEvent keyCode]) {
case 123: // Left arrow
NSLog(#"Left behind.");
break;
case 124: // Right arrow
NSLog(#"Right as always!");
break;
case 125: // Down arrow
NSLog(#"Downward is Heavenward");
break;
case 126: // Up arrow
NSLog(#"Up, up, and away!");
break;
default:
break;
}
// Return the event, a new event, or, to stop
// the event from being dispatched, nil
return theEvent;
};
// Creates an object we do not own, but must keep track of so that
// it can be "removed" when we're done; therefore, put it in an ivar.
eventMon = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask
handler:monitorHandler];
See the Event-Handling Guide for some details about what you're supposed to do with that monitor object. Specifically, Apple apparently "discourages" removing it inside of dealloc, but doesn't give a reason.
*So long as you can require Snow Leopard, at least.
You may need to implements callbacks in your application. Take a look to CGEventTapCreate to start listening for hotkeys.
CGEventTapCreate(kCGSessionEventTap,
kCGTailAppendEventTap,
kCGEventTapOptionDefault,
kCGEventKeyDown
myEventTapCallback,
NULL);
myEventTapCallback should be conform to CGEventTapCallBack and gets called when a key is pressed. Then, inside myEventTapCallback you'll have enough information on keys pressed and you can implement your custom functionality.
Have a look at the Cocoa Event-Handling Guide. There are a few places you can intercept events before they get to the key view. You can intercept all events in the application by overriding -[NSApplication sendEvent:], or you can intercept events at a per-window level by overriding-[NSWindow sendEvent:].