admin管理员组文章数量:1244389
Using XMonad, I have a keybinding on my desktop that on Windows+F2 runs volume down and Windows+F3 runs volume up. This works just fine except it can be awkward at times.
So I'm trying to switch to just F2 and F3. The problem is that when I don't have my super key (Windows) in the keymap, as long as I am pressing the volume up or down buttons XMonad is running my simple volume up script. This creates a really unpredictable nature in my desktop where it is hard to control how much I am actually changing the volume unless I do it through the terminal.
((0, xK_F2), spawn "volume down 5"), -- run /usr/bin/volume --> ~/.config/scripts/cpp/volume
((0, xK_F3), spawn "volume up 5") -- ditto
While I do not know how it works, the spawn function essentially does a std::system("volume up 5")
which runs a very simple C++ program I wrote to easily change the volume the way I like it.
So here is the philosophical solution I have as a programmer with experience in procedural languages, but I cannot for the life of me implement in Haskell:
Every time the button is pressed save the CPU time in a variable like lastpress
and compare it to the last instance of the variable last press and if the difference is greater than 15ms (about the human reaction speed with some room for fine tuning/debugging) then allow spawn to run. This prevents spawn from running unless I am intentionally pressing the button. I can also write some more sophisticated function later to allow holding the button down but in a more predictable way, once I get the basic idea working.
Can anyone give me any pointers as to how I can accomplish this in Haskell?
For the sake of reference, here is what I see as a temporary solution, which I implemented by changing the volume
C++ program spawned by xmonad:
#include <cstdio>
#include <cstdlib>
#include <utility>
#include <string>
#include <fstream>
#include <chrono>
int main(int argc, char* argv[])
{
// store the command to run here
std::string command;
// If there is no argument, print the master volume
if(argc == 1) command = std::string("amixer get Master | awk -F\"[][]\" '/Left:/ { print $2 }'");
else
{
// This is not production code and I am using it in a very limited case on my system.
// Minimal security implemented!!
// Assume only 2 arguments
// Read the contents of the file into a string. This file contains the system time of the last time this point was reached in the code.
// This is a temporary solution until I learn how to do this in the xmonad.hs file instead
std::ifstream fs_in("/home/nick/.config/lastvolpress");
std::string str( (std::istreambuf_iterator<char>( fs_in )), (std::istreambuf_iterator<char>()) );
long long last_ms = std::stoll( str ); // Convert the last press time to a long long (probably the type that will be exported by count function later)
fs_in.close();
const auto now = std::chrono::system_clock::now(); // get the current time
long long now_ms = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count(); // Number of microseconds since 01-01-1970 0:00
if(now_ms - last_ms < 15) return 0; // If 15ms has not passed since the last time the volume was adjusted from this program, exit.
else
{
std::ofstream fs_out("/home/nick/.config/lastvolpress", std::ios::trunc);
fs_out << now_ms;
fs_out.close();
auto action = std::make_pair( std::string(argv[1]), std::string(argv[2]));
if(action.first == "up")
{
command = std::string("pactl set-sink-volume alsa_output.pci-0000_00_1f.3.analog-stereo +") + action.second + std::string("%");
}
else // assume down because I will know how to use it
{
command = std::string("pactl set-sink-volume alsa_output.pci-0000_00_1f.3.analog-stereo -") + action.second + std::string("%");
}
}
}
std::system(command.c_str()); // run the command
return 0;
}
This works precisely as I meant it to, so I have removed the modmask (Windows+) from my xmonad.hs code. Now my volume controls are so much more responsive and my problem is solved temporarily.
This is not a true fix, though, because it is short-sighted. This script is not meant to be used only by the volume keys, so I would like to separate the functionality between key-bindings and system scripts to keep my system anized. Also, since volume is not always running, I have to store the time in a file rather than just a variable as I could do in Xmonad. This is wasteful and contrived lol. I still will keep trying to accomplish this in haskell.
Using XMonad, I have a keybinding on my desktop that on Windows+F2 runs volume down and Windows+F3 runs volume up. This works just fine except it can be awkward at times.
So I'm trying to switch to just F2 and F3. The problem is that when I don't have my super key (Windows) in the keymap, as long as I am pressing the volume up or down buttons XMonad is running my simple volume up script. This creates a really unpredictable nature in my desktop where it is hard to control how much I am actually changing the volume unless I do it through the terminal.
((0, xK_F2), spawn "volume down 5"), -- run /usr/bin/volume --> ~/.config/scripts/cpp/volume
((0, xK_F3), spawn "volume up 5") -- ditto
While I do not know how it works, the spawn function essentially does a std::system("volume up 5")
which runs a very simple C++ program I wrote to easily change the volume the way I like it.
So here is the philosophical solution I have as a programmer with experience in procedural languages, but I cannot for the life of me implement in Haskell:
Every time the button is pressed save the CPU time in a variable like lastpress
and compare it to the last instance of the variable last press and if the difference is greater than 15ms (about the human reaction speed with some room for fine tuning/debugging) then allow spawn to run. This prevents spawn from running unless I am intentionally pressing the button. I can also write some more sophisticated function later to allow holding the button down but in a more predictable way, once I get the basic idea working.
Can anyone give me any pointers as to how I can accomplish this in Haskell?
For the sake of reference, here is what I see as a temporary solution, which I implemented by changing the volume
C++ program spawned by xmonad:
#include <cstdio>
#include <cstdlib>
#include <utility>
#include <string>
#include <fstream>
#include <chrono>
int main(int argc, char* argv[])
{
// store the command to run here
std::string command;
// If there is no argument, print the master volume
if(argc == 1) command = std::string("amixer get Master | awk -F\"[][]\" '/Left:/ { print $2 }'");
else
{
// This is not production code and I am using it in a very limited case on my system.
// Minimal security implemented!!
// Assume only 2 arguments
// Read the contents of the file into a string. This file contains the system time of the last time this point was reached in the code.
// This is a temporary solution until I learn how to do this in the xmonad.hs file instead
std::ifstream fs_in("/home/nick/.config/lastvolpress");
std::string str( (std::istreambuf_iterator<char>( fs_in )), (std::istreambuf_iterator<char>()) );
long long last_ms = std::stoll( str ); // Convert the last press time to a long long (probably the type that will be exported by count function later)
fs_in.close();
const auto now = std::chrono::system_clock::now(); // get the current time
long long now_ms = std::chrono::duration_cast<std::chrono::milliseconds>( now.time_since_epoch() ).count(); // Number of microseconds since 01-01-1970 0:00
if(now_ms - last_ms < 15) return 0; // If 15ms has not passed since the last time the volume was adjusted from this program, exit.
else
{
std::ofstream fs_out("/home/nick/.config/lastvolpress", std::ios::trunc);
fs_out << now_ms;
fs_out.close();
auto action = std::make_pair( std::string(argv[1]), std::string(argv[2]));
if(action.first == "up")
{
command = std::string("pactl set-sink-volume alsa_output.pci-0000_00_1f.3.analog-stereo +") + action.second + std::string("%");
}
else // assume down because I will know how to use it
{
command = std::string("pactl set-sink-volume alsa_output.pci-0000_00_1f.3.analog-stereo -") + action.second + std::string("%");
}
}
}
std::system(command.c_str()); // run the command
return 0;
}
This works precisely as I meant it to, so I have removed the modmask (Windows+) from my xmonad.hs code. Now my volume controls are so much more responsive and my problem is solved temporarily.
This is not a true fix, though, because it is short-sighted. This script is not meant to be used only by the volume keys, so I would like to separate the functionality between key-bindings and system scripts to keep my system anized. Also, since volume is not always running, I have to store the time in a file rather than just a variable as I could do in Xmonad. This is wasteful and contrived lol. I still will keep trying to accomplish this in haskell.
Share Improve this question asked Feb 16 at 13:39 Nicholas AllbrittonNicholas Allbritton 191 silver badge4 bronze badges 4 |1 Answer
Reset to default 3One solution would be to modify your C++ program to take an flock on its own executable for your desired delay time (and quit without changing the volume if another process already has the lock).
Another somewhat more intrusive solution would be to modify your keyboard repeat rate; check out xset for that.
Here's an example of the flock
approach. Toss this in a script somewhere. Supposing you name it chvol
, you can then bind some keys to spawn chvol +
and chvol -
in xmonad.
#!/bin/sh
sink=alsa_output.pci-0000_00_1f.3.analog-stereo
flock -n "$0" sh -c "pactl set-sink-volume $sink $1; sleep 0.1"
本文标签: haskellForce a delay between key presses in XMonadStack Overflow
版权声明:本文标题:haskell - Force a delay between key presses in XMonad - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1740205675a2241034.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
pactl
directly from XMonad (e.g.((0, xK_F2), spawn "pactl set-sink-volume 0 -3%")
). What I see when I press F2 and look at thepavucontrol
window is an initial 3% change, a brief pause, and then quick changes at about the rate I'd expect the input to be when holding a keyboard key. Also, the behaviour is exactly the same with or without the Super mod. Does that match what you're seeing, and what you'd like to make slower? (If so, it should be possible to simplify the question by removing some inessential details.) – duplode Commented Feb 16 at 15:15