Challenge: compiling decade-old unmaintained code on modern system

Airclick is/was a USB RF remote for controlling software. It had mostly Mac and some Windows support for specific applications, but nothing for linux.

No problem, someone named Bryan Taylor made a cool airclick linux software that is far more functional: a daemon interprets combinations of single and double presses of the remote’s 5 buttons, with 62 possible input actions, and maps them with a config file to any sequence of keyboard keypresses.

Button combinations

From the README:

For the curious…

This 5 button remote supports many more than 5 button combinations, but just how
many more? We will assume that C(n, r) indicates the number of ways to choose
r elements from a group of n, or rather, the number of possible metabuttons when
you must press exactly r buttons. So, the answer follows:
C(5, 1) + C(5, 2) + C(5, 3) + C(5, 4) + C(5, 5)
5 + 10 + 10 + 5 + 1

This equals 31. However, you can single click or double click each of these
button combinations, so we will multiply the total by two.
31 * 2

There are 62 recognizable actions provided by this simple 5 button remote when
taking the ability to double click into account.

This means any software’s keyboard shortcuts could be triggered without having to explicitly support individual software. So neat and versatile.

It was great being able to control my music player (clementine) and volume from bed, or across the house. Fun for impressing techy housemates too.

Alas, the airclick software hasn’t been maintained in about a decade. It compiled fine on previous versions of Ubuntu, but now on Fedora 28 it no longer compiles. Presumably due to changing C++ standards.

Not being a C++ programmer it’s not easy to figure out if the errors would be easy to fix.

So, L1Techs, what are your thoughts?

Compile errors
$ make
make  all-recursive
make[1]: Entering directory '[DIR]/airclick-0.7.1'
Making all in src
make[2]: Entering directory '[DIR]/airclick-0.7.1/src'
g++ -DHAVE_CONFIG_H -I. -I..     -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.cpp
In file included from airclick.h:32,
                 from main.cpp:1:
logger.h:130:5: warning: ‘template<class> class std::auto_ptr’ is deprecated [-Wdeprecated-declarations]
     auto_ptr<string_stream_type> m_p_stream_buffer;
     ^~~~~~~~
In file included from /usr/include/c++/8/memory:80,
                 from logger.h:10,
                 from airclick.h:32,
                 from main.cpp:1:
/usr/include/c++/8/bits/unique_ptr.h:53:28: note: declared here
   template<typename> class auto_ptr;
                            ^~~~~~~~
In file included from airclick.h:32,
                 from main.cpp:1:
logger.h: In member function ‘virtual int logger_streambuf<char_type, traits_type>::sync()’:
logger.h:99:29: warning: ‘template<class> class std::auto_ptr’ is deprecated [-Wdeprecated-declarations]
         m_p_stream_buffer = auto_ptr<string_stream_type>(new string_stream_type);
                             ^~~~~~~~
In file included from /usr/include/c++/8/memory:80,
                 from logger.h:10,
                 from airclick.h:32,
                 from main.cpp:1:
/usr/include/c++/8/bits/unique_ptr.h:53:28: note: declared here
   template<typename> class auto_ptr;
                            ^~~~~~~~
In file included from airclick.h:32,
                 from main.cpp:1:
logger.h: In member function ‘virtual bool logger_basic_simple_rotation_policy<char_type, traits_type>::rotate(std::basic_ofstream<_CharT, _Traits>&, const string&, const string&, const string&)’:
logger.h:265:13: error: there are no arguments to ‘unlink’ that depend on a template parameter, so a declaration of ‘unlink’ must be available [-fpermissive]
             unlink(current_log.c_str());
             ^~~~~~
logger.h:265:13: note: (if you use ‘-fpermissive’, G++ will accept your code, but allowing the use of an undeclared name is deprecated)
main.cpp: In function ‘int main(int, char**)’:
main.cpp:369:19: error: ‘getpid’ was not declared in this scope
     pid_t mypid = getpid();
                   ^~~~~~
main.cpp:369:19: note: suggested alternative: ‘getpt’
     pid_t mypid = getpid();
                   ^~~~~~
                   getpt
main.cpp:415:25: error: ‘fork’ was not declared in this scope
         if ((proc_pid = fork()) > 0) {
                         ^~~~
main.cpp:415:25: note: suggested alternative: ‘flock’
         if ((proc_pid = fork()) > 0) {
                         ^~~~
                         flock
In file included from airclick.h:32,
                 from main.cpp:1:
logger.h: In instantiation of ‘bool logger_basic_simple_rotation_policy<char_type, traits_type>::rotate(std::basic_ofstream<_CharT, _Traits>&, const string&, const string&, const string&) [with char_type = char; traits_type = std::char_traits<char>; std::__cxx11::string = std::__cxx11::basic_string<char>]’:
logger.h:248:18:   required from here
logger.h:265:19: error: ‘unlink’ was not declared in this scope
             unlink(current_log.c_str());
             ~~~~~~^~~~~~~~~~~~~~~~~~~~~
logger.h:265:19: note: suggested alternative: ‘uint’
             unlink(current_log.c_str());
             ~~~~~~^~~~~~~~~~~~~~~~~~~~~
             uint
logger.h:293:35: error: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]
             char *lastdot = rindex(files[i].file.c_str(), '.');
                             ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
logger.h:309:19: error: ‘unlink’ was not declared in this scope
             unlink(files[findex++].file.c_str());
             ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
logger.h:309:19: note: suggested alternative: ‘uint’
             unlink(files[findex++].file.c_str());
             ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
             uint
make[2]: *** [Makefile:230: main.o] Error 1
make[2]: Leaving directory '[DIR]airclick-0.7.1/src'
make[1]: *** [Makefile:223: all-recursive] Error 1
make[1]: Leaving directory '[DIR]/airclick-0.7.1'
make: *** [Makefile:162: all] Error 2

You could try to force different C++ standard with -std=c++98 flag for g++ or build/get older version of gcc and all of it toolchain. (binutils and such)


Just noticed they use autotools, I never used them, so can’t really help ya. Too much. For the record, it doesn’t build on Arch too.

$ g++ --version
g++ (GCC) 8.1.1 20180531
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ uname -a 
Linux redarch 4.17.4-1-ARCH #1 SMP PREEMPT Tue Jul 3 15:45:09 UTC 2018 x86_64 GNU/Linux

Thanks for trying. Yeah I’m not familiar with autotools either.

1 Like

I compiled it but idk how to test it.


  1. ./configure CPPFLAGS='-std=gnu++98 -fpermissive'
  2. I added #include <unistd.h> to src/airclick.h (coz it uses fork() and errors that it’s not defined)

patch:

--- src/airclick.h	        2018-07-16 01:19:02.295754578 +0300
+++ src/airclick_unistd.h	2018-07-16 01:16:53.345682385 +0300
@@ -26,6 +26,7 @@
 #include <string>
 
 #include <wchar.h>
+#include <unistd.h>
 
 #include "ac_exception.h"
 #include "platform.h"

  1. then just make

There are still some warnings, so def needs testing.


I didn’t install it on the system. But the executable runs and exits with FATAL ERROR (in -lt debug trace mode)

1 Like

Awesome!

With your two changes, it compiles for me too and the only warning is:

logger.h:293:35: warning: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]
         char *lastdot = rindex(files[i].file.c_str(), '.');
                         ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~

Running it crashes for me too:

$ airclick -ld
terminate called after throwing an instance of 'logger_error<int>'
  what():  Couldn't find logger!
Aborted (core dumped)

Of course it’s hard for you to test it without the hardware!

1 Like

Sorry that I can’t help more from here. I must say that I am also not on good terms with C++ (mostly do C, sometimes C#) so there’s that.

Good luck!

1 Like