summaryrefslogtreecommitdiffstats
path: root/core/modules/slx-brightness/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/modules/slx-brightness/src/main.c')
-rw-r--r--core/modules/slx-brightness/src/main.c318
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;
+}