How do I get keyboard events in an NSStatusWindowLevel window while my application is not frontmost? - cocoa

After creating a translucent window (based on example code by Matt Gemmell) I want to get keyboard events in this window. It seems that there are only keyboard events when my application is the active application while I want keyboard events even when my application isn't active but the window is visible.
Basically I want behavior like that provided by the Quicksilver application (by blacktree).
Does anybody have any hints on how to do this?

There are two options:
Use GetEventMonitorTarget() with a tacked-on Carbon run loop to grab keyboard events. Sample code is available on this page at CocoaDev.
Register an event trap with CGEventTapCreate. Sample code can be found in this thread from the Apple developer mailing list.
Edit: Note that these methods only work if you check off “Enable access for assistive devices” in the Universal Access preference pane.

A simpler route that may work better for you is to make your app background-only. The discussion on CocoaDev of the LSUIElement plist key explains how to set it up. Basically, your application will not appear in the dock or the app switcher, and will not replace the current application's menu bar when activated. From a user perspective it's never the 'active' application, but any windows you open can get activated and respond to events normally. The only caveat is that you'll never get to show your menu bar, so you'll probably have to set up an NSStatusItem (one of those icon menus that show up on the right side of the menu bar) to control (i.e. quit, bring up prefs, etc.) your application.
Edit: I completely forgot about the Non-Activating Panel checkbox in Interface Builder. You need to use an NSPanel instead of an NSWindow to get this choice. This setting lets your panel accept clicks and keyboard input without activating your application. I'm betting that some mix of this setting and the Carbon Hot Keys API is what QuickSilver is using for their UI.

Update:
Apple actually seems to have changed everything again starting with 10.5 BTW (I recently upgraded and my sample code did not work as before).
Now you can indeed only capture keydown events setting up an event tap if you are either root or assistive devices are enabled, regardless on which level you plan to capture and regardless if you selected to capture (which allows you to modify and even discard events) or to be listen only. You can still get information when flags have changed (actually even change these) and other events, but keydown under no other circumstances.
However, using the carbon event handler and the method RegisterEventHotKey() allows you to register a hotkey and you'll get notified when it is pressed, you neither need to be root for that nor do you need anything like assistive devices enabled. I think Quicksilver is probably doing it that way.

Related

Disabling the auto-hiding of scroll bars on Lion

In Mac Lion, the scroll bar hides itself after a few seconds if no activity occurs. I have written an apple script to modify that behavior. I have to turn on the radio button every time my app launches. My question is, I have a cocoa application. Is it possible to keep the scroll enabled for the application always with out having to change the settings in system preferences.I don't want to enable for all the other apps always. And is the only way via the applescript. Or is there a defaults write to enable the scroll bars in lion ?.
I don't know about a defaults key to set up the style.
When you change the Appearance preference panel's "Show scroll bars", all the NSScrollView instances are notified and receive a setScrollerStyle: with the new style (through the NSPreferredScrollerStyleDidChangeNotification notification).
You can achieve the same result by explictly calling setScrollerStyle: on the NSScrollView with the NSScrollerStyleLegacy scroller style.
You can write to defaults to accomplish this.
The key is AppleShowScrollBars and it has three possible values:
Automatic
WhenScrolling
Always
To set it system-wide from the command line, you could do:
defaults write -g AppleShowScrollBars Always
It can also be done programmatically by modifying preferences in any of various ways. It can get a little tricky depending on application sandboxing. This blog post explains it in more detail.

How do I allow ⌘V into NSTextField without having a Menu?

I've removed the menu from my cocoa app, all of the interaction should happen from within a status item, that links to a menu, that links to different NSPanels. But this seems to have removed the ability to ⌘C or ⌘V within a NSTextField. Is there a way to add this back without having to have a standard menu included with my app?
Even if your app is a faceless background app, so it never shows a menu bar, you should still have a full main menu because that's what provides all of the default actions (and enables the user to redefine the key commands if they so choose). If you remove the main menu, you have to reimplement everything in it, including anything Apple adds in the future, in code.
And I'd especially warn you against trying to handle keyboard shortcuts yourself. That's damned tricky. A lot of applications get it wrong, causing us Dvorak users (among others) to curse their developers.
I assume you can just implement the actions originally connected to the menu items in a keyDown event. Check out this page for details: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html
Just have it respond to cmd-v with paste:, and cmd-C with copy:
Edit: Though I have to agree with Peter, you really should have a main menu...

Is there a way to make changes to the titlebar with GTK2?

I have a desktop application written in Ruby that is using GTK2. It's just a small test application to play with GTK2, but I'm having problems achieving what I want to do. Is there any way using GTK2 to get at the titlebar (apart from setting the title), specifically to either add a button to it (beside the min/max/etc, B in the below diagram) or to add an option to the menu that pops up when you click the icon on the titlebar (A in the below diagram)?
I'm thinking there might not be because GTK is meant to work with many many different window managers, but I just wondered if there was. As a side question, what event does clicking the 'cross' button fire? At the moment if the user clicks that the window disappears but the program doesn't end - I need to capture that event and quit the program.
Thanks for any help, including hitting me over the head and telling me how silly I am.
Note that this is possible in GTK 3.10 and up, by using gtk_window_set_titlebar(). It replaces the window manager's title bar with a custom one. GtkHeaderBar is a good custom title bar class to use.
You can't, however, make it look just like the window manager would, because you won't know which window manager the user is running.
No, the title bar is owned by the window manager and you will typically not have direct access to it.
When the user tries to close the window by clicking the window manager's button, the window will receive the delete event.

Cocoa accessibility API, can I click a window in the background without activating it?

I've been searching forever for a solution to this, so I thought I'd seek out the brainpower of greater minds than mine. I'm developing a Cocoa app that uses the Accessibility API to manipulate another program (it's a hotkey app). The app I'm controlling typically has multiple windows open, with some hidden behind others. What I would like to do, if it's possible, is to send mouse events to windows using the Accessibility API in a way that presses a button in the window without bringing it to the foreground (interact with the window but don't activate it). The reason I'm trying to do this is that sending the mouse event to this other window will force it to the foreground and disrupt the user's interaction with the foremost window.
This is possible on Windows - apparently, because apps similar to mine do it there - but I'm getting the feeling that this isn't possible with Cocoa, given the way the window manager works. Am I mistaken?
Accessibility is higher-level than that. You send, for example, AXPress actions to AXButton objects, but “press” is not necessarily a click—pressing the space bar while a view is focused, for example, is also a “press”. AXPress is a high-level action that means “do your thing”, which obviously has meaning for some views (such as buttons) and not others (such as fields).
Accessibility activating the application does make sense when you look at it from its intended purpose: Assistive devices for disabled users. If the user “presses” something by whatever means, they probably intend to activate the application and work in it.
Quartz Event Services will get you almost there: You can create an event tap for the process you want to control, and you can forge events and send them to a tap. The catch is that you can only send events to a tap when the tap fires—i.e., when the application already has an event to deal with. When it doesn't, you're stuck.

How to assume/steal another process's windows as my own?

I'd like to show another app's windows under my app's taskbar button. It's a background app that reports another process's windows as my app's own. Is there any universal way to do this, e.g. each "new" window, alert glow, progressmeter, and other taskbar features, show under my own app's button?
For example, Winfox runs under its own process and steals Firefox's windows. It also adds features, but that's irrelevant -- I just want to support another app's existing taskbar features under my own app's button -- multiple windows, progressmeter, alert flashing, error flashing, mini-icons, etc. Is there a near-universal way to steal an app, or is it largely app-specific? Thanks!
You should be able to use SetParent() to take ownership of a window, but I'm not sure how much this will help you in your attempt to add taskbar features to the legacy app.

Resources