Linux/X11 input library without creating a window - linux

Is there a good library to use for gathering user input in Linux from the mouse/keyboard/joystick that doesn't force you to create a visible window to do so? SDL lets you get user input in a reasonable way, but seems to force you to create a window, which is troublesome if you have abstracted control so the control machine doesn't have to be the same as the render machine. However, if the control and render machines are the same, this results in an ugly little SDL window on top of your display.
Edit To Clarify:
The renderer has an output window, in its normal use case, that window is full screen, except when they are both running on the same computer, just so it is possible to give the controller focus. There can actually be multiple renderers displaying a different view of the same data on different computers all controlled by the same controller, hence the total decoupling of the input from the output (Making taking advantage of the built in X11 client/server stuff for display less useable) Also, multiple controller applications for one renderer is also possible. Communication between the controllers and renderers is via sockets.

OK, if you're under X11 and you want to get the kbd, you need to do a grab.
If you're not, my only good answer is ncurses from a terminal.
Here's how you grab everything from the keyboard and release again:
/* Demo code, needs more error checking, compile
* with "gcc nameofthisfile.c -lX11".
/* weird formatting for markdown follows. argh! */
#include <X11/Xlib.h>
int main(int argc, char **argv)
{
Display *dpy;
XEvent ev;
char *s;
unsigned int kc;
int quit = 0;
if (NULL==(dpy=XOpenDisplay(NULL))) {
perror(argv[0]);
exit(1);
}
/*
* You might want to warp the pointer to somewhere that you know
* is not associated with anything that will drain events.
* (void)XWarpPointer(dpy, None, DefaultRootWindow(dpy), 0, 0, 0, 0, x, y);
*/
XGrabKeyboard(dpy, DefaultRootWindow(dpy),
True, GrabModeAsync, GrabModeAsync, CurrentTime);
printf("KEYBOARD GRABBED! Hit 'q' to quit!\n"
"If this job is killed or you get stuck, use Ctrl-Alt-F1\n"
"to switch to a console (if possible) and run something that\n"
"ungrabs the keyboard.\n");
/* A very simple event loop: start at "man XEvent" for more info. */
/* Also see "apropos XGrab" for various ways to lock down access to
* certain types of info. coming out of or going into the server */
for (;!quit;) {
XNextEvent(dpy, &ev);
switch (ev.type) {
case KeyPress:
kc = ((XKeyPressedEvent*)&ev)->keycode;
s = XKeysymToString(XKeycodeToKeysym(dpy, kc, 0));
/* s is NULL or a static no-touchy return string. */
if (s) printf("KEY:%s\n", s);
if (!strcmp(s, "q")) quit=~0;
break;
case Expose:
/* Often, it's a good idea to drain residual exposes to
* avoid visiting Blinky's Fun Club. */
while (XCheckTypedEvent(dpy, Expose, &ev)) /* empty body */ ;
break;
case ButtonPress:
case ButtonRelease:
case KeyRelease:
case MotionNotify:
case ConfigureNotify:
default:
break;
}
}
XUngrabKeyboard(dpy, CurrentTime);
if (XCloseDisplay(dpy)) {
perror(argv[0]);
exit(1);
}
return 0;
}
Run this from a terminal and all kbd events should hit it. I'm testing it under Xorg
but it uses venerable, stable Xlib mechanisms.
Hope this helps.
BE CAREFUL with grabs under X. When you're new to them, sometimes it's a good
idea to start a time delay process that will ungrab the server when you're
testing code and let it sit and run and ungrab every couple of minutes.
It saves having to kill or switch away from the server to externally reset state.
From here, I'll leave it to you to decide how to multiplex renderes. Read
the XGrabKeyboard docs and XEvent docs to get started.
If you have small windows exposed at the screen corners, you could jam
the pointer into one corner to select a controller. XWarpPointer can
shove the pointer to one of them as well from code.
One more point: you can grab the pointer as well, and other resources. If you had one controller running on the box in front of which you sit, you could use keyboard and mouse input to switch it between open sockets with different renderers. You shouldn't need to resize the output window to less than full screen anymore with this approach, ever. With more work, you could actually drop alpha-blended overlays on top using the SHAPE and COMPOSITE extensions to get a nice overlay feature in response to user input (which might count as gilding the lily).

For the mouse you can use GPM.
I'm not sure off the top of my head for keyboard or joystick.
It probably wouldn't be too bad to read directly off there /dev files if need be.
Hope it helps

Related

USB Serial port programming has "disastrous" results

I am currently working on a C program running on a Raspberry Pi 3 (Linux Ubuntu) that is intended to provide a web page interface for configuring networking on an embedded system.
The code is being developed using Code::Blocks with the GDB debugger. I'm using microhttpd for the web server and that, plus the various web pages, are all working great. I'm now working on the USB Serial link to the embedded system using information in "Serial Programming Guide for POSIX Operating Systems".
The code below is responsible for opening the USB Serial link to the target system and seems to work fine - once. If I close the program and restart it (either standalone on the command line or from within Code::Blocks) the second time microhttpd is hosed - browser windows will no longer connect. Further, from within Code::Blocks the debugger is also hosed - once the program is started it cannot be paused or stopped. The only way is to kill it by closing the project.
The problem is clearly within the function since I can comment out the call to it and everything works as it did previously. Unfortunately, once the problem happens the only solution seems to be to reboot the Pi.
I've done things like this before using a scripting language (Tcl) but this time around I'm looking for a performance boost from a non-interpreted language since the Pi will also be running a high bandwidth data logging program through a similar USB serial interface.
The code is shown below:
/******************************************************************************/
/* This function scans through the list of USB Serial ports and tries to */
/* establish communication with the target system. */
/******************************************************************************/
void tapCommInit(void) {
char line[128];
char port[15]; // this is always of the form "/dev/TTYACMn"
char *ptr;
FILE *ifd;
struct termios options;
uint8_t msgOut[3], msgIn[4];
msgOut[0] = REQ_ID; // now prepare the message to send
msgOut[1] = 0; // no data so length is zero
msgOut[2] = 0;
/**************************************************************************/
/* First, get the list of USB Serial ports. */
/**************************************************************************/
system("ls -l /dev/serial/by-path > usbSerial\n"); // get current port list
ifd = fopen("usbSerial", "r");
logIt(fprintf(lfd, "serial ports: \n"));
/**************************************************************************/
/* The main loop iterates through the file looking for lines containing */
/* "tty" which should be a valid USB Serial port. The port is configured */
/* in raw mode as 8N1 and an ID request command is sent, which has no */
/* data. If a response is received it's checked to see if the returned */
/* ID is a match. If not, the port is closed and we keep looking. If a */
/* match is found, tapState is set to "UP" and the function returns. If */
/* no match is found, tapState is left in the initial "DOWN" state. */
/**************************************************************************/
while(1) {
if (fgets(line, 127, ifd) == NULL) { // end of file?
break; // yes - break out and return
}
ptr = strstr(line, "tty"); // make sure the line contains a valid entry
if (ptr == NULL) {
continue; // nothing to process on this line
}
strcpy(port, "/dev/"); // create a correct pathname
strcat(port, ptr); // append the "ttyACMn" part of the line
port[strlen(port)-1] = 0; // the last character is a newline - remove it
logIt(fprintf(lfd," %s\n", port)); // we have a port to process now
cfd = open(port, O_RDWR | O_NOCTTY | O_NDELAY); // cfd is a global int
if (cfd == -1) {
logIt(fprintf(lfd, "Could not open port: %s\n", port));
continue; // keep going with the next one (if any)
}
fcntl(cfd, F_SETFL, 0); // blocking mode
tcgetattr(cfd, &options); // get the current port settings
options.c_cflag |= (CLOCAL | CREAD); // ena receiver, ignore modem lines
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // raw, no echo
options.c_oflag &= ~OPOST; // no special output processing
options.c_cc[VMIN] = 0; // minimum number of raw read characters
options.c_cc[VTIME] = 10; // timeout in deciseconds (1 second timeout)
tcsetattr(cfd, TCSANOW, &options); // set options right now
cfsetispeed(&options, B115200); // input baud rate
cfsetospeed(&options, B115200); // output baud rate
options.c_cflag &= ~(CSIZE | PARENB | // clear size bits, no parity
CSTOPB | CRTSCTS); // 1 stop bit, no hw flow control
options.c_cflag |= CS8; // now set size: 8-bit characters
options.c_cflag &= ~(IXON | IXOFF | IXANY); // no sw flow control
if (write(cfd, msgOut, 3) < 3) {
logIt(fprintf(lfd, "Sending of output message failed\n"));
close(cfd);
continue;
}
if (read(cfd, msgIn, 4) != 4) {
logIt(fprintf(lfd, "Didn't get expected amount of return data\n"));
close(cfd);
continue;
}
if (msgIn[3] != HOST_ID) {
logIt(fprintf(lfd, "Got the wrong HOST_ID response\n"));
close(cfd);
continue;
}
logIt(fprintf(lfd, "Port found - communication established\n"));
tapState = UP;
break; // we're done - break out of the loop
}
fclose(ifd); // close and remove the file we created
remove("usbSerial");
}
from within Code::Blocks the debugger is also hosed - once the program is started it cannot be paused or stopped
It is far more likely that you do not understand your tools than that you have created an unkillable program.
It's easy enough to figure this out: divide and conquer. You've got a whole pile of unrelated components here. Start separating them and find out which pieces work fine in isolation and which continue to behave badly when disconnected from everything else. Then you'll have your culprit.
Specifically here, that means try running your program outside the IDE, then under command line gdb instead of GDB via the IDE.
Also, it should be possible to run your program without starting the web server piece, so that you can run the serial part of the app in isolation. This is not only good for debugging by minimizing confounding variables, it also encourages a loosely-coupled program design, which is a good thing in its own right.
In the end, you may find that the thing keeping your program from stopping is the web framework, Code::Blocks, or the way GDB operates on the Pi under Code::Blocks, rather than anything to do with the USB to serial adapter.
once the problem happens the only solution seems to be to reboot the Pi
If your program is still running in the background, then of course your next instance will fail if it tries to open the same USB port.
Don't guess, find out:
$ sudo lsof | grep ttyACM
or:
$ lsof -p $(pidof myprogram)
(Substitute pgrep if your system doesn't have pidof.)
I've done things like this before using a scripting language (Tcl) but this time around I'm looking for a performance boost from a non-interpreted language
Your serial port is running at 115,200 bps. Divide that by 10 to account for the stop and start bits, then flip the fraction to get seconds per byte, and you come to 87 microseconds per byte. And you only achieve that when the serial port is running flat-out, sending or receiving 11,500 bytes per second. Wanna take a guess at how many lines of code Tcl can interpret in 87 microseconds? Tcl isn't super-fast, but 87 microseconds is an eternity even in Tcl land.
Then on the other side of the connection, you have HTTP and a [W]LAN, likely adding another hundred milliseconds or so of delay per transaction.
Your need for speed is an illusion.
Now come back and talk to me again when you need to talk to 100 of these asynchronously, and then maybe we can start to justify C over Tcl.
(And I say this as one whose day job involves maintaining a large C++ program that does a lot of serial and network I/O.)
Now lets get to the many problems with this code:
system("ls -l /dev/serial/by-path > usbSerial\n"); // get current port list
ifd = fopen("usbSerial", "r");
Don't use a temporary where a pipe will suffice; use popen() here instead.
while(1) {
This is simply wrong. Say while (!feof(ifd)) { here, else you will attempt to read past the end of the file.
This, plus the next error, is likely the key to your major symptoms.
if (fgets(line, 127, ifd) == NULL) {
break;
There are several problems here:
You're assuming things about the meaning of the return value that do not follow from the documentation. The Linux fopen(3) man page isn't super clear on this; the BSD version is better:
The fgets() and gets() functions do not distinguish between end-of-file and error, and callers must use feof(3) and ferror(3) to determine which occurred.
Because fgets() is Standard C, and not Linux- or BSD-specific, it is generally safe to consult other systems' manual pages. Even better, consult a good generic C reference, such as Harbison & Steele. (I found that much more useful than K&R back when I was doing more pure C than C++.)
Bottom line, simply checking for NULL doesn't tell you everything you need to know here.
Secondarily, the hard-coded 127 constant is a code bomb waiting to go off, should you ever shrink the size of the line buffer. Say sizeof(line) here.
(No, not sizeof(line) - 1: fgets() leaves space for the trailing null character when reading. Again, RTFM carefully.)
The break is also a problem, but we'll have to get further down in the code to see why.
Moving on:
strcat(port, ptr); // append the "ttyACMn" part of the line
Two problems here:
You're blindly assuming that strlen(ptr) <= sizeof(port) - 6. Use strncat(3) instead.
(The prior line's strcpy() (as opposed to strncpy()) is justifiable because you're copying a string literal, so you can see that you're not overrunning the buffer, but you should get into the habit of pretending that the old C string functions that don't check lengths don't even exist. Some compilers will actually issue warnings when you use them, if you crank the warning level up.)
Or, better, give up on C strings, and start using std::string. I can see that you're trying to stick to C, but there really are things in C++ that are worth using, even if you mostly use C. C++'s automatic memory management facilities (not just string, but also auto_ptr/unique_ptr and more) fall into this category.
Plus, C++ strings operate more like Tcl strings, so you'll probably be more comfortable with them.
Factual assertions in comments must always be true, or they are likely mislead you later, potentially hazardously so. Your particular USB to serial adapter may use /dev/ttyACMx, but not all do. There's another common USB device class used by some serial-to-USB adapters that causes them to show up under Linux as ttyUSBx. More generally, a future change may change the device name in some other way; you might port to BSD, for example, and now your USB to serial device is called /dev/cu.usbserial, blowing your 15-byte port buffer. Don't assume.
Even with the BSD case aside, your port buffer should not be smaller than your line buffer, since you are concatenating the latter onto the former. At minimum, sizeof(port) should be sizeof(line) + strlen("/dev/"), just in case. If that seems excessive, it is only because 128 bytes for the line buffer is unnecessarily large. (Not that I'm trying to twist your arm to change it. RAM is cheap; programmer debugging time is expensive.)
Next:
fcntl(cfd, F_SETFL, 0); // blocking mode
File handles are blocking by default in Unix. You have to ask for a nonblocking file handle. Anyway, blasting all the flags is bad style; you don't know what other flags you're changing here. Proper style is to get, modify, then set, much like the way you're doing with tcsetattr():
int flags;
fcntl(cfd, F_GETFL, &flags);
flags &= ~O_NONBLOCK;
fcntl(cfd, F_SETFL, flags);
Well, you're kind of using tcsetattr() correctly:
tcsetattr(cfd, TCSANOW, &options);
...followed by further modifications to options without a second call to tcsetattr(). Oops!
You weren't under the impression that modifications to the options structure affect the serial port immediately, were you?
if (write(cfd, msgOut, 3) < 3) {
logIt(fprintf(lfd, "Sending of output message failed\n"));
close(cfd);
continue;
}
Piles of wrong here:
You're collapsing the short-write and error cases. Handle them separately:
int bytes = write(cfd, msgOut, 3);
if (bytes == 0) {
// can't happen with USB, but you may later change to a
// serial-to-Ethernet bridge (e.g. Digi One SP), and then
// it *can* happen under TCP.
//
// complain, close, etc.
}
else if (bytes < 0) {
// plain failure case; could collapse this with the == 0 case
// close, etc
}
else if (bytes < 3) {
// short write case
}
else {
// success case
}
You aren't logging errno or its string equivalent, so when (!) you get an error, you won't know which error:
logIt(fprintf(lfd, "Sending of output message failed: %s (code %d)\n",
strerror(errno), errno));
Modify to taste. Just realize that write(2), like most other Unix system calls, has a whole bunch of possible error codes. You probably don't want to handle all of them the same way. (e.g. EINTR)
After closing the FD, you're leaving it set to a valid FD value, so that on EOF after reading one line, you leave the function with a valid but closed FD value! (This is the problem with break above: it can implicitly return a closed FD to its caller.) Say cfd = -1 after every close(cfd) call.
Everything written above about write() also applies to the following read() call, but also:
if (read(cfd, msgIn, 4) != 4) {
There's nothing in POSIX that tells you that if the serial device sends 4 bytes that you will get all 4 bytes in a single read(), even with a blocking FD. You are especially unlikely to get more than one byte per read() with slow serial ports, simply because your program is lightning fast compared to the serial port. You need to call read() in a loop here, exiting only on error or completion.
And just in case it isn't obvious:
remove("usbSerial");
You don't need that if you switch to popen() above. Don't scatter temporary working files around the file system where a pipe will do.

X11 FocusIn is not working

As long as I understand it, the X11 FocusIn event is triggered whenever the window comes into focused. It the the window that the keyboard input is sent to. I am having trouble triggering this event. I have made sure to give it the FocusChangeMask when creating the window. I have created a breakpoint inside my event handler where the FocusIn event is supposed to happen and it is not stopping.
I have 2 separate window, a transparent one and one non-transparent. Currently I have it so the transparent window is always on top of the non transparent window. Whenever I switch focus and then switch back to the transparent window, the non-transparent window is directly underneath. This causes other windows to be stuck in 'between' the transparent and non-transparent window.
I have noticed that whenever I focus on the non-transparent window that is underneath that triggers the FocusIn event. I can not get the transparent window to trigger the event. Does this have something to do with the window being in 32-bit color?
What am I missing?
while(!renderer->stop)
{
XNextEvent(renderer->x_display, &event);
switch(event.type)
{
case Expose:
if (event.xexpose.window == gstreamer_window)
{
XRaiseWindow(renderer->x_display, renderer->opengl_window);
}
break;
case FocusIn:
if (event.xfocus.window == renderer->opengl_window)
{
XRaiseWindow(renderer->x_display, gstreamer_window);
}
break;
case ConfigureNotify:
if (event.xconfigure.window == renderer->opengl_window)
{
XMoveWindow(renderer->x_display, gstreamer_window,
event.xconfigure.x, event.xconfigure.y - top_border_offset);
}
break;
}
}
Here is how I created the window.
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask | FocusChangeMask;
swa.colormap = XCreateColormap(x_display, XDefaultRootWindow(x_display), visual, AllocNone);
swa.background_pixel = 0;
swa.border_pixel = 0;
/* Create a window */
opengl_window = XCreateWindow (
x_display, parent,
0, 0, m_plane_width, m_plane_height, 0,
depth, InputOutput,
visual, CWEventMask | CWBackPixel | CWColormap | CWBorderPixel,
&swa );
It appears as though I set the FocusChangeMask at the wrong place. By adding the line XSelectInput(x_display, opengl_window, FocusChangeMask)
, it now triggers the FocusIn event. It was triggering the other display because it had the mask but this one didn't.
It's not clear from your question whether the windows referenced in your question are top level application windows, or secondary, ancillary windows that are child windows of your top level application windows.
But in all cases, proper input focus handling requires you to inform the window manager exactly how your application expects to handle input focus.
See section 4.1.7 of the ICCCM specification for more information. Unfortunately, it's not enough just to code an event loop that handles FocusIn messages, and expect them to fall out of the sky. First, you need to tell your window manager how exactly you are going to handle input focus switching, then respond to the window manager's messages, as explained in the ICCCM specification; perhaps by explicitly sending the SetInputFocus requests for your own application windows.
See my post under OP's self accepted answer for background
I had the same problem and it seems if you use XSelectInput it overrides the event mask for a window, instead of adding to it. You need to either:
skip adding XSelectInput and set the event masks on the window when created
or add all of the masks for the window to the XSelectInput command
Here's my similar setup which worked before I added the XSelectInput line:
swa.event_mask = StructureNotifyMask | SubstructureNotifyMask | FocusChangeMask | ExposureMask | KeyPressMask;
wnd = XCreateWindow(disp, wndRoot, left, top, width, height, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
XMapWindow(disp, wnd);
When I added this for capturing key input:
XSelectInput(disp, wnd, KeyPressMask | KeyReleaseMask);
everything else stopped working, which I didn't realize for a while, until now, because I was just testing keys.
I have a single window application with no controls in the client area, so I don't know what else XSelectInput does for a window that already has the masks, but I do get key up and down messages without it. In the OP's self accepted answer he just goes the other way and adds 'FocusChangeMask' to XSelectInput instead.
The documentation for a lot of X11 is, in my opinion, a lot like the standard Apple documentation where it has the affliction of never saying what the function really actually does or affects, but tells you all about everything else. (See bottom snippet here) To be fair a good portion of the Cocoa docs are pretty decent, but maybe half are not. That goes for many or most of the parameters as well.
From Linux man page which I think is the maybe verbatim the same as the Xlib manual here https://tronche.com/gui/x/xlib/
The XSelectInput function requests that the X server report the events associated with the specified event mask.
Setting the event-mask attribute of a window overrides any previous call for the same window but not for other clients.
(Below is my own perhaps incorrect rewording of the XInputSelect docs)
This can be interpreted as perhaps:
XSelectInput sets the event-mask on a window for a display for events reported to the client. Each client can set its own independent mask for events it wants to receive with the following exceptions (skipping the exceptions).
Now the critical part:
XInputSelect will override and replace for a client any previous event-mask settings for a window on a display, including any event-mask set when creating the window.
And what I wish it was written as to make sure its understood, if true, that the XInputSelect does nothing else like enable key to character interpretation and what not, because I believe the core confusion is that people from all the various examples out there get the impression that there is some other magic besides setting mask. I'm assuming there's not, and the docs speak of no other magic besides the mask.
XInputSelect sets the event-mask for a window and allows for setting an event mask after a window is created.
And my opinion is that that line should have been first.

Clicking Sounds When Playing Clips in Rapid Succession

I have a very simple program that plays 4 different tones, depending on what button is pressed. I have found that if I play multiple tones or the same tone in rapid succession, there are unpleasant clicking noises produced. I have made sure that these clicks are not present in my audio samples; it is definitely caused by playing the clips quickly one after another.
After googling around, I'm fairly sure that the clicks are due to the rapid change in pitch between clips. Looking at the waveform of the playback from the offending audio, it looks like a clip is first cancelled for a fraction of a second before starting the next clip. I have highlighted the section where this seems particularly obvious.
The clip that showcases these audio clicks can also be downloaded here.
My code is very simple. I am using XInput to read input from a connected controller, which determines the tone to play, and I am using WinMM to output sound from wav files. It is written in the D programming language, but I have modified it to use no D-specific features to make it as C-like as possible and to avoid confusion.
SHORT keyPressed(int vkey)
{
enum highBit { val = 0x8000 }
return cast(SHORT)(GetKeyState(vkey) & highBit.val);
}
enum Button
{
DPAD_UP = 0x0001,
DPAD_DOWN = 0x0002,
DPAD_LEFT = 0x0004,
DPAD_RIGHT = 0x0008,
START = 0x0010,
BACK = 0x0020,
LEFT_THUMB = 0x0040,
RIGHT_THUMB = 0x0080,
LEFT_SHOULDER = 0x0100,
RIGHT_SHOULDER = 0x0200,
A = 0x1000,
B = 0x2000,
X = 0x4000,
Y = 0x8000,
}
struct XINPUT_GAMEPAD
{
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
}
struct XINPUT_STATE
{
DWORD dwPacketNumber;
XINPUT_GAMEPAD Gamepad;
bool isPressed(int button)
{
return cast(bool)(Gamepad.wButtons & button);
}
}
int main()
{
HANDLE xinputDLL = initXinput();
XINPUT_STATE oldState;
XINPUT_STATE newState;
while (!keyPressed(VK_ESCAPE))
{
oldState = newState;
XInputGetState(0, &newState);
enum flags { val = SND_ASYNC | SND_FILENAME | SND_NODEFAULT }
if (newState.isPressed(Button.A) && !oldState.isPressed(Button.A))
{
PlaySoundA(toStringz("Piano.ff.A4.wav"), null, flags.val);
}
if (newState.isPressed(Button.B) && !oldState.isPressed(Button.B))
{
PlaySoundA(toStringz("Piano.ff.B4.wav"), null, flags.val);
}
if (newState.isPressed(Button.X) && !oldState.isPressed(Button.X))
{
PlaySoundA(toStringz("Piano.ff.C5.wav"), null, flags.val);
}
if (newState.isPressed(Button.Y) && !oldState.isPressed(Button.Y))
{
PlaySoundA(toStringz("Piano.ff.F4.wav"), null, flags.val);
}
}
denitXinput(xinputDLL);
return 0;
}
Assuming that I'm correct in regards to the source of the clicking sounds, I think the solution is to have each sample fade into the next one. However, I am not sure how to do this as the WinMM documentation seems relatively sparse, and I am inexperienced with it.
Is the solution to my problem of clicks when playing audio samples to have each sample fade into the next one? If so, how can I accomplish this using WinMM? If not, is there another solution that I can try?
I know how we can solve this in theory, but I don't have actual working code yet for all cases. (When I do, I'll edit this.)
First, the simple case which kinda works: instead of using PlaySound, try mciSendStringA:
if(auto err = mciSendStringA("play test.wav", null, 0, null))
writeln(err);
I am not making that up, Windows actually has that function, and it actually works with a lot of little command strings and file formats (though if your program terminates, all sound stops, so make sure the program keeps running e.g. stay in your controller loop or call Sleep(something)).
I've used a lot of Win32 and sometimes I'm amazed by how much stuff it has. Prototype:
extern(Windows) uint mciSendStringA(in char*,char*,uint,void*);
found in winmm.lib.
That basically works, but in my test, playing the same file twice at the same time has no effect. Playing different files together mixes them though. So it is a partial solution.
Next step from that would be to use the mciSendCommand function - a bit lower level than send string, so you can open multiple devices and try to get more overlap that way:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd743675%28v=vs.85%29.aspx
I haven't tried this yet, but it looks fairly simple and I suspect it might be good enough for you. Open up a few devices for each button so you can hit them a few times fast and it cycles through them, hopefully mixing the same sound more than once when needed.
The prototype to that is:
extern(Windows) uint /*MCIERROR*/ mciSendCommandA(MCIDEVICEID,UINT,DWORD,DWORD);
Yes, it casts to void* then to DWORD in the msdn example. Blargh. Relevant structs:
struct MCI_OPEN_PARMSA {
DWORD dwCallback;
MCIDEVICEID wDeviceID; // aka uint
LPCSTR lpstrDeviceType;
LPCSTR lpstrElementName;
LPCSTR lpstrAlias;
}
struct MCI_PLAY_PARMS {
DWORD dwCallback;
DWORD dwFrom;
DWORD dwTo;
}
and you can borrow some constants from here too:
https://github.com/AndrejMitrovic/DWinProgramming/blob/master/WindowsAPI/win32/mmsystem.d#L693
(if you are already using the win32 bindings, great! But I think they are kinda a pain for little things so I try to avoid them, preferring to copy/paste prototypes+structs+constants off MSDN as I need them.)
You should be able to get the MSDN example working with those definitions and core.sys.windows.windows. Don't forget pragma(lib, "winmm"); too.
I think a full solution that will certainly work, but is also quite a bit harder, will be using the low level interface to mix the sounds yourself as they happen and send that result to the device. I don't have this working yet and I'm out of time today, but hopefully I can get something to you tomorrow.
The basic steps are:
1) call waveOutOpen to get a device. Set up a callback function which it calls when it needs more data.
2) prepare a buffer - or perhaps more than one - with waveOutPrepareHeader
3) feed data with waveOutWrite when requested by your callback (might want this in a separate thread) with the current notes. Mixing two samples is simply a case of adding the values together (and clipping if they overflow - sounds awful btw but hopefully that won't actually happen) so if you are doing more than one sound, just add them as you go.
Don't forget extern(Windows) on any callback function!
4) Loading your samples probably means reading the .wav file. That's not super hard, Windows has helper functions or you can do it yourself. I'll show code for this too.
What I have so far is in my simpleaudio.d https://github.com/adamdruppe/arsd/blob/master/simpleaudio.d find struct AudioOutput and the WinMM version. It has a horrible API right now that must be radically changed - it was acceptable on Linux but sucks on Windows. A callback feeder instead of write(data) should work better on both platforms, so that's what I'll do.
Problem I'm having with the demo right now is gaps between buffers... leading to clicky sounds. Yeah. But I'm sure it is just latency that should be solved with the proper callback approach and buffer sizing.
That MCI function might work for you as a next step though, maybe even a final step if the multiple devices works.
BTW: you could also prolly make it do MIDI commands instead of playing wavs and get all kinds of cool stuff. Simpleaudio.d's low level midi is already functioning - the demo main even shows a piano scale. Rigging it into the xbox controller shouldn't be too hard... note on when the button is pressed, note off when released, and not even think about timing.. Not really an answer to the question but a cool thing to play with in the same vein!

How to monitor keyboard events from X11

I know there has been a few of these, but a lot of the answers always to have a lot of buts, ifs, and you shouldn't do that.
What I'm trying to do is have a background program that can monitor the keyboard events from X11. This is on an embedded device, and it will have a main app basically running in something like a kiosk mode. We want to have a background app that manages a few things, and probably a back doors hook. But this app generally will not have focus.
I can't use the main app, because its partially there for a fail safe if the main app ever fails, or to do some dev type things to bypass the main app.
The best question I found is a few years old, so I'm not sure how up to date it is. This was extremely easy to do with windows.
X KeyPress/Release events capturing irrespective of Window in focus
The correct way for doing that is using Xlib. Using this library you can write code like this:
while (1) {
XNextEvent(display, &report);
switch (report.type) {
case KeyPress:
if (XLookupKeysym(&report.xkey, 0) == XK_space) {
fprintf (stdout, "The space bar was pressed.\n");
}
break;
}
}
// This event loop is rather simple. It only checks for an expose event.
// XNextEvent waits for an event to occur. You can use other methods to get events,
// which are documented in the manual page for XNextEvent.
// Now you will learn how to check if an event is a certain key being pressed.
// The first step is to put case KeyPress: in your switch for report.type.
// Place it in a similar manner as case Expose.
Also you could use poll or select on the special device file that is mapped to your keyboard. In my case is /dev/input/event1.
If you have doubts about what's the special file mapped to your keyborad, read the file /var/log/Xorg.0.log (search for the word keyboard).
Here you have another link of interest: Linux keyboard event capturing /dev/inputX

Grabbing the X server with XGrabServer

In an embedded Linux system, I'm trying to show a shutdown notification that should override any other windows when shutting down. Now creating the window isn't a problem, but showing the window reliably is. The X server or WM is somehow too busy to draw the notification every time. Considering the limited CPU power we have, its not surprising.
So, I figured I will make it easy to the WM/X by grabbing the X server using gdk_x11_grab_server() (which calls XGrabServer on default display). But when should I call the grab func? If I call it before building my window, prior showing my window or event in expose-event of my window, nothing is drawn to the screen (even in no-load test)!
The documentation says:
The XGrabServer function disables
processing of requests and close downs
on all other connections than the one
this request arrived on.
I suppose that would mean that only requests from my app should be processed, but it seems that is not the case, since nothing is drawn if X is grabbed by my app.
So, how and when should grabbing the X server be used to achieve wanted outcome, or is it totally a wrong tool and I've misunderstood the use (or trying to use it too high level for it to work really).
I'd guess that nothing is being drawn because you're opening a normal top level window, in which case the window manager needs to operate on it before it'll be visible; however you've locked out the window manager by calling XGrabServer().
You could try setting OverrideRedirect on the window, which tells the X server that the window manager shouldn't be involved with this window at all. This also has the effect of removing any decorations (title bar, close button, etc) from the window, which could well be what you want for a shutdown notification.
You may need to follow the call with XSync/XFlush.
Shouldn't you follow up with a call to XUngrabServer so that the X server resumes processing requests? All other connections have already been closed because you called XGrabServer, but you obviously need request handling to resume because you want to make requests on your connection.
There seems to be some confusing as to what XGrabServer actually does precisely, and not just on this question. The man pages are pretty ambiguous about this. We can easily verify though that the server indeed still processes requests from our connection:
// cc grab-test.c `pkg-config --cflags --libs x11`
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <X11/Xlib.h>
int main(void)
{
// Initialize
Display* dpy = XOpenDisplay(NULL);
assert(dpy != NULL);
int s = DefaultScreen(dpy);
// Grab the Server
XGrabServer(dpy); // XFlush(dpy);
// Create some stuffs
Window win = XCreateSimpleWindow(dpy, RootWindow(dpy, s), 10, 10, 640, 480, 1, BlackPixel(dpy, s), WhitePixel(dpy, s));
XSelectInput(dpy, win, ExposureMask);
XMapWindow(dpy, win);
XStoreName(dpy, win, "An X11 window");
// Notice we can query the attributes BEFORE ungrabbing the server
XWindowAttributes wa;
XGetWindowAttributes(dpy, win, &wa);
int width = wa.width;
int height = wa.height;
printf("Current window size: %dx%d\n", width, height);
sleep(2); // Give some time to see the window
XDestroyWindow(dpy, win); XFlush(dpy);
sleep(2); // Give some time to NOT see the window
// Ungrab the server
XUngrabServer(dpy); // XFlush(dpy);
// Shutdown
XCloseDisplay(dpy);
return 0;
}
There is probably a window manager or composite manager preventing the window from being displayed, if I run the above program in Xephyr (without a window manager or composite manager), I can see a white window for 2 seconds followed by a black screen for 2 seconds, after which all other windows are redrawn again.

Resources