#include #include #include #include #include #include #include #include #include int open_events( int ep ); bool handle_fd( int fd ); void handle_message( struct input_event *ev ); ssize_t file_get_contents( const char *filename, char *buffer, size_t len ); ssize_t file_put_contents( const char *filename, const char *buffer, size_t len ); int _min, _max, _step; const char *_brightness_file; bool _all; int main( int argc, char **argv ) { int steps, ep, num, valid; struct epoll_event events[10]; // List all devices by name if ( argc > 1 && strcmp( "-l", argv[1] ) == 0 ) { open_events( -1 ); return 0; } // Listen to all devices if ( argc > 1 && strcmp( "-a", argv[1] ) == 0 ) { _all = true; argc--; argv++; } if ( argc < 5 ) { fprintf( stderr, "%s \n", argv[0] ); return 1; } // Sanity checks, calculate step size _brightness_file = argv[1]; _min = atoi( argv[2] ); _max = atoi( argv[3] ); steps = atoi( argv[4] ); if ( _max <= _min || steps < 1 ) { fprintf( stderr, "Error: min(%d) >= max(%d), or steps(%d) < 1\n", _min, _max, steps ); return 1; } _step = ( _max - _min ) / steps; if ( _step < 1 ) { _step = 1; } ep = epoll_create( 100 ); if ( ep == -1 ) { perror( "Cannot epoll_create" ); return 1; } // Scan all input-event devices, open all that potentially have brightness keys // As of now, we match only one so epoll is pointless, but I only checked two // laptop models, so maybe there are different ways out there. Let's be prepared. valid = open_events( ep ); if ( valid < 1 ) { fprintf( stderr, "Nothing to wait for, exiting\n" ); return 0; } printf( "Waiting...\n" ); while ( ( num = epoll_wait( ep, events, 10, -1 ) ) > 0 || ( num == -1 && errno == EINTR ) ) { for ( int i = 0; i < num; ++i ) { if ( !handle_fd( events[i].data.fd ) ) { // Try to remove if ( epoll_ctl( ep, EPOLL_CTL_DEL, events[i].data.fd, NULL ) == 0 ) { valid--; close( events[i].data.fd ); } } } } if ( num == -1 ) { perror( "epoll error" ); return 1; } fprintf( stderr, "Unexpected loop exit\n" ); return 0; } /** * Open all /dev/input/eventX, check the name against known ones with * brightness controls. * Pass ep == -1 to just print all the device names. */ int open_events( int ep ) { int opened = 0; int fails = 0; int h, len; char fn[200]; for ( int id = 0 ;; ++id ) { if ( snprintf( fn, sizeof(fn), "/dev/input/event%d", id ) == -1 ) { perror( "snprintf broken" ); break; } // Open and see if success h = open( fn, O_RDONLY ); if ( h == -1 ) { if ( errno == ENOENT ) { // Not found - potentially no more devices, but keep going for a bit, since for example // unplugging a USB device would leave a gap in the numbering. if ( ++fails < 10 ) continue; } else { perror( "Cannot open eventX" ); } break; } // Success, inspect if ( ep == -1 ) { printf( "Opened %s: ", fn ); } fails = 0; len = ioctl( h, EVIOCGNAME( sizeof(fn) ), fn ); if ( len <= 0 ) { perror( "Could not get device name" ); close( h ); continue; } if ( ep == -1 ) { printf( "%.*s\n", len, fn ); close( h ); continue; } if ( !_all && strcmp( "Video Bus", fn ) != 0 ) { printf( "Ignoring '%s'\n", fn ); close( h ); continue; } printf( "Listening on '%s'\n", fn ); // Make nonblocking, add to epoll set fcntl( h, F_SETFL, O_NONBLOCK ); struct epoll_event ee = { .events = EPOLLIN, .data.fd = h, }; if ( epoll_ctl( ep, EPOLL_CTL_ADD, h, &ee ) == -1 ) { perror( "Cannot add eventX fd to epoll set" ); close( h ); } else { opened++; } } return opened; } /** * Read events from fd. */ bool handle_fd( int fd ) { int num; int done = 0; struct input_event buf; while ( ( num = read( fd, ( (char*)&buf ) + done, sizeof(buf) - done ) ) > 0 || ( num == -1 && errno == EINTR ) ) { if ( num + done == sizeof(buf) ) { handle_message( &buf ); if ( done != 0 ) { done = 0; // Switch to nonblocking again fcntl( fd, F_SETFL, O_NONBLOCK ); } } else { printf( "WARNING: PARTIAL READ\n" ); // Switch to blocking so we can finish the message fcntl( fd, F_SETFL, 0 ); done += num; } } if ( num == -1 && errno != EAGAIN ) { perror( "Error reading event" ); return false; } return true; } /** * Gets passed read events in input_event struct, * check and handle brightness keys. */ void handle_message( struct input_event *ev ) { ssize_t len; int val, newval; char buf[100]; //printf( "Type: %d, Key %d, Value %d\n", (int)ev->type, (int)ev->code, (int)ev->value ); if ( ev->type != EV_KEY ) return; if ( ev->value != 1 && ev->value != 2 ) return; // Down or Repeat only if ( ev->code != KEY_BRIGHTNESSDOWN && ev->code != KEY_BRIGHTNESSUP ) return; // Match our keys // Get current brightness len = file_get_contents( _brightness_file, buf, sizeof(buf) ); if ( len <= 0 ) { fprintf( stderr, "Cannot read brightness\n" ); return; } // Calculate new brightness val = atoi( buf ); newval = val + _step * ( ev->code == KEY_BRIGHTNESSUP ? 1 : -1 ); if ( newval < _min ) { newval = _min; } else if ( newval > _max ) { newval = _max; } if ( val == newval ) return; // Set new brightness len = snprintf( buf, sizeof(buf), "%d", newval ); if ( len == -1 ) { perror( "Brightness snprintf fail" ); } else if ( len == 0 ) { fprintf( stderr, "Brightness snprintf returned zero\n" ); } else { file_put_contents( _brightness_file, buf, (size_t)len ); } } /** * Read file contents into buffer. */ ssize_t file_get_contents( const char *filename, char *buffer, size_t len ) { ssize_t ret; size_t done = 0; ssize_t bytes_read = -1; int fd; if ( len <= 1 ) { if ( len == 1 ) { buffer[0] = '\0'; } return 0; } len--; fd = open( filename, O_RDONLY ); if ( fd == -1 ) { fprintf( stderr, "%s: ", filename ); perror( "Error opening file" ); return -1; } while ( done < len && ( bytes_read = read( fd, buffer + done, len - done ) ) > 0 ) { done += bytes_read; } if ( bytes_read == -1 ) { fprintf( stderr, "%s: ", filename ); perror( "Error reading file" ); ret = -1; } else { ret = (ssize_t)done; buffer[done] = '\0'; } close( fd ); return ret; } /** * Write buffer contents to file. */ ssize_t file_put_contents( const char *filename, const char *buffer, size_t len ) { ssize_t ret; size_t done = 0; ssize_t bytes_written = -1; int fd = open( filename, O_WRONLY | O_CREAT | O_TRUNC, 0644 ); if ( fd == -1 ) { fprintf( stderr, "%s: ", filename ); perror( "Error opening file" ); return -1; } while ( done < len && ( bytes_written = write( fd, buffer + done, len - done ) ) > 0 ) { done += bytes_written; } if ( bytes_written == -1 ) { fprintf( stderr, "%s: ", filename ); perror( "Error writing file" ); ret = -1; } else { ret = (ssize_t)done; } close( fd ); return ret; }