diff options
Diffstat (limited to 'core/modules/slx-brightness/src/main.c')
-rw-r--r-- | core/modules/slx-brightness/src/main.c | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/core/modules/slx-brightness/src/main.c b/core/modules/slx-brightness/src/main.c new file mode 100644 index 00000000..410154e0 --- /dev/null +++ b/core/modules/slx-brightness/src/main.c @@ -0,0 +1,318 @@ +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/epoll.h> +#include <stdio.h> +#include <linux/input.h> +#include <errno.h> +#include <stdlib.h> +#include <stdbool.h> + +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 <path> <min> <max> <num_steps>\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; +} |