I wrote an FMX (FireMonkey) application and I want to change (increase / decrease) and mute / unmute the master volume output in OS X. Either in Delphi or C++Builder. Alternatively I would do it by simulating key presses of the specific keys of the keyboard.
For Windows, it is fairly easily by simulating key presses with SendInput() or even easier with keybd_event().
This is how it works on windows for me:
// vkVolumeUp / vkVolumeDown / vkVolumeMute
// VK_VOLUME_UP / VK_VOLUME_DOWN / VK_VOLUME_MUTE
keybd_event(vkVolumeUp, 1, 0, 0);
keybd_event(vkVolumeUp, 1, KEYEVENTF_KEYUP, 0);
But I can't manage to compile it for OS X, since the IDE tells me that it doesn't know this functions. A direct way to change the volume would be even better if it is possible.
This is probably a long-winded, inefficient way of doing it, but you can mute the volume from the Terminal like this:
osascript -e 'set volume with output muted'
and increase it by 20 notches like this
osascript -e 'set volume output volume ((output volume of (get volume settings)) + 20)'
I presume you could use the system() command to execute those till someone tells you a better way.
Related
I'm using a 14 inch M1 Pro Macbook Pro, running Mac OS Monterey 12.6.
I'm making an OpenCV and Mediapipe based Computer Vision Project that allows me to use hand detection to control my Mac's volume. The code detects the distance between the tips of my index finger and thumb using the webcam, and changes the volume based on that. I've been trying to use osascript to set the volume:
osascript.osascript("set volume output volume 0")
It works, but only for hard coded values like 0, 5 and 10. How do I pass a variable value N to osascript:
osascript.osascript("set volume output volume N")
If I could pass that variable value, then I could actually vary the volume instead of having it set at either 0, 5 or 10. The documentation hasn't been very helpful, anybody have any ideas or alternatives instead of osascript?
I've tried applescript but couldn't figure it out.
I'm guessing you are actually using Python, though you don't mention it in the question or in your tags.
If so, you can use an "f-string" like this - note the f at the start:
N = 7
osascript.osascript(f"set volume output volume {N}")
Use command
osascript -e "set Volume 10"
or use python library driver_controler https://github.com/Alexandro1112/driver-controller
When the standard keyboard key to increase the volume is hit on windows, a small window appears in the upper left displaying the volume and possibly information about playing media. I am looking for a way to trigger the window without changing the volume status, preferably in an easy to integrate way with Autohotkey.
Windows 8 introduced the MediaControl class that lets Modern apps hook into the system playback control. In Windows 8.1 and 10 it was replaced by the SystemMediaTransportControls class.
While it supports "Manual control of the System Media Transport Controls" there does not seem to be a way to show/hide the overlay and certainly not from a desktop app.
Going into undocumented territory I found the class name of the overlay and that lead me to HideVolumeOSD. Unfortunately the class names are rather generic so you probably have to look at the size of the window as well to determine if it is the volume overlay.
I don't know if just showing the window will work, Windows is not expecting it to be visible except in response to keyboard and playback events. The HideVolumeOSD app uses keybd_event (volume up/down) to trigger it but this is problematic as noted in the comments...
I set up an autohotkey to send volume up, the volume down (For shortcut ctrl+pgdn).
^PgDn::
Send {Volume_Up}
Send {Volume_Down}
return
The desired behavior when used with volume control keys can be gotten by storing the current volume, sending a media key press, then updating the volume to the stored value +/- 1 (or +0, if just seeking to display the OSD).
There is an edge case when mimicking Volume_Down when the volume is <=3. The sent Volume_Down keypress triggers the mute, but this can be accommodated for by manually setting the mute to be off afterwards, or by pre-setting the volume to 4, which prevents the rounding to 0. Which method to choose depends upon if you prefer a brief blip of potentially louder sound (pre-set to 4) or a brief mute (disable mute afterwards) when the volume is lowered from <=3 values.
The following code is in AHK v2.
; Prefix with '$' so as to prevent self triggering, since internally sending Volume_Up
$Volume_Up::{
; Get the current volume. SoundGetVolume returns a float which often needs rounding
volume:=Round(SoundGetVolume())
; Send the Volume_Up key, so the media display is triggered
Send "{Volume_Up}"
; Indiscriminately set the volume manually to volume+1
SoundSetVolume(volume+1)
}
; Much the same as above, yet when sending Volume_Down at volumes <=3 the volume
; is rounded down to 0 which triggers mute. The volume is then set properly,
; yet remains muted. In order to not hear a cut in the audio, you need to set
; the volume to 4 when (1<volume<=3) so that the Volume_Down doesn't round it
; down to 0, or disable the mute afterwards (if volume > 1). This causes a
; brief volume spike or mute blip, respectively. Currently the volume spike option
; is uncommented.
$Volume_Down::{
volume:=Round(SoundGetVolume())
; Bumping the volume before sending the Volume_Down to prevent mute blip (if needed)
if(1 < volume and volume <= 3){
SoundSetVolume(4)
}
Send "{Volume_Down}"
SoundSetVolume(volume-1)
; ; Disable accidental triggering of mute when volume >= 3 after sending Volume_Down
; if(volume > 1) {
; SoundSetMute(0)
; }
}
To just trigger the OSD as the question asks the following works. Hitting a volume key then quickly resetting the volume will displays it, yet considerations need to be made if currently muted as to prevent a blip of sound. Volume keys are used since double toggling Volume_Mute causes a gap in the sound output.
; Trigger the on screen display of the volume bar by hitting Volume_Up and
; then quickly resetting the volume. If muted and send Volume_Up, we will
; hear audio at original volume for a brief second. To prevent this, we
; can set the volume to 0 and send Volume_Down instead.
^PgUp::{
volume:=Round(SoundGetVolume())
muted:=SoundGetMute()
; Trigger the display with a volume key (might briefly bump the volume if unmuted)
if (muted or volume == 0) {
SoundSetVolume(0)
Send "{Volume_Down}"
} else {
Send "{Volume_Up}"
}
; Reset to the original volume and mute status
SoundSetMute(muted)
SoundSetVolume(volume)
}
All of the same code, yet uncommented:
$Volume_Up::{
volume:=Round(SoundGetVolume())
Send "{Volume_Up}"
SoundSetVolume(volume+1)
}
$Volume_Down::{
volume:=Round(SoundGetVolume())
if(1 < volume and volume <= 3){
SoundSetVolume(4)
}
Send "{Volume_Down}"
SoundSetVolume(volume-1)
}
^PgUp::{
volume:=Round(SoundGetVolume())
muted:=SoundGetMute()
if (muted or volume == 0) {
SoundSetVolume(0)
Send "{Volume_Down}"
} else {
Send "{Volume_Up}"
}
SoundSetMute(muted)
SoundSetVolume(volume)
}
Building on Anna Wang's answer (https://stackoverflow.com/a/62012058/3251466).
This will restore the volume even when it was at an odd value.
^PgDn:: ;Ctrl+Page Down
SoundGet, original_volume
SendInput {Volume_Down}
SoundSet, original_volume
return
I've been working on a script that will run on startup but I am running into a problem. The script is meant to slow mouse acceleration because I use a gaming mouse and it's always too fast.
When I use xinput --list I get this output (out of many other lines):
SteelSeries Sensei Raw Gaming Mouse id=10 [slave pointer (2)]
When I open terminal and run this command, everything runs fine and my sensitivity is changed:
xinput --set-prop 10 "Device Accel Constant Deceleration" 2
However, when I put the above string in a shell.sh with 'eval' in the beginning, it prompts me the following error:
property 'Device' doesn't exist, you need to specify its type and
format
What am I doing wrong?
You don't need eval; put the command exactly as you used it from the terminal in your script.
The problem is that eval essentially reparses the string it receives, which results from joining the arguments arguments with whitespace. Your eval command is equivalent to
xinput --set-prop 10 Device Accel Constant Deceleration 2
You could use eval if you quoted the entire string:
eval 'xinput --set-prop 10 "Device Accel Constant Deceleration" 2'
but there is no reason to do so; just use
xinput --set-prop 10 "Device Accel Constant Deceleration" 2
As said on other posts, you don't need to use eval. I would also add something else: you should use the name of the device, rather than the id number, because the id number can change if you add things to your computer (or for other more obscure reasons, too). I would recommend this:
xinput --set-prop "SteelSeries Sensei Raw Gaming Mouse" "Device Accel Constant Deceleration" 2
I'm basically trying to figure this out because I want to use my iMac as an external monitor for my macbook air. I also want to use the iMac keyboard for my macbook air however for some reason, Apple has decided that once you press and hold Command F2 to activate Target Display Mode (meaning it is now an external monitor) that the keyboard paired with the iMac cannot be unpaired with the iMac.
To work around this I thought I would just pair the keyboard with the macbook air initially (leaving the iMac without a keyboard) and create an Applescript macro that would simulate the keyboard pressing and holding the Command F2 for five seconds eliminating the need to go buy another Apple keyboard.
Here's what I have so far and it doesn't work. It's telling me F2 is not right. I'm pretty sure F2's key code is 120.
tell application "System Events"
key down Command
key down F2
delay 5
key up Command
key up F2
end tell
Does anyone know how I might accomplish this?
Observations as of OS X 10.9.1:
There's a problem with the way you're sending F2 (you need to use (key code 120) instead of just 120), but the larger problem is that key up/down only works as expected with modifier keys.
While NON-modifier keys can be sent (using (key code <n>) syntax), the up / down aspect is ignored, making both key down (key code <n>) and key up (key code <n>) statements effectively the same as key code <n> (i.e., a Key Down event immediately followed by a Key Up event is sent).
There's a suggested workaround here, based on repeatedly sending keystrokes in short sequence - it's worth a try, but from a technical perspective it's not the same as keeping a key [combination] held down, so I'm not sure it'll work.
Adapted to your situation (and replacing key down with key code), we get:
tell application "System Events"
set now to the seconds of the (current date)
set later to now + 5
if later > 60 then set later to later - 60
key down command
# Workaround: send F2 repeatedly.
repeat while the seconds of the (current date) is not later
key code 120
end repeat
key up command
end tell
As I said: this may not work; also note that the loop is "tight" meaning that it'll make your machine pretty busy (if sending keys repeatedly, but not necessarily as fast as possible is an option, you could insert a short delay).
Some optional background info:
The key up and key down commands, while also requiring the System Events context, are NOT exposed in the System Events.sdef, the app's dictionary (only key code and keystroke are listed) - this may indicate that Apple doesn't officially support them.
On OS X 10.9.1 (unlike on OS X 10.8 - don't know about earlier versions) there is a bizarre bug where an extra "a" keypress is sent whenever you use key down with a (keycode <n>) specifier.
Ways of determining key-code values (gleaned from various other SO answers, mostly here):
Key Codes, a free GUI app for interactive use - very handy.
The following header file on your system (list of codes in hex format):
/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
List of decimal codes (incomplete):
I've started a project to do something similar, namely monitor the iMac and automatically trigger target display mode and toggle off bluetooth when a Macbook is connected. You can download it from https://github.com/duanefields/VirtualKVM.
i'm trying to send fake keyboard input to an application that's running in a Remote Desktop session. i'm using:
Byte key = Ord("A");
keybd_event(key, 0, 0, 0); // key goes down
keybd_event(key, 0, KEYEVENTF_KEYUP, 0); // key goes up
Now this code does send the character "a" to any local window, but it will not send to the remote desktop window.
What that means is i use Remote Desktop to connect to a server, and i then open Notepad on that server. If i manually punch keys on the keyboard: they appear in Notepad's editor window. But keybd_event's fake keyboard input not causing "a"'s to appear in Notepad.
How can i programtically send fake keyboard input to an application running inside a remote desktop connection, from an application running on the local machine?
Nitpickers Corner
In this particular case i want to do this becase i'm trying to defeat an idle-timeout. But i could just as well be trying to
perform UI automation tests
UI stress tests
UI fault finding tests
UI unit tests
UI data input tests
UI paint tests
or UI resiliance tests.
In other words, my reasons for wanting it aren't important
Note: The timeout may be from remote desktop inactivity, or perhaps not. i don't know, and it doesn't affect my question.
Answer
Although Microsft says you don't need to, and you should not, send the OEM code, you need to send the OEM scan codes. In this example i need to send the OEM scan codes for
key A goes down
key A goes up
There is a picture of a chart on CodeProject that lists the make and break scan codes for various keys:
In my case the original calls to keybd_event need to be changed to:
Byte key = Ord("A");
keybd_event(key, 0x1E, 0, 0); // key goes down
keybd_event(key, 0x9E, KEYEVENTF_KEYUP, 0); // key goes up
i tested this, and it works. So all is well.
May be you can execute an autoit script with PsExec, a light-weight telnet-replacement that lets you execute processes on other systems, complete with full interactivity for console applications, without having to manually install client software.
(AutoIt is quite capable to send any signal (keys or other) to any window application, and could be launched with PsExec on the remote desktop)
An AutoIt script like KillSaver, for instance, is designed to move the mouse to avoid any prolong idle time on a computer!
This worked very well thank you. In order to get the keyboard scan code one can use:
int scan;
scan = MapVirtualKey(key & 0xff, 0);
keybd_event(key, scan, 0, 0); // key goes down
keybd_event(key, scan | 0x80, KEYEVENTF_KEYUP, 0); // key goes up
You could use SendMessage();
It's really a much better simulator for keys.
Well, good luck on this!