CGEventTapCreate detecting multiple keyboard inputs - c

I'm working on a macOS menubar app that locks any mouse events. What I'm trying to achieve is that after sending mouse events to the CGEventRef based callback, I cannot click anywhere (naturally) but the problem is I cannot quit the loop because of that.
Every time I need to close it, I'm switching to the Xcode app to stop the running app.
This is the main function and the events that I'm sending to the callback function;
void lockTrackpad(void) {
// For keyboard inputs, I add CGEventMaskBit(kCGEventKeyUp)| CGEventMaskBit(kCGEventKeyDown)| CGEventMaskBit(NX_SYSDEFINED)
CGEventMask mask = (
CGEventMaskBit(kCGEventMouseMoved)
| CGEventMaskBit(kCGEventLeftMouseUp)
| CGEventMaskBit(kCGEventLeftMouseDown)
| CGEventMaskBit(kCGEventRightMouseUp)
| CGEventMaskBit(kCGEventRightMouseDown)
| CGEventMaskBit(kCGEventScrollWheel)
);
eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, mask, CGEventCallback_r, nil);
...
}
And this is the callback function;
CGEventRef CGEventCallback_r(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
printf("Event Tap: %d\n", keycode);
if (keycode == 43) {
printf("quit the application \n");
exit(EXIT_SUCCESS);
}
return nil;
}
What I want is, for example when the loop is running, if press "control + U + L", I want to run some function to exit the loop. I believe in order to get the keyboard input, I should send the keyboard events as well but I couldn't figure out how to detect my keyboard shortcuts.
I added the keyboard bits to the mask.
The app is sandboxed.
I want to do something like this;
CGEventRef CGEventCallback_r(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
// When pressing "control + U + L", call a function
if (...) {
printf("pressed control + U + L \n");
doSomething();
}
return nil;
}

I figured out like this;
CGEventRef CGEventCallback_r(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
if (type == kCGEventKeyDown) {
CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
if (CGEventGetFlags(event) & kCGEventFlagMaskControl) {
// control key is pressed
controlKeyPressed = true;
}
if (keyCode == kVK_ANSI_U) {
// u key is pressed
uKeyPressed = true;
}
if (keyCode == kVK_ANSI_L) {
// l key is pressed
lKeyPressed = true;
}
if (controlKeyPressed && uKeyPressed && lKeyPressed) {
// doSomething();
}
return event;
} else if (type == kCGEventKeyUp) {
CGKeyCode keyCode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
// set the variable to false just like above.
}
if (keycode == 43) {
printf("quit the application \n");
exit(EXIT_SUCCESS);
}
return nil;
}
Also, don't forget to put your variables on a global scope.

Related

Arduino with adafruit RGBLCDShield Buttons weird behavior

I have the Arduino uno with rgb lcd shield. there is a very weird behavior with the buttons in one specific function.
The function is called yes/no. It displays a message on the screen (working) the user can select answer yes/no with the buttons up/down/left/right and approve the answer by pressing select button.
The function is as follows:
bool yesno(String message)
{
//Serial.println("asking yesno question " + message);
bool answer = false;
bool answerSelected = false;
setColor('r');
setText(message+'?', "no");
while (!answerSelected)
{
uint8_t buttons = lcd.readButtons();
if (buttons){
if (buttons &(BUTTON_UP || BUTTON_DOWN || BUTTON_RIGHT || BUTTON_LEFT)) {
if (answer) {
answer = false;
setColor('r');
setText(message+'?', "no");
Serial.println(answer);
}
else {
answer = true;
setColor('g');
setText(message+'?', "yes");
Serial.println(answer);
}
}
else if (buttons & BUTTON_SELECT) {
setColor('w');
answerSelected = true;
Serial.println("selected ");
Serial.println(answer);
return answer;
}
}
delay(50);
}
}
for some reason, when pressing left/right/up/down, nothing happens. When pressing select, instead it executes function up/down/left/right
using if (buttons &&(BUTTON_UP || BUTTON_DOWN || BUTTON_RIGHT || BUTTON_LEFT)) instead, the buttons left/right/up/down work as intended but the button select still acts as the other buttons
similar code for a menu works as intended:
void InitializeMenu()
{
// initialize
Serial.println("entering menu");
setColor('w');
while (!exitMenu)
{
if (menuItem != selectedItem)
{
Serial.println("switching menu to: "+menuItems[menuItem]);
setText("Menue", menuItems[menuItem]);
selectedItem = menuItem;
}
uint8_t buttons = lcd.readButtons();
if (buttons & BUTTON_UP) {
menuItem--;
Serial.println("menu up");
Serial.println(menuItem);
Serial.println(menuSize);
if (menuItem < 0)
{
menuItem = menuSize;
Serial.println("start of menu going to end");
}
}
if (buttons & BUTTON_DOWN) {
menuItem++;
Serial.println("menu down");
Serial.println(menuItem);
Serial.println(menuSize);
if (menuItem > menuSize) {
menuItem = 0;
Serial.println("end of menu going to start");
}
}
if (buttons & BUTTON_SELECT) {
Serial.println("enter");
if (menuItem == menuSize) exitMenu = true;
}
delay(50);
}
}
I want to keep the code as short and simple as possible due to very limited space on the arduino uno.
BUTTON_UP || BUTTON_DOWN || BUTTON_RIGHT || BUTTON_LEFT
doesn't do what you expect it to do. || is logical or, and the entire expression evaluates to true. To get a bitmask, change it to bitwise or (|):
BUTTON_UP | BUTTON_DOWN | BUTTON_RIGHT | BUTTON_LEFT

XI2. How to ignore events of modifier keys?

I'm trying to fix xneur keyboard switcher. Its author tried to add XI2 support, but broke input in the applications that use only XI2(google chrome for example).
And I'm stuck with modifiers filtering. There is the following code:
// Grab all keys...
if (has_x_input_extension) {
XIEventMask mask;
mask.deviceid = XIAllDevices;
mask.mask_len = XIMaskLen(XI_KeyPress)+
XIMaskLen(XI_KeyRelease);
mask.mask = (void *)calloc(mask.mask_len, sizeof(char));
XISetMask(mask.mask, XI_KeyPress);
XISetMask(mask.mask, XI_KeyRelease);
XISelectEvents(main_window->display, DefaultRootWindow(main_window->display), &mask, 1);
free(mask.mask);
}
else {
XGrabKey(main_window->display, AnyKey, AnyModifier, window, FALSE, GrabModeAsync, GrabModeAsync);
// ...without ModKeys.
grab_modifier_keys(window, FALSE);
}
grab_modifier_keys ungrabs key modifiers. And I don't know how to do the same with XI2(if branch).
I think it's possible to ignore those events in the event handling loop. Something like this:
int is_modifier (XEvent *event)
{
/* ??? */
}
while (1) {
XEvent event;
XNextEvent(display, &event);
if (is_modifier(event)) {
continue;
}
}
But I don't know how to implement is_modifier function.
I'd appreciate any help
Probably not a perfect solution, but here is an implementation of is_modifier I came up with:
Bool is_modifier(KeySym key_sym)
{
XModifierKeymap *modmap = XGetModifierMapping(main_window->display);
if (modmap == NULL) {
return False;
}
KeyCode key_code = XKeysymToKeycode(main_window->display, key_sym);
int size = modmap->max_keypermod * 8;
for (int i = 0; i < size; ++i) {
if (key_code == modmap->modifiermap[i]) {
return True;
}
}
XFreeModifiermap(modmap);
return False;
}

SDL/C EventHandler continuous key

I'm trying to develop an event handler in C using SDL.
I wanted to be able to detect the following things: Key pressed, key released and key held down.
The problem is that keyboard auto-repeat spams pressed keys after a certain delay and at a given interval. To mitigate this I tried using SDL_EnableKeyRepeat(0, 0); which according to documentation should disable it?
As that failed I tried mapping SDL_GetKeyboardState(NULL); and it sort of works. But I'm having the issue that if i hold down a key for X seconds, it will spam ghost events for the same amount of time.
An example output from pressing esc, holding it down for a bit, and then releasing it is:
Pressed
HELD
HELD
HELD
...
RELEASED {it breaks down after first proper release}
PRESSED
RELEASED
HELD
PRESSED
RELEASED
HELD
...
RELEASED // it always ends on released.
The result from above is:
Pressed: 18;
Released: 21;
Held: 39.
Should be:
Pressed 1;
Released 1;
Held: say 20.
Not sure if I'm messing up my logic somehow, or if it's something with the actual built in mechanics of keyboard events.
Here is the code:
/**
Program Interface
void ev_handle_start() // Starts the Event Handler.
void ev_handle_stop() // Pauses the Event Handler.
**/
#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL/SDL.h>
#include "ev_handler.h"
#define PRESSED 1
#define RELEASED 0
#define TMR_FREQ 33
#define MOUSE_RIGHT 1
#define MOUSE_LEFT 3
#define EV_LEFT_CLICK 0
#define EV_LEFT_RELEASE 1
#define EV_RIGHT_CLICK 2
#define EV_RIGHT_RELEASE 3
#define EV_MOUSE_MAX 4
#define MAX_SCANCODES SDL_SCANCODE_MODE
#define EV_DELAY 70
//********************************************************************************************//
// VARIABLES
//********************************************************************************************//
SDL_Event e;
Uint8* keyboardState;
SDL_TimerID ev_timer = NULL; // Timer to callback the search of the code
bool ev_handler_active = false; // Used to determine if the ev_handler() is enabled or not
bool buttonState[EV_MOUSE_MAX]; // Button Stats, either pressed or released
bool evMouseEvent[EV_MOUSE_MAX]; // Used to flag if a mouse events
int evKeyEventStatus[MAX_SCANCODES];
bool evKeyStatePrev[MAX_SCANCODES]; //
bool evKeyStateCur[MAX_SCANCODES]; //
//********************************************************************************************//
// 1 FRONTEND
//********************************************************************************************//
//********************************************************************************************//
// 1.1 MOUSE
// Can only handle left and right mouse buttons.
//********************************************************************************************//
bool evButtonPressed(int button)
{ bool flag = button == SDL_BUTTON_RIGHT || button == SDL_BUTTON_LEFT;
if (!flag) {
printf("ERROR: isButtonPressed(int) received parameter of invalid type.\n");
return false;
}
return buttonState[button];
}
bool evLeftClick()
{
return evMouseEvent[EV_LEFT_CLICK];
}
bool evLeftRelease()
{
return evMouseEvent[EV_LEFT_RELEASE];
}
bool evRightClick()
{
return evMouseEvent[EV_RIGHT_CLICK];
}
bool evRightRelease()
{
return evMouseEvent[EV_RIGHT_RELEASE];
}
//********************************************************************************************//
// 1.2 KEYBOARD
//********************************************************************************************//
bool evKeyHeld(SDL_Scancode sc)
{
return evKeyEventStatus[sc] == 1 || evKeyEventStatus[sc] == 3;
}
bool evKeyPressed(SDL_Scancode sc)
{
return evKeyEventStatus[sc] == 1;
}
bool evKeyReleased(SDL_Scancode sc)
{
return evKeyEventStatus[sc] == 2;
}
//********************************************************************************************//
// 1.3 MANAGE
//********************************************************************************************//
void evSetup()
{
for (int i = 0; i < MAX_SCANCODES; i++) {
evKeyStatePrev[i] = 0;
}
evClearEvents();
}
int j;
void evClearEvents()
{
evMouseEvent[EV_LEFT_CLICK] = false;
evMouseEvent[EV_LEFT_RELEASE] = false;
evMouseEvent[EV_RIGHT_CLICK] = false;
evMouseEvent[EV_RIGHT_RELEASE] = false;
}
//********************************************************************************************//
// 2 BACKEND
//********************************************************************************************//
void ev_handler()
{
while(SDL_PollEvent(&e))
{
switch (e.type)
{
/*Skip unnecessary events*/
case SDL_KEYDOWN:
break;
case SDL_KEYUP:
break;
case SDL_MOUSEMOTION:
break;
case SDL_MOUSEBUTTONDOWN:
buttonState[e.button.button] = PRESSED;
if (e.button.button == SDL_BUTTON_LEFT) {
evMouseEvent[EV_LEFT_CLICK] = true;
}
else if (e.button.button == SDL_BUTTON_RIGHT) {
evMouseEvent[EV_RIGHT_CLICK] = true;
}
break;
case SDL_MOUSEBUTTONUP:
buttonState[e.button.button] = RELEASED;
if (e.button.button == SDL_BUTTON_LEFT) {
evMouseEvent[EV_LEFT_RELEASE] = true;
}
else if (e.button.button == SDL_BUTTON_RIGHT) {
evMouseEvent[EV_RIGHT_RELEASE] = true;
}
break;
case SDL_QUIT:
exit(0);
printf("Quit\n");
break;
default:
break;
}
keyboardState = SDL_GetKeyboardState(NULL);
for (int i = 0; i < MAX_SCANCODES; i++)
{
evKeyStateCur[i] = keyboardState[i];
int sum = evKeyStateCur[i] + evKeyStatePrev[i]*2;
switch (sum) {
case 0:
break;
case 1:
evKeyEventStatus[i] = 1;
break;
case 2:
evKeyEventStatus[i] = 2;
break;
case 3:
evKeyEventStatus[i] = 3;
break;
default: evKeyEventStatus[i] = 0; break;
}
evKeyStatePrev[i] = evKeyStateCur[i];
}
}
}
The problem was that SDL 2.0.4 had a bug where holding down a key sent key releases in-between key presses instead of continuous key presses. Downloading the latest version fixed the issue.

Menu Selection WIndow Creation for LCDsheild and arduino Interface

I am trying to create Menu Selection process.if SELECT button pressed once should display the Display parameter and if select button pressed twice should goes to Set_parameter window(Where we set parameter). once it is display parameter using up and down arrow Choose the Display window .But problem i am facing here is It enter the Display parameter but comes out Menu window directly .
Circuit diagram for LCD is given here
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// Pin configuration of the buttons
#define btnRIGHT 0 // Okay
#define btnUP 1 // inc
#define btnDOWN 2 // dec
#define btnLEFT 3 // Select
#define btnSELECT 4 // Menu
#define btnNONE 5
#define beeper A1 // Alarm buzzer
#define shortBeep 100
#define longBeep 500
int button_counter=0;
void setup()
{
Serial.begin(9600);
}
void loop()
{
Select_Menu();
}
void Select_Menu()
{
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print("MENU");
int button=read_LCD_buttons();
if(button==btnSELECT)
{
button_counter=button_counter+1
}
if(button_counter==1)
{
Display_function();
}else if((button_counter==2))
{
Set_function();
}else
{
button_counter=0;
}
}
void Display_function()
{
int button = read_LCD_buttons();
if(button==btnUP)
{
button_counter=button_counter+1;
Serial.print("button_counter");
Serial.println(button_counter);
}else if(button==btnDOWN)
{
button_counter=button_counter-1;
Serial.println(button_counter);
}
if (button_counter>5)
{
button_counter=1;
}else
while(button_counter<5)
{
int button = read_LCD_buttons();
if(button != prev_button && button != btnNONE)
{
prev_button = button;
//timedBeep(shortBeep,2);
}
if ((((button ==btnUP )||(button_counter==1))||((button ==btnDOWN )))&&(prev_button==btnUP)
{
digitalClockDisplay();//timedBeep(200,1);
}else if((((button ==btnUP )||(button_counter==2))||((button ==btnDOWN )))&&(prev_button==btnUP))
{
Display_angle();//timedBeep(200,1);
}else if((((button ==btnUP )||(button_counter==3))||((button ==btnDOWN )))&&(prev_button==btnUP))
{
Display_coordinate();//timedBeep(200,1);
}else if((((button ==btnUP )||(button_counter==4))||((button ==btnDOWN )))&&(prev_button==btnUP))
{
button_loop();//timedBeep(500,4);
}else if((((button ==btnUP )||(button_counter==5))||((button ==btnDOWN )))&&(prev_button==btnUP))
{
Display_Wind();//timedBeep(200,1);
}
}
}
void Display_Wind()
{
lcd.setCursor(0,0);
lcd.print("WS kmph:");
lcd.print(Wind_Kmph);
lcd.setCursor(0,1);
lcd.print("WS m/s:");
lcd.print(Wind_Speed);
}
void button_loop()
{
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print("Welcome");
}
void Display_coordinate()
{
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print("Latitude:");
lcd.print(latitude);
lcd.setCursor(0,1);
lcd.print("Longitude:");
lcd.print(longitude);
}
void Display_angle()
{
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print("DESIRED: ");
lcd.print(tracker_des_angle,DEC);
lcd.setCursor(0,1);
lcd.print("ACTUAL: ");
lcd.print(tracker_actual_pos,DEC);
}
void digitalClockDisplay()
{
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print("Date:");
lcd.print(local_day);
lcd.print("/");
lcd.print(local_month);
lcd.print("/");
lcd.print(local_year);
lcd.setCursor(0,1);
lcd.print("Time:");
lcd.print(local_h);
lcd.print(":");
lcd.print(local_m);
lcd.print(":");
lcd.print(local_s);
lcd.print(" ");
}
int read_LCD_buttons()
{
adc_key_in = analogRead(0); // read the value from the sensor
// my buttons when read are centered at these valies: 0, 131, 307, 481, 722
// we add approx 50 to those values and check to see if we are close
// No button pressed should be 1023
if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
if (adc_key_in < 50) return btnRIGHT;
if (adc_key_in < 195) return btnUP;
if (adc_key_in < 380) return btnDOWN;
if (adc_key_in < 555) return btnLEFT;
if (adc_key_in < 790) return btnSELECT;
return btnNONE; // when all others fail, return this...
}
You have a flow control problem.
Your main loop runs SelectMenu() in there you enter Display_function() which contains no loop so it reaches the end and returns to SelectMenu() which then returns to loop() which again calls SelectMenu() redrawing your menu and overwriting the display with the menu again.
There are many ways to solve this, but as this is an arduino environment where resources are anything other than endless I would suggest you make each "page" its own loop breaking out of it when needing to move on. Do not switch pages inside the loops or you will build up quite the callstack.
Pseudo code:
loop():
#print and handle input
if (navigate)
Display_function()
Display_function():
while(true)
# print and do whatever
if (nav_angle)
Display_angle()
else if (nav_coord)
Display_coordinate()
#... etc.
if (nav_back)
return
Display_angle():
while(true)
#do stuff
if (nav_back)
return
This should give you the idea, it will work as long as you don't have too deep of a structure and run out of memory.

SDL - Why moving the mouse changes button state?

I'm having a problem with the Simple Directmedia Layer library. The following code draws a block on the screen when a mouse button is pressed:
SDL_Event event;
while(running){
while(SDL_PollEvent(&event)){
while(event.button.state == SDL_PRESSED){
SDL_PollEvent(&event);
//where to draw
boxRect.x = event.motion.x;
boxRect.y = event.motion.y;
//Draw to screen
SDL_FillRect(display,&boxRect,boxColor);
SDL_Flip(display);
}
// ...
}
// ...
}
It works fine until I move the mouse, why moving the mouse makes event.button.state untrue?
How can I work with both simultaneously (i.e. keep drawing while the button is pressed)?
The problem with your code is that you're calling SDL_PollEvent (documented here) twice. As said in the documentation:
If event is not NULL, the next event is removed from the queue and
stored in the SDL_Event structure pointed to by event.
Rearranging your code a bit, like getting rid of the second SDL_PollEvent, creating the proper flow for clicking, moving, releasing and extracting rendering from the input pumping should give you something like this:
SDL_Event Event;
while(running)
{
while(SDL_PollEvent(&Event))
{
switch(Event.type)
{
// Handle your drawing with a simple state machine:
case SDL_MOUSEBUTTONDOWN:
{
if(Event.button.button == SDL_BUTTON_LEFT)
{
if(stateMachine == Released)
{
// ... begin drawing
stateMachine = Dragging
}
}
break;
}
case SDL_MOUSEMOTION:
{
if(stateMachine == Dragging)
{
// ... update the extends of your rect
}
}
case SDL_MOUSEBUTTONUP:
{
if(Event.button.button == SDL_BUTTON_LEFT)
{
if(stateMachine != Released)
{
// ... finalize drawing... add the rect to a list? flush it?
stateMachine = Released;
}
}
}
case SDL_QUIT:
{
running = false;
break;
}
}
}
// Outside of your event pumping, update the graphics
SDL_FillRect(display,&boxRect,boxColor);
SDL_Flip(display);
}
event.button.state is a bit mask. It is declared as a char which can hold eight bits representing different states of the buttons.
typedef struct{
Uint8 type;
Uint8 state;
Uint16 x, y;
Sint16 xrel, yrel;
} SDL_MouseMotionEvent;
More information here,
http://www.libsdl.org/docs/html/sdlgetmousestate.html
http://www.libsdl.org/docs/html/sdlmousemotionevent.html
Thanks to emartel's answer, it's working as expected, here's the code:
SDL_Event event;
int drawing = 0;
while(running){
while(SDL_PollEvent(&event)){
switch(event.type){
//starts drawing when any mouse button is pressed
case SDL_MOUSEBUTTONDOWN:
drawing = 1;
boxRect.x = event.button.x;
boxRect.y = event.button.y;
break;
//keeps drawing while no button is released
case SDL_MOUSEMOTION:
if(drawing == 1){
boxRect.x = event.motion.x;
boxRect.y = event.motion.y;
}
break;
//stops drawing when any button is released
case SDL_MOUSEBUTTONUP:
drawing = 0;
break;
case SDL_QUIT:
running = 0;
break;
}
}
//draws to screen
if(drawing == 1){
SDL_FillRect(display,&boxRect,boxColor);
SDL_Flip(display);
}
}

Resources