Is there a way to programatically (e.g. from Python code) to prevent my Mac from dimming and subsequently locking the screen? Of course, after my application is done, I would like to enable normal operation again. I know about caffiniate, but that applies to the whole application...that is not what I want. At some point in my code I want to disable dimming and then at some other point I want to enable it again.. Any tips, hints, suggestions?
You can try IOKit. Use IOPMAssertionDeclareUserActivity will wake the screen if necessary and keep it awake until the user's display sleep Energy Saver settings:
IOReturn ret;
CGError err;
IOPMAssertionID assertId;
ret = IOPMAssertionDeclareUserActivity(CFSTR("Stay awake!"), kIOPMUserActiveLocal, &assertId);
if (ret == kIOReturnSuccess)
{
// The screen is on
}
However, from the documentation for that method:
"If you prefer to hold the display awake for a longer period and you know how long you'd like to hold it, consider taking assertion kIOPMAssertionTypePreventUserIdleDisplaySleep using IOPMAssertionCreateWithDescription API instead."
Sounds closer to what you want. But I haven't tried it so I don't have a sample.
Related
I have an old C program for displaying caller ID called YAC. Fortunately, the author Jensen Harris provided the source.
15 years ago, I modified the source to turn on the monitor if the computer was awake but the monitor was off. The code below worked well, turning on the monitor and making the caller ID message visible on the screen.
// TG - add a call to turn on the monitor if it is sleeping.....
SendMessage(hwnd, WM_SYSCOMMAND, SC_MONITORPOWER, -1);
Recently the behavior has changed (presumably a Windows update changed something)
Now when a Caller ID message should be displayed, the monitor turns on (as evidenced by the LED), but the screen remains black. The monitor remains in the black-screen condition for a few seconds, then turns off again.
What additional or different call is now required to cause Windows to activate the display and show the desktop? Possibly this could be forced by sending a mouse move, but is there a better way?
EDIT:
I have implemented the following additional code to press and release ESC. I was unable to find a good example of a relative mouse move of 1 pixel, so I used an example for keyboard. I will test and see if it is effective.
INPUT inputs[2];
UINT uSent;
// reference: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
ZeroMemory(inputs, sizeof(inputs));
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki.wVk = VK_ESCAPE;
inputs[1].type = INPUT_KEYBOARD;
inputs[1].ki.wVk = VK_ESCAPE;
inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
EDIT2 - I can confirm this approach does work to cause the monitor to display video, but of course has the potential for side-effects as any keyboard or mouse action would. I would still be interested in learning of a pure API function that works to fully wake the system like SC_MONITORPOWER used to.
I want my UI to change design depending on whether the screen is composited (thus supporting certain effects) or not. Is it possible to
Reliably query whether the X server is running a compositing window manager
Get notified when compositing is switched on/off?
Solution:
To elaborate on Andrey Sidorov's correct answer for people not so familiar with the X11 API, this is the code for detecting a EWMH-compliant compositor:
int has_compositor(Display *dpy, int screen) {
char prop_name[20];
snprintf(prop_name, 20, "_NET_WM_CM_S%d", screen);
Atom prop_atom = XInternAtom(dpy, prop_name, False);
return XGetSelectionOwner(dpy, prop_atom) != None;
}
EWMH-compliant compositors must acquire ownership of a selection named _NET_WM_CM_Sn, where n is the screen number
To track compositor you'll need to check if selection is _NET_WM_CM_S0 is owned by anyone (assuming you are on screen 0) using XGetSelectionOwner. If not owned, acquire ownership yourself and monitor SelectionClear events to detect when compositor is started.
I need to programmatically disable/suppress system-wide touch gestures on Mac OS. I'm referring to gestures such as the 4-finger swipe between spaces, etc.
I've looked to EventTap but that doesn't appear to be an option (despite previous reports here - perhaps it's changed under 10.8)
I've also tried numerous ways of changing the the system preferences programatically. For example, I've tried using IOConnectSetCFProperties on the service having located it using IORegistryEntryCreateCFProperties.
I've also delved into the trackpad preference pane to see how they do it, and I tried to reproduce it (ignore any create/release inconsistencies, this is just test code):
NSInteger zero = 0;
CFNumberRef numberWith0 = CFNumberCreate(kCFAllocatorDefault, kCFNumberNSIntegerType, &zero);
CFMutableDictionaryRef propertyDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(propertyDict, #"TrackpadFourFingerHorizSwipeGesture", numberWith0);
io_connect_t connect = getEVSHandle(); // Found in the MachineSettings framework
if (!connect)
{
NSLog(#"Unable to get EVS handle");
}
kern_return_t status = IOConnectSetCFProperties(connect, propertyDict);
if (status != KERN_SUCCESS)
{
NSLog(#"Unable to get set IO properties");
}
CFRelease(propertyDict);
CFPreferencesSetValue(CFSTR("com.apple.trackpad.fourFingerHorizSwipeGesture"), _numberWith0, kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
CFPreferencesSetValue(CFSTR("TrackpadFourFingerHorizSwipeGesture"), _numberWith0, CFSTR("com.apple.driver.AppleBluetoothMultitouch.trackpad"), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
CFPreferencesSynchronize(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
status = BSKernelPreferenceChanged(CFSTR("com.apple.driver.AppleBluetoothMultitouch.trackpad"));
In this case it appears to work, there are no errors and the option becomes disabled in the system preference pane, however the four finger gesture continues to work. I suspect that logging out then in will have an effect, but I haven't tried because that's not good enough in any case.
It's worth noting that the Pref Pane itself also calls BSKernelPreferenceChanged, but I don't know which framework that might be in order to link to it. Perhaps that's the key to the problem...
UPDATE: Actually I've now found it and linked it to. Adding that call made no difference, although it returns 1 which may indicate an error. I've added the call to the code above.
Finally I tried this from the terminal:
defaults write -globalDomain com.apple.trackpad.fourFingerHorizSwipeGesture 0
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadFourFingerHorizSwipeGesture 0
That doesn't have an immediate effect either.
I don't believe that this isn't possible, there must be a way...
MAS compatibility is not required.
I'm also trying to do this.
Event taps does not work, neither does having a view that is first responder.
From Apple docs:
However, there are certain system-wide gestures, such as a four-finger swipe. for which the system implementation takes precedence over any gesture handling an application performs.
The only way i've been able to stop the system wide gestures is using CGDisplayCapture. This gives my application exclusive access to all events... but also a fullscreen drawing context.
Perhaps it's possible to see what calls are made to the quartz event services when entering this mode
https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/QuartzDisplayServicesConceptual/Articles/DisplayCapture.html
I think you are looking in the wrong spot for disabling the touch events. The way OSX (and many other systems) is that the first responder in the view chain to handle the event will stop the event from propagating. You will need to write event handlers in your views for each of the touch events you want to handle, and if they exist, the OS will stop sending the events all the way to finder or whatever other application is next in line to handle the touch events.
See: http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/EventOverview/HandlingTouchEvents/HandlingTouchEvents.html
Specifically: Handling Multi-Touch Events (call setAcceptsTouchEvents, then implement touches...WithEvent...)
Hope that helps!
I'm trying to use CGAssociateMouseAndMouseCursorPosition(NO) in a program. This disconnects the mouse from the on screen cursor when your application is "in the foreground". Unfortunately it also disconnects it when Mission Control or the application switcher or who knows what else comes up.
So far I know:
The application is still active.
The window is still key.
Nothing is sent to the default notification center when these things come up.
The application stops receiving mouse moved events, but an NSEvent addGlobalMonitorForEventsMatchingMask:handler: also does not receive them, which is strange to say the least. It should receive any events not delivered to my application. (I was planning to detect the missing events to know when to associate the mouse again.
So, is there a way to detect when my application is no longer in control, specifically because Mission Control or the switch has taken over? They really expect the mouse to work and I need to restore that association for them.
I share your surprise that a global event monitor isn't seeing the events. In a similar situation, I used a Quartz Event Tap for a similar purpose. The Cocoa global event monitor is quite similar to event taps, so I figured it would work.
I put the tap on kCGAnnotatedSessionEventTap and compared the result from CGEventGetIntegerValueField(event, kCGEventTargetUnixProcessID) to getpid() to determine when the events were going to another app (e.g. Mission Control or Exposé). (I disable the tab when my app resigns active status, so it should only receive events destined for another app when this sort of overlay UI is presented.)
By the way, you mentioned monitoring the default notification center, but, if there's a notification about Mission Control or the like, it's more likely to come to the distributed notification center (NSDistributedNotificationCenter). So, it's worth checking that.
I needed to check for mission control being active and ended up with an approach along the lines of Ken's answer.
Sharing is caring so here is the smallest sensible complete code that worked for me: (Swift 5)
import Foundation
import AppKit
let dockPid = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.processIdentifier
var eventTargetPid: Int32?
let eventTap = CGEvent.tapCreate(
tap: .cgAnnotatedSessionEventTap,
place: .headInsertEventTap,
options: .listenOnly,
eventsOfInterest: CGEventMask(
(1 << CGEventType.mouseMoved.rawValue)
| (1 << CGEventType.keyDown.rawValue)
),
callback: { (tapProxy, type, event, _:UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? in
// Now, each time the mouse moves this var will receive the event's target pid
eventTargetPid = Int32(event.getIntegerValueField(.eventTargetUnixProcessID))
return nil
},
userInfo: nil
)!
// Add the event tap to our runloop
CFRunLoopAddSource(
CFRunLoopGetCurrent(),
CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0),
.commonModes
)
let periodSeconds = 1.0
// Add a timer for periodic checking
CFRunLoopAddTimer(CFRunLoopGetCurrent(), CFRunLoopTimerCreateWithHandler(
kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent() + periodSeconds, periodSeconds, 0, 0,
{ timer in
guard eventTargetPid != dockPid else {
print("Dock")
return
}
print("Not dock")
// Do things. This code will not run if the dock is getting events, which seems to always be the case if mission control or command switcher are active
}), .commonModes)
CFRunLoopRun()
This simply checks whether the dock was the one to receive the last event of interest (here that includes mouse movement and key-downs).
It covers most cases, but will report the wrong value between the command switcher or mission-control hiding and the first event being sent to a non-dock app. This is fine in my use-case but could be an issue for other ones.
Also, of course, when the dock at the bottom is active, this will detect that too.
Have you tried asking NSRunningApplication?
I need to perform several operations on a list of windows (minimize some of them, restore others) in order to switch between two or more set of windows at once.
The problem with this are those animations you can see when minimizing and restoring a window. The whole process look terrible with all those animations going in and out, up and down.
I cannot, however, disable those animations because this is for other computers and i dont want to change other people's settings, plus those animations are actually useful when you minimize/restore one window only (i.e. when YOU do it manually) because you can see what is happening, but for doing it programmatically on several windows at a time, it's not nice.
I'm currenlty using the SendMessage function to send the WM_SYSCOMMAND message with params SC_MINIMIZE/SC_RESTORE. I dont know whether there is another way.
So, the question:
How can I minimize/restore a window programatically without the animation effect??
PS: The programming language is not important. I can use any language that's nessesary for accomplishing this.
SetWindowPlacement with SW_SHOWMINIMIZED or SW_RESTORE as appropriate for showCmd in WINDOWPLACEMENT seems to bypass window animation. I'd keep an eye on the functionality for future versions of the OS though since documentation does not mention anything about animation.
How about Hide > Minimize > Show ?
You could temporarily disable the animations and then restore the user's original setting.
class WindowsAnimationSuppressor {
public:
WindowsAnimationSuppressor() : m_suppressed(false) {
m_original_settings.cbSize = sizeof(m_original_settings);
if (::SystemParametersInfo(SPI_GETANIMATION,
sizeof(m_original_settings),
&m_original_settings, 0)) {
ANIMATIONINFO no_animation = { sizeof(no_animation), 0 };
::SystemParametersInfo(SPI_SETANIMATION,
sizeof(no_animation), &no_animation,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
m_suppressed = true;
}
}
~WindowsAnimationSuppressor() {
if (m_suppressed) {
::SystemParametersInfo(SPI_SETANIMATION,
sizeof(m_original_settings),
&m_original_settings,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
}
private:
bool m_suppressed;
ANIMATIONINFO m_original_settings;
};
void RearrangeWindows() {
WindowsAnimationSuppressor suppressor;
// Rearrange the windows here ...
}
When the suppressor is constructed, it remembers the user's original setting and turns off the animation. The destructor restores the original settings. By using a c'tor/d'tor, you ensure that the user's settings are restored if your rearranging code throws an exception.
There is a small window of vulnerability here. In theory, the user could change the setting during the operation, and then you'll slam the original setting back. That's extremely rare and not really that bad.