I have some C++ code that looks like this:
CFRunLoopRef ref = CFRunLoopGetCurrent();
CGEventMask mask = CGEventMaskBit(kCGEventLeftMouseDown) |
CGEventMaskBit(kCGEventLeftMouseUp) |
CGEventMaskBit(kCGEventRightMouseDown) |
CGEventMaskBit(kCGEventRightMouseUp) |
CGEventMaskBit(kCGEventMouseMoved) |
CGEventMaskBit(kCGEventLeftMouseDragged) |
CGEventMaskBit(kCGEventRightMouseDragged) |
CGEventMaskBit(kCGEventScrollWheel);
CFMachPortRef tap =
CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap,
kCGEventTapOptionListenOnly, mask, OnMouseEvent, this);
CFRunLoopSourceRef source =
CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0);
CFRunLoopAddSource(ref, source, kCFRunLoopCommonModes);
CGEventTapEnable(tap, true);
CFRunLoopRun();
CGEventTapEnable(tap, false);
CFRunLoopRemoveSource(ref, source, kCFRunLoopCommonModes);
CFRelease(source);
CFRelease(tap);
OnMouseEvent is not being called when I move the mouse around, click, or scroll. Why not?
Make sure the application that's running this code is added to accessibility in security and privacy. In this screenshot, iterm is added, and so running this program from iterm will work.
Here's a github issue that includes code for running sample code you've mentioned, that only works when accessibility is turned on.
Related
I tried to draw the menu background in MFC Dlg App.
But I found if I declare CMenu subMenu in OnInitDialog(), the background color works well.
If I declare CMenu subMenu in Head file or like CMenu* subMenu=new CMenu; in OnInitDialog(), the Item's background will not be changed.This is weird and seems to be related to the CMenu lifetime.
Variable:
CMenu m_accountMenu;
CMenu subMenu;
CMFCApplication3Dlg::OnInitDialog()
......some code
// TODO: Add extra initialization here
m_accountMenu.CreateMenu();
subMenu.CreatePopupMenu();
m_accountMenu.AppendMenuW(MF_BYPOSITION | MF_POPUP | MF_STRING , (UINT)subMenu.m_hMenu, _T("Sub Info"));
subMenu.AppendMenuW(/*MF_MENUBREAK|*/ MF_STRING | MF_ENABLED , 1001, _T("sub Info1")); // note: MF_MENUBREAK can make it work
subMenu.AppendMenuW(MF_STRING | MF_ENABLED , 1002, _T("sub Info2"));
CBrush cBrush;
cBrush.CreateSolidBrush(RGB(255, 0, 0));
MENUINFO mi = { 0 };
mi.cbSize = sizeof(MENUINFO);
mi.fMask = MIM_BACKGROUND;
mi.hbrBack = cBrush;
SetMenuInfo(m_accountMenu.GetSubMenu(0)->GetSafeHmenu(), &mi);
cBrush.Detach();
SetMenu(&m_accountMenu);
Can someone help me unravel the mystery? Any useful information will be appreciated.
I have a really basic little command line app that grabs the mouse coordinates the next time the mouse is clicked.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGFloat displayScale = 1.0f;
if ([[NSScreen mainScreen] respondsToSelector:#selector(backingScaleFactor)])
{
displayScale = [NSScreen mainScreen].backingScaleFactor;
}
CGPoint loc = CGEventGetLocation(event);
CFRelease(event);
printf("%dx%d\n", (int)roundf(loc.x * displayScale), (int)roundf(loc.y * displayScale) );
exit(0);
return event;
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
CFMachPortRef eventTap;
CGEventMask eventMask;
CFRunLoopSourceRef runLoopSource;
eventMask = 1 << kCGEventLeftMouseDown;
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
1, eventMask, myCGEventCallback, #"mydata");
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRunLoopRun();
}
return 0;
}
I'm building it with cmake with the following file:
cmake_minimum_required(VERSION 3.0.0)
project (location)
set(CMAKE_C_FLAGS "-arch x86_64 -mmacosx-version-min=10.12 -std=gnu11 -fobjc-arc -fmodules")
This all worked fine until the upgrade to Mojave.
A bit of poking around shows this is down to the latest set of security updates and some hints (except CGEventTapCreate() is not returning null) about settings some values in Info.plist to allow the app to use the accessibility API. But I'm struggling to work out where to put it as I just have a single .m file with the code.
Edit
This needs to run as a none root user (company policy)
if the only way to get it to ask for permission then it can be extended to be a "GUI" app with a minimal UI
This app is just to grab the upper left hand corner of a region of the screen to feed to a second app that streams that area of screen to a second device. The code for the streamer is common across Win/Linux/MacOS so trying to keep the screen coordinate collection totally separate
As you surmise, event taps won't work on Mojave without having accessibility access. From the documentation:
Event taps receive key up and key down events if one of the following
conditions is true: The current process is running as the root user.
Access for assistive devices is enabled. In OS X v10.4, you can enable
this feature using System Preferences, Universal Access panel,
Keyboard view.
A GUI app will prompt the user to enable accessibility the first time it's needed, but it looks like a CLI app doesn't do that (which makes sense).
There is no way to enable this programatically or through a script; the user must do it themselves.
Running your tool as root should work - can you enforce that?
Otherwise, you can direct the user to the correct place in System Preferences:
tell application "System Preferences"
reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
activate
end tell
It may be possible using Carbon, if your app isn't sandboxed.
Finally, a quick test shows this is at least possible using IOHID. I shameless borrowed the KeyboardWatcher class from this answer. Then, modified the device type:
[self watchDevicesOfType:kHIDUsage_GD_Keyboard];
into:
[self watchDevicesOfType:kHIDUsage_GD_Mouse];
Finally, my callback looks like this:
static void Handle_DeviceEventCallback (void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef value)
{
IOHIDElementRef element = IOHIDValueGetElement(value);
IOHIDElementType elemType = IOHIDElementGetType(element);
if (elemType == kIOHIDElementTypeInput_Button)
{
int elementValue = (int) IOHIDValueGetIntegerValue(value);
// 1 == down 0 == up
if (elementValue == 1)
{
CGEventRef ourEvent = CGEventCreate(NULL);
CGPoint point = CGEventGetLocation(ourEvent);
printf("Mouse Position: %.2f, y = %.2f \n", (float) point.x, (float) point.y);
}
}
}
That is really a quick hack job, but it demonstrates this is possible and hopefully you can refine it to your needs.
I've found the CGEventTap documentation is out of date beginning with Mojave. Running as root used to act as a bypass for certain entitlements, but in Mojave this was tightened down. One bizarre side effect, as you noticed, is that root can still acquire the mach port for the tap; its just that no events can be read from it. If you try your application without running as root you should get the expected popup asking for permission.
If you do not get the popup, or need to run as root for other purposes, you can manually add your application to the trusted TCC database via SystemPreferences -> Security & Privacy -> Privacy -> Accessibility
settings some values in Info.plist to allow the app to use the accessibility API
I believe you mean adding entitlements (which are also a plist). The entitlement that allows an application to use the Accessibility API is the com.apple.private.tcc.allow entitlement (with a value of kTCCServiceAccessibility). As you can probably guess from the name it is only allowed on Apple signed binaries.
You can add these entitlements to your own app if you disable System Integrity Protection (SIP) and boot the kernel with the option amfi_get_out_of_my_way=1, but I wouldn't recommend it (and certainly any customers of yours wouldn't want to). With just SIP disabled you could manually add an entry to the TCC database to grant privileges, but still wouldn't recommend it.
Possible Alternative
You can use an event monitor:
NSEventMask mask = (NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask);
mouseEventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask: mask
handler:^(NSEvent *event){
// get the current coordinates with this
NSPoint coords = [NSEvent mouseLocation];
// event cooordinates would be event.absoluteX and event.absoluteY
... do stuff
}];
The documentation does mention:
Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted).
But I don't think that applies to mouse events.
I try to monitor file changes on OSX 10.10, starting with a fresh Cocoa application in Xcode, just adding the following code.
If I uncomment the last line in the snippet then I receive the file change events perfectly fine. But I can not make this last call because it should be a Cocoa GUI application.
I digged through a lot of documentation and can't find my error. Do I have to initialize or start this whole dispatch subsystem somehow?
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
int fd = open("<FILENAME>", O_EVTONLY);
if (fd == -1) return;
dispatch_queue_t qu = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
if (!qu) {
printf("can not get queue");
return;
}
unsigned long mask =
DISPATCH_VNODE_DELETE |
DISPATCH_VNODE_WRITE |
DISPATCH_VNODE_EXTEND |
DISPATCH_VNODE_ATTRIB |
DISPATCH_VNODE_LINK |
DISPATCH_VNODE_RENAME |
DISPATCH_VNODE_REVOKE;
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, mask, qu);
printf("source created\n");
if (!source) {
close(fd);
return;
}
printf("source valid\n");
dispatch_source_set_event_handler(source, ^{
printf("FILE CHANGED\n");
});
dispatch_resume(source);
printf("source resumed\n");
// If I call dispatch_main() I will receive the file system events as expected.
// But as a Cocoa application, I must not call this.
// Instead, I was under the impression that NSApplicationMain handles this.
//dispatch_main();
}
Grand Central Dispatch objects, such as dispatch sources, are automatically retained and released by ARC in recent versions of the compiler and frameworks.
At the end of your method, the last strong reference to source is lost and ARC is issuing an automatic dispatch_release(source). (It would also release the queue, but the source has another strong reference to that. So, if the source survived, so would the queue.)
You need to keep a strong reference to the source in an instance variable.
In Windows, it is possible to say
CreateWindow("myclass",...,WS_CHILD,...);
or
CreateWindow("myclass",...,WS_OVERLAPPEDWINDOW,...);
It is also possible to switch appearance at will:
SetWindowLongPtr((HWND)handle,GWL_STYLE,style_1);
SetWindowPos((HWND)handle,HWND_TOP,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
SetWindowLongPtr((HWND)handle,GWL_EXSTYLE,style_0);
SetWindowPos((HWND)handle,HWND_TOP,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
if(style_1&WS_CHILD)
{
HWND owner=GetWindow((HWND)handle,GW_OWNER);
SetParent((HWND)handle,owner);
}
else
{SetParent((HWND)handle,NULL);}
It seems to me that most other GUI toolkits make a clear distinction between top-level windows and other widgets. The question is if and how it is possible to implement similar behavior in GTK on X
There is a GTK_WINDOW_TOPLEVEL and GTK_WINDOW_POPUP - unless you know what you do use the first. Also RTM https://developer.gnome.org/gtk3/stable/gtk3-Standard-Enumerations.html#GtkWindowType
You need to create a GtkWindow or GtkMainWindow and then add your desired widget x via gtk_container_add to the window you created.
I am trying to use some classes from the MFC Feature Pack to improve the look & feel of my MFC application.
In my application, I use one CReBar object to dock three different toolbars. I have updated the class of this object to use CMFCReBar, but it doesn´t look good when using some visual styles.
It seems there's a problem in the Feature Pack because it happens even with the RebarTest example deployed with package.
This is a screenshot of the example application just changing the visual style to Office 2007 (using the app. menu not by code):
Screenshot of RebarTest example application http://img105.imageshack.us/img105/1057/rebartestep5.png
Has anybody successfully used CMFCReBar? Is there any other way to achieve the same without using it?
Basically you don't need to use a rebar control anymore. By simply creating your CMFCToolbars and CMFCMenuBar, calling EnableDocking on them and then using DockPane on each, they will dock and take on the Office 2007 (or whatever other theme you use) look-and-feel. Check out the WordPad Feature Pack sample, or create a new project (one with all the default settings is fine) using AppWizard to see an example.
Ok from your comment: if you want to dock toolbars next to each other you can use DockPaneLeftOf after DockPane. It tends to act strangely with toolbar placement in my experience if you don't DockPane both toolbars first.
I haven't found a good simple solution to stopping the toolbars from being dragged yet while docking next to each other, you can remove the CBRS_GRIPPER style, however that doesn't stop the toolbars from being dragged.
You can also just not call EnableDocking on the menubar or toolbars. This will make them fixed place. However, DockPaneLeftOf does not seem to work in this case, so you lose docking toolbars next to each other.
So it seems like one or the other right now if you want to stop docking, or dock toolbars next to each other.
Paul DiLascia wrote a class to lock the CToolBar, I used it to create this class which will work on CMFCToolbar. And you can copy it to do exactly the same sort of thing for CMFCMenuBar - just change MFCToolBar to MFCMenuBar and you're done.
class CLockedMFCToolBar : public CMFCToolBar
{
public:
CLockedMFCToolBar() : CMFCToolBar() {}
protected:
LRESULT CLockedMFCToolBar::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
if ((msg==WM_LBUTTONDOWN || msg==WM_LBUTTONDBLCLK))
{
// Got click or double-click and toolbar is locked: if mouse in "dead
// zone" then ignore the message--don't pass to control bar
CPoint pt(lp);
if (OnToolHitTest(pt, NULL) == -1)
return 0; // return without handling: bypass control bar dragging!
}
// pass unhandled messages subclassed window--this is important!*/
return CMFCToolBar::WindowProc(msg, wp, lp);
}
};
//////////////////////////////
// in CMainFrame declaration
protected:
CLockedMFCMenuBar m_wndMenuBar;
CLockedMFCToolBar m_wndToolBar1;
CLockedMFCToolBar m_wndToolBar2;
////////////////////////////
// in CMainFrame::OnCreate
if (!m_wndToolBar1.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar1.LoadToolBar(theApp.m_bHiColorIcons ? IDR_MAINFRAME_256 : IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!m_wndToolBar2.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar2.LoadToolBar(theApp.m_bHiColorIcons ? IDR_MAINFRAME_256 : IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
EnableDocking(CBRS_ALIGN_ANY);
m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar1.EnableDocking(CBRS_ALIGN_ANY);
m_wndToolBar2.EnableDocking(CBRS_ALIGN_ANY);
DockPane(&m_wndMenuBar);
DockPane(&m_wndToolBar2);
DockPane(&m_wndToolBar1);
DockPaneLeftOf(&m_wndToolBar1, &m_wndToolBar2);
m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() &
~(CBRS_GRIPPER | CBRS_BORDER_TOP | CBRS_BORDER_BOTTOM | CBRS_BORDER_LEFT | CBRS_BORDER_RIGHT));
m_wndToolBar1.SetPaneStyle(m_wndToolBar1.GetPaneStyle() &
~(CBRS_GRIPPER | CBRS_BORDER_TOP | CBRS_BORDER_BOTTOM | CBRS_BORDER_LEFT | CBRS_BORDER_RIGHT));
m_wndToolBar2.SetPaneStyle(m_wndToolBar2.GetPaneStyle() &
~(CBRS_GRIPPER | CBRS_BORDER_TOP | CBRS_BORDER_BOTTOM | CBRS_BORDER_LEFT | CBRS_BORDER_RIGHT));
I've noticed a few visual problems when using the Office 2007 style too - it seems to be a little bit buggy. Can you use one of the others instead? XP Luna seems to be quite stable...