É apenas que quando o stdout não é um terminal, a saída é armazenada em buffer.
E quando você pressiona Ctrl-C , esse buffer é perdido como / se ainda não foi escrito.
Você obtém o mesmo comportamento com qualquer coisa usando stdio . Tente por exemplo:
grep . > file
Insira algumas linhas não vazias e pressione Ctrl-C , e você verá que o arquivo está vazio.
Por outro lado, digite:
xinput test 10 > file
Digite o suficiente no teclado para que o buffer fique cheio (pelo menos 4k de saída), e você verá o tamanho do arquivo crescer em pedaços de 4k de cada vez.
Com grep , você pode digitar Ctrl-D para que grep seja encerrado normalmente após ter liberado seu buffer. Para xinput , não acho que exista essa opção.
Observe que, por padrão, stderr não é armazenado em buffer, o que explica por que você tem um comportamento diferente com fprintf(stderr)
Se, em xinput.c , você adicionar um signal(SIGINT, exit) , que diga xinput para sair normalmente quando receber SIGINT , verá que file não está mais vazio (supondo que não Não travar, pois chamar funções de biblioteca de manipuladores de sinais não é garantido como seguro: considere o que poderia acontecer se o sinal chegasse enquanto o printf estivesse gravando no buffer).
Se estiver disponível, você pode usar o comando stdbuf para alterar o comportamento do buffer stdio :
stdbuf -oL xinput test 10 > file
Existem muitas perguntas neste site que abrangem a desativação do tipo stdio buffering, onde você encontrará soluções ainda mais alternativas.