I'm trying to add an event trap to enable/disable event from my magic trackpad. I thought this would be straight forward, i.e. register an event trap and when required, discard the event by returning NULL. The idea is to use the pad for some specific, time consuming data entry, the applications to enter the data into are third party ones so I can't just add code to do want I want there. So i figured I'd monitor the system events and then send the desired input via a bunch of CGEventCreateKeyboardEvents.
The problem is returning null does not seem to discard the events, a bit more investigation suggests that this is not restricted to those coming from the trackpad but also my default usb mouse.
My code is below. With what is below i'd expect not to be able to move the mouse, if I change (A) to use kCGEventScrollWheel or kCGEventLeftMouseDragged then event is consumed, i.e. scrolling or left btn drag don't occur. Does this mean that not all events can be discarded? Hopefully I'm just missing something obvious here
#define case_print(a) case a: printf("%s - %d\n",#a,a); break;
CGEventRef eventOccurred(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) {
int subType = CGEventGetIntegerValueField(event, kCGMouseEventSubtype);
if (type == NSEventTypeGesture || subType == NX_SUBTYPE_MOUSE_TOUCH) {
printf("touchpad\n");
switch(type) {
case_print(kCGEventNull)
case_print(kCGEventLeftMouseDown)
case_print(kCGEventLeftMouseUp)
case_print(kCGEventRightMouseDown)
case_print(kCGEventRightMouseUp)
case_print(kCGEventMouseMoved)
case_print(kCGEventLeftMouseDragged)
case_print(kCGEventRightMouseDragged)
case_print(kCGEventScrollWheel)
case_print(kCGEventOtherMouseDown)
case_print(kCGEventOtherMouseUp)
case_print(kCGEventOtherMouseDragged)
case_print(kCGEventTapDisabledByTimeout)
case_print(kCGEventTapDisabledByUserInput)
case_print(NSEventTypeGesture)
case_print(NSEventTypeMagnify)
case_print(NSEventTypeSwipe)
case_print(NSEventTypeRotate)
case_print(NSEventTypeBeginGesture)
case_print(NSEventTypeEndGesture)
default:
printf("default: %d\n",type);
break;
}
event = NULL;
} else {
if (type == kCGEventMouseMoved) { // (A)
printf("discarding mouse event");
event = NULL;
}
}
return event;
}
CFMachPortRef createEventTap() {
CGEventMask eventMask = NSAnyEventMask;
if (!AXAPIEnabled() && !AXIsProcessTrusted()) {
printf("axapi not enabled");
}
return CGEventTapCreate(kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
eventMask,
eventOccurred,
NULL);
}
int main (int argc, const char * argv[]) {
CFMachPortRef tap = createEventTap();
if (tap) {
CFRunLoopSourceRef rl = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), rl, kCFRunLoopCommonModes);
CGEventTapEnable(tap, true);
CFRunLoopRun();
printf("Tap created.\n");
sleep(-1);
} else {
printf("failed!\n");
}
return 0;
}
Note, "axapi not enabled" is not output although i don't think the accessibility option affects anything but the keyboard events.
BTW, I've seen a few similar posts on how to get the events from the touch pad, just nothing applicable to discarding them (other than returning null should work).
I think atm it is not possible to simply discard these events. From the CGEventTypes.h header file:
"The event passed to the callback is
retained by the calling code, and is
released after the callback returns
and the data is passed back to the
event system. If a different event is
returned by the callback function,
then that event will be released by
the calling code along with the
original event, after the event data
has been passed back to the event
system."
I've played around with this a little and it seems after the callback returns the window server actively checks again for what you've done to the event. It only allows deletion of key events and mouse up/down events, but just as it ignores deletion of mouse moved events (well, the mouse rendering is processed elsewhere anyways, I guess) it seems to ignore deletion (i.e. your callback returning NULL) for mouse gestures.
Figures, considering tapping for gestures is not specifically explained in the documentation (no type defined at this level).
I tried returning a different event (a key press) and this one is then handled additionally to the original gesture. Releasing the event in your callback doesn't do it, either (of course), just results in an exception.
The only thing I didn't try is directly manipulating the passed CGEvent's internal data to at least make the gesture do nothing (deleting all movement and such), but that's kind of hard because there are no specific methods defined to do that. I'm pretty sure the needed information is somewhere in the various fields accessible via the CGEventSet* methods, though.
I looked down as far as IOLLEvent.h to figure out their data structure, but this was way too ugly for my taste to dig into much further. Let's hope Lion offers a little more regarding the gesture type of events on the CF level.
I can corroborate that on 10.6 neither of the following methods succeed for this purpose.
1-returning NULL
2-returning a new mouse event with the previous cursor position
3-returning the passed event with it's kCGMouseEventDelta's changed to 0
I can not speak of 10.7 but let's just say i would not get my hopes up.
There is one thing however that you could do instead of dropping the movement move it back to the previous location with CGWarpMouseCursorPosition, in effect the cursor will stop moving with only a slight change on the first one.
CGAssociateMouseAndMouseCursorPosition(FALSE);
Will prevent the mouse from functioning while your app is active.
Still exploring if I can extend that to global apps...
http://lists.apple.com/archives/quartz-dev/2007/May/msg00112.html
If your tap is passive, returning NULL will leave the event stream unaffected. From the CGEventTapCallBack reference documentation:
"If the event tap is an passive
listener, your callback function may
return the event that is passed in, or
NULL. In either case, the event stream
is not affected."
However, it looks like your tap is active. Thus your returning NULL should delete the event. Have you considered modifying the event to nullify the action?
Also note that the call CGEventTapCreate requires root user permissions to intercept all events. Is your process running as root?
Related
After testing out a few other engines I've settled into Godot for my game development learning process and have really appreciated the conciseness of GDScript, the node/inheritance structure, and the way observer events are covered by signals. I've been building knowledge through various tutorials and by reading through the documentation.
Somehow I'm struggling to solve the very fundamental task of detecting a mouseclick on a sprite. (Well, on a sprite's parent node, either a Node2D or an Area2D.)
My process has been this:
Create an Area2D node (called logo) with a Sprite child and a CollisionShape2D child
Assign a texture to the Sprite node, and change the x and y extent values of the CollisionShape2D node to match the size of the Sprite's texture
Connect the _on_logo_input_event(viewport, event, shape_idx) signal to the Area2D node's script (called logo.gd)
Use the following code:
func _on_logo_input_event(viewport, event, shape_idx):
if (event is InputEventMouseButton && event.pressed):
print("Logo clicked")
When I run the game I get nothing in the output after clicking, and see these errors:
The argument 'viewport' is never used in the function '_on_logo_input_event'. If this is intended, prefix it with an underscore: '_viewport'
The argument 'shape_idx' is never used in the function '_on_logo_input_event'. If this is intended, prefix it with an underscore: '_shape_idx'
I don't know how to address the parameters in this signal's function - My Area2D node is set to Pickable, and the logo Area2D node is a direct child to the game_window Node2D in the main scene. I can't figure out what is going wrong here, whether it's some project setting I need to change or an inspector attribute I need to set. Is there a better way to feed an input signal for a mouse click into a script?
I don't want to clutter stackoverflow with such a simple question but I've tried to do my due diligence and haven't been able to find this error message on any forums. I'd appreciate any help, thanks.
If the CollisionLayer of your Area2D is not empty, and input_pickable is on, then it is capable to get input. Either by connecting the input_event signal or by overriding _input_event.
If that is not working, the likely cause is that there is some Control/UI element that is stopping mouse events. They have a property called mouse_filter, which is set to Stop by default. You will need to find which Control is intercepting the input, and set its mouse_filter to Ignore.
By the way, these:
The argument 'viewport' is never used in the function '_on_logo_input_event'. If this is intended, prefix it with an underscore: '_viewport'
The argument 'shape_idx' is never used in the function '_on_logo_input_event'. If this is intended, prefix it with an underscore: '_shape_idx'
These are warnings. They are not the source of the problem. They tell what they say on the tin: you have some parameter that you are not using, and you can prefix its name with an underscore as a way to suppress the warning.
I would also recommend checking out this video:
https://www.youtube.com/watch?v=iSpWZzL2i1o
The main modification that he makes from what you have done is make a separate click event in Project > Project Settings > Input Map that maps to a left click. He can then reference that in the _on_Area2D_input_event.
extends Node2D
var selected = false
func _ready():
pass
func _on_Area2D_input_event(viewport, event, shape_idx):
if Input.is_action_just_pressed("click"):
selected = true
func _physics_process(delta):
if selected:
global_position = lerp(global_position, get_global_mouse_position(), 25 * delta)
func _input(event):
if event is InputEventMouseButton:
if event.button_index == BUTTON_LEFT and not event.pressed:
selected = false
I am looking for an interruptable, delayed event, which I hope could be already part of Spring4D, or easily implemented there.
What would be best way anyway, to achieve this ?
Probably the Event wouldn't be the right place.
Basically looking for something like
Event.InvokeDelayed(1000, nil); and
Event.InvokeCancel;
procedure TTestMulticastEvent.Test;
var
e: Event<TNotifyEvent>;
begin
e.Add(HandlerA);
e.Invoke(nil); // Yes, this is the normal behaviour
// But would this make sense in Events,
// or better use other concept, like TTask.IdleWorker, e.g. ?
e.InvokeDelayed(1000, nil); // waits 1sec
Sleep(100); // Interrupts after 100ms
e.InvokeCancel; // Prevent future Event, started earlier
end;
Maybe I oversee something already available to solve this task in S4D.
Rollo
No, multicast events as they are in the library are synchronous. For them to work properly asynchronously in all cases it would require way more than just delaying their invokation but capturing any data you pass through them, make sure that data does not get invalid and so on.
If you need a mechanism like that you should use one of the existing libraries out there to do asynchronous things.
For example with the PPL (naive code to just show the concept):
var
e: Event<TNotifyEvent>;
t: ITask;
begin
e.Add(HandlerA);
t := TTask.Run(
procedure
begin
Sleep(1000);
if t.Status <> TTaskStatus.Canceled then
TThread.Synchronize(nil,
procedure
begin
e.Invoke(nil);
end);
end);
Sleep(100);
t.Cancel;
end;
You can probably wrap this into your own routine depending on your needs and the actual implementation. It will not be possible however to make a InvokeDelayed method with an additional parameter to what the event type has as signature because Event<T>.Invoke is not a method but a property returning T which you can invoke (which makes it look like a method call itself).
Anything beyond that requires as I said preserving the parameters passed to Invoke to pass them later (something that anonymous methods to for you to a certain degree) which is not trivial (at least not when we are talking about the generic Event<T>)
I'm learning VueJS. I'm figuring out their currency validation example code.
Vue.component('currency-input', {
template: `
<span>
$
<input
ref="input"
v-bind:value="value"
v-on:input="updateValue($event.target.value)">
</span>
`,
props: ['value'],
methods: {
// Instead of updating the value directly, this
// method is used to format and place constraints
// on the input's value
updateValue: function (value) {
var formattedValue = value
// Remove whitespace on either side
.trim()
// Shorten to 2 decimal places
.slice(
0,
value.indexOf('.') === -1
? value.length
: value.indexOf('.') + 3
)
// If the value was not already normalized,
// manually override it to conform
if (formattedValue !== value) {
this.$refs.input.value = formattedValue
}
// Emit the number value through the input event
this.$emit('input', Number(formattedValue))
}
}
})
The $emit call at the bottom of the updateValue function, triggers an input event.
When I comment it out, the real time currency validation no longer works. So I realize it has a purpose.
But why trigger an input event inside an input event?
You'd think the input event would fire again, causing the updateValue handler to fire again, causing a stack overflow due to recursive calls.
I understand VueJS's much simpler $emit example code. It's just like Jquery's trigger function.
vm.$on('test', function (msg) {
console.log(msg)
})
vm.$emit('test', 'hi')
// -> "hi"
But in the currency validation example, I do not understand why $emit is used the way it's used, and why it works the way it works.
Can somebody explain?
The Emit call here is to allow you to hook into the event in parent contexts. The Input event is also used by the v-model directive to handle two way binding with components.
v-model='model' is essentially v-bind:value='model' v-on:input='model = $event.target.value' with some added bits to make it play nice. When you remove the this.$emit('input', Number(formattedValue)) You're removing the mechanism that updates the value outside the component.
EDIT: #Jay careful what you wish for sometimes
All elements in HTML have a series of native handlers for the common events; resize, load, unload, etc. These handle what to do when the page changes it's rendering and can be disabled or added onto, since the introduction of JavaScript browsers have used an event pump system that allows multiple functions to be attached to any event which run in sequence when the event is raised. An example being how you can have 3 functions run on resize to handle edge cases such as minimum/maximum size, screen orientation etc.
Form elements generally implement their own base event functions: keydown, keyup, mousedown, mouseup. These base functions invoke events to make our lives easier as developers, these being: input, blur, focus. Some have specialized events as in select elements implementing change, form tags implementing submit.
Input tags on focus capture keyboard input and display the text input cursor to indicate that it's ready to receive input. It adds in handlers for the tab keycode which finds the next available input and shifts focus to that element. The event pump style function system is great here as it allows you to bind to focus and do things like change the background color or border when the input is focused without having to implement the code for capturing input or displaying the cursor yourself.
Input tags also raise the input event when you type in them indicating that the input has changed, telling the browser to change the value and update the display so that the functionality expected by the user is consistent.
In the currency-input example we are adding the updateValue function to work with the native function and process the input value of the event, in the updateValue function we modify the string representation of the value and need someplace to put it. You could simply add a data property to hold the value and bind the input's value property to the data property allowing the currency-input to internally handle the display of the result but that would lock the value behind a private accessor and you would be unable to modify or retrieve the value of the resulting currency formatted value.
Using this.$emit('input', Number(formattedValue)) the updateValue function is acting similar to the native input tag by raising an event that can be captured by the parent context and worked with. You can store it in a value, use it as the basis for a function, or even ignore it completely though that may not help much. This allows you to keep track of the value of the input and modify it as needed or send it to the server, display it, etc.
It also ties into a few directives most pertinently v-model which is syntactic sugar to allow for a value property binding and an input event binding to a data property inside the current context. By providing a value prop and emitting an input event a custom element can act similar to a native form element in the systems of a Vue application. An extremely attractive feature when you want to package and distribute or reuse components.
It's a lot nicer to go:
...
<currency-input v-model='dollarValue'></currency-input>
<input v-model='dollarValue'>
...
Than to have to add in value and input bindings everywhere ergo:
...
<currency-input v-bind:value='dollarValue' v-on:input='updateDollarValue($event.target.value)'></currency-input>
<input v-bind:value='dollarValue' v-on:input='updateDollarValue($event.target.value)'>
...
Now that my weird rambling is done, I hope this helped with understanding some of the patterns and reasoning behind the currency-input example.
Following the guidelines here I'm able to set the "consumer_cancel_notify" property for my client connection, but when the Queue is deleted the client still isn't noticing. I'm guessing that I probably have to override some method or set a callback somewhere, but after digging through the source code I'm lost as to where I'd do this. Does anybody offhand know where I'd listen for this notification?
Ok here's how I got it to work:
When creating the Queue (i.e. "declaring" the Queue), add a callback for the "AMQP_CANCEL" messages.
Inside AMQPQueue::sendConsumeCommand(), inside the while(1) loop where the code checks for the different *frame.payload.method.id*s, add a check for the AMQP_BASIC_CANCEL_METHOD, e.g.
if (frame.payload.method.id == AMQP_BASIC_CANCEL_METHOD){
cout << "AMQP_BASIC_CANCEL_METHOD received" << endl;
if ( events.find(AMQP_CANCEL) != events.end() ) {
(*events[AMQP_CANCEL])(pmessage);
}
continue;
}
That's it.
For my purposes I wanted to redeclare the Queue if it got deleted so that I could keep consuming messages, so inside my callback I just redeclared the queue, set up the bindings, added events, set the consumer tag, and consumed.
I was looking to intercept Command key combinations and thought that IMKit would be a good choice. By extending IMKInputController I can intercept most keys but it seems to ignore modified ones.
I've tried overriding
-(BOOL)inputText:(NSString*)string client:(id)sender;
and (alternatively)
-(BOOL)inputText:(NSString*)string
key:(NSInteger)keyCode
modifiers:(NSUInteger)flags
client:(id)sender;
but no luck; the methods just plain aren't called when the modifiers are applied. To be more specific the command and alt key don't get caught by the methods above. Simple modifiers like shift and ctrl work (and the modifier flags variable is set in the second method). Fire up Apple's sample application NumberInput to see for yourself.
Any suggestions? Am I on totally the wrong track?
Short answer:
Use handleEvent:client: and listen to the NSFlagsChanged event.
IMKInputController implements the IMKServerInput Protocol, which provides three alternatives to handle an event.
Key binding - using inputText:client: and didCommandBySelector:client:
Text data only - using inputText:key:modifiers:client:
Handle all events - using handleEvent:client:
Seems like you've only tried the top two. You can achieve the goal with the third option.
Try the following:
override recognizedEvents: (from IMKStateSetting Protocol)
- (NSUInteger)recognizedEvents:(id)sender
{
return NSKeyDownMask | NSFlagsChangedMask;
}
and use handleEvent:client:
-(BOOL)handleEvent:(NSEvent*)event client:(id)sender
{
NSLog(#"handling event: %#", event);
return false;
}
You can see the printout on every keydown and keyup of the modifiers in Console, including command and alt.