C application for capturing keyboard shortcuts

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
5
down vote

favorite












I'm writing an application for capturing keyboard shortcuts on Linux, but I don't have a lot of experience with C or the Linux API, so I'm wondering about problems with the code or maybe parts that could be done better.



This is the code so far. I'm planning on adding other shortcuts in the future, not just for brightness.



config.h





static const char *devname_video = "Video Bus";
static const char *devname_kb = "AT Translated Set 2 keyboard";

static const double percent_brightness = 10.0;
static const char *fname_brightness_max = "/sys/class/backlight/intel_backlight/max_brightness";
static const char *fname_brightness_now = "/sys/class/backlight/intel_backlight/brightness";


ksd.c





#define _GNU_SOURCE

#include <dirent.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include "config.h"

/* Common. */

static const char *app_name = "ksd";

static void cleanup(void);

static int stop = 0;
static void handle_interrupt()
stop = 1;


/**
* Prints a failure message and exits with a failure status.
*/
static void fail(const char *format, ...)
char buffer[4096];
va_list args;
va_start(args, format);

vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);

fprintf(stderr, "%s: %s", app_name, buffer);

cleanup();
exit(EXIT_FAILURE);


/* Devices. */

static char fname_video[255];
static char fname_kb[255];
static const int fname_n = 2;

/**
* Returns true if the name of a directory file represents an event device.
*/
static int is_evdev(const struct dirent *dir)
return strncmp("event", dir->d_name, 5) == 0;


/**
* Scans input event device files and stores supported device file names.
*/
static void scan_devices(void)
int res;
struct dirent **fnames;

const int n = scandir("/dev/input", &fnames, is_evdev, versionsort);
if (n < 0)
fail("could not list /dev/input: %dn", n);


const int devname_video_l = strlen(devname_video);
const int devname_kb_l = strlen(devname_kb);

int found = 0;

for (int i = 0; i < n && found < fname_n; ++i)
char path[11 /* prefix */ + 256 /* d_name */];
snprintf(path, sizeof(path), "/dev/input/%s", fnames[i]->d_name);

const int fd = open(path, O_RDONLY);
if (fd < 0)
fail("could not open %s for reading: %dn", path, fd);


char devname[256] = 0;
if ((res = ioctl(fd, EVIOCGNAME(sizeof(devname)), devname)) < 0)
close(fd);
fail("could not read device name for %s: %dn", path, res);

close(fd);

if (strncmp(devname, devname_video, devname_video_l) == 0)
memcpy(fname_video, path, strlen(path));
++found;
else if (strncmp(devname, devname_kb, devname_kb_l) == 0)
memcpy(fname_kb, path, strlen(path));
++found;



if (found < fname_n)
fail("could not find all devices");



/* Virtual keyboard. */

static const char *vk_name = "Virtual Keyboard";
static int fd_vk;

/**
* Specifies which events the virtual keyboard should support.
*/
static void set_vk_evbits(void)
if (ioctl(fd_vk, UI_SET_EVBIT, EV_KEY) < 0)
fail("could not set EV_KEY bit on virtual keyboardn");


if (ioctl(fd_vk, UI_SET_EVBIT, EV_SYN) < 0)
fail("could not set EV_SYN bit on virtual keyboardn");



/**
* Specifies which key codes the virtual keyboard should support.
*/
static void set_vk_keybits(void)
int res;

for (int i = 0; i < KEY_MAX; ++i)
if ((res = ioctl(fd_vk, UI_SET_KEYBIT, i)) < 0)
fail("could not set key bit %d on virtual keyboard device: %dn",
i, res);




/**
* Creates a virtual keyboard device.
*/
static void create_vk(void) O_NONBLOCK);

if (fd_vk < 0)
fail("could not initialize virtual keyboardn");


set_vk_evbits();
set_vk_keybits();

struct uinput_user_dev dev;
memset(&dev, 0, sizeof(dev));

snprintf(dev.name, UINPUT_MAX_NAME_SIZE, vk_name);
dev.id.bustype = BUS_USB;
dev.id.vendor = 1;
dev.id.product = 1;
dev.id.version = 1;

if ((res = write(fd_vk, &dev, sizeof(dev)) < 0))
fail("could not write virtual keyboard data: %dn", res);


if ((res = ioctl(fd_vk, UI_DEV_CREATE)) < 0)
fail("could create virtual keyboard: %dn", res);



/**
* Destroys the virtual keyboard and closes the file descriptor.
*/
static void destroy_vk(void)
if (fd_vk <= 0)
return;


int res;

if ((res = ioctl(fd_vk, UI_DEV_DESTROY)) < 0)
close(fd_vk);
fail("could not destroy virtual keyboard: %dn", res);


close(fd_vk);


/* Devices. */

static int fd_video;
static int fd_kb;

/**
* Opens and captures devices.
*/
static void capture_devices(void)
int res;

if ((fd_video = open(fname_video, O_RDONLY)) < 0)
fail("could not open video device %s for reading: %dn",
fname_video, fd_video);


if ((res = ioctl(fd_video, EVIOCGRAB, 1)) < 0)
fail("could not capture video device %s: %dn", fname_video, res);


if ((fd_kb = open(fname_kb, O_RDONLY)) < 0)
fail("could not open keyboard device %s for reading: %dn",
fname_kb, fd_kb);


if ((res = ioctl(fd_kb, EVIOCGRAB, 1)) < 0)
fail("could not capture keyboard device %s: %dn", fname_kb, res);



/**
* Releases captured devices.
*/
static void release_devices(void)
if (fd_video > 0)
ioctl(fd_video, EVIOCGRAB, 0);
close(fd_video);


if (fd_kb > 0)
ioctl(fd_video, EVIOCGRAB, 0);
close(fd_kb);



/* Events. */

static struct input_event ev;
static const int ev_size = sizeof(struct input_event);

/* Screen brightness events. */

static int brightness_max;
static int brightness_step;

#define BRIGHTNESS_VAL_MAX 10

/**
* Reads the brightness value from a file.
*/
static int read_brightness(const char *fname)
const int fd = open(fname, O_RDONLY);
if (fd < 0)
fail("could not open brightness device %s: %d", fname, fd);


char value[BRIGHTNESS_VAL_MAX];
const ssize_t bytes = read(fd, &value, sizeof(value));
close(fd);

if (bytes == 0)
fail("could not read brightness device %s", fname);


const int value_parsed = atoi(value);
if (value_parsed == 0)
fail("could not parse brightness value "%s" from %s", value, fname);


return value_parsed;


/**
* Returns the maximum screen brightness.
*/
static int get_brightness_max(void)
if (brightness_max == 0)
brightness_max = read_brightness(fname_brightness_max);


return brightness_max;


/**
* Returns the current screen brightness.
*/
static int get_brightness_now(void)
return read_brightness(fname_brightness_now);


/**
* Returns the amount with which the brightness needs to be increased or
* decreased.
*/
static int get_brightness_step(void)
if (brightness_step == 0)
brightness_step = percent_brightness / 100.0 * get_brightness_max();


return brightness_step;


/**
* Sets the specified screen brightness.
*/
static void write_brightness(int value)
const int fd = open(fname_brightness_now, O_WRONLY);
if (fd < 0)
fail("could not open brightness file %s for writing: %d",
fname_brightness_now, fd);


char value_s[BRIGHTNESS_VAL_MAX];
snprintf(value_s, sizeof(value_s), "%d", value);
write(fd, &value_s, strlen(value_s));
close(fd);


/**
* Tries to handle a screen brightness event and returns true if the event was
* handled.
*/
static bool handle_brightness_event(void)
const int now = get_brightness_now();
const int step = get_brightness_step();

int value;

switch (ev.code)
case KEY_BRIGHTNESSDOWN:
value = now - step;
if (value < 10)
value = 10;

break;

case KEY_BRIGHTNESSUP:
value = now + step;
const int max = get_brightness_max();
if (value > max)
value = max;

break;

default:
return false;


if (value == now)
return true;


write_brightness(value);
return true;


/* Main event handling. */

static bool is_ctrl_down = false;

/**
* Returns true if the current event is a Control key event.
*/
static bool is_ctrl_key(void)
return (
ev.type == EV_KEY &&
(ev.code == KEY_LEFTCTRL

/**
* Tries to handle a keyboard event and returns true if the event was handled.
*/
static bool handle_kb_event(void)
if (is_ctrl_key())
is_ctrl_down = ev.value > 0;


if (is_ctrl_down && ev.code == KEY_C && ev.value > 0)
stop = 1;


return false;


/**
* Tries to handle a video event and returns true if the event was handled.
*/
static bool handle_video_event(void)
if (ev.value == 0)
return true;


switch (ev.code)
case KEY_BRIGHTNESSDOWN:
case KEY_BRIGHTNESSUP:
return handle_brightness_event();


return false;


/**
* Reads then tries to handle the next event from the supported devices, and
* returns true if the event was handled.
*/
static bool handle_event(void)
fd_set fds;

FD_ZERO(&fds);
FD_SET(fd_video, &fds);
FD_SET(fd_kb, &fds);

int fd_max = fd_video;
if (fd_kb > fd_max)
fd_max = fd_kb;


select(fd_max + 1, &fds, NULL, NULL, NULL);
if (stop)
cleanup();
exit(EXIT_SUCCESS);


ssize_t bytes;

#define CHECK_BYTES(b)
if ((b) < 0)
fail("expected to read %d bytes, got %ldn", ev_size, (long) bytes);


if (FD_ISSET(fd_video, &fds))
CHECK_BYTES(bytes = read(fd_video, &ev, ev_size));
return handle_video_event();
else if (FD_ISSET(fd_kb, &fds))
CHECK_BYTES(bytes = read(fd_kb, &ev, ev_size));
return handle_kb_event();
else
fail("expected file descriptor to be setn");


return false;


/**
* Forwards the current event to the virtual keyboard.
*/
static void forward_event(void)
int res;

if ((res = write(fd_vk, &ev, ev_size)) < 0)
fail("could not forward event to virtual keyboard: %dn", res);



/* Cleanup. */

static void cleanup(void)
destroy_vk();
release_devices();


/* Cache. */

static void cache(void)
get_brightness_max();
get_brightness_step();


int main(void)
scan_devices();
create_vk();
capture_devices();
cache();

signal(SIGINT, handle_interrupt);
signal(SIGTERM, handle_interrupt);

while (!stop)
if (!handle_event())
forward_event();



cleanup();
return EXIT_SUCCESS;







share|improve this question















  • 1




    For the handle_kb_event() function, it says it returns true if the event was handled, but I do not see where. For functions like fail(), which unconditionally exit the program, you can use the _Noreturn keyword. I like this code, it is very readable.
    – esote
    Jul 17 at 3:53











  • @esote, thanks! Added _Noreturn. handle_kb_event() for now handles Ctrl+C, and there's currently no key combination that can be considered handled without exiting the program, but it will later handle things like volume up or down, just like handle_video_event() handles brightness.
    – rid
    Jul 17 at 5:12

















up vote
5
down vote

favorite












I'm writing an application for capturing keyboard shortcuts on Linux, but I don't have a lot of experience with C or the Linux API, so I'm wondering about problems with the code or maybe parts that could be done better.



This is the code so far. I'm planning on adding other shortcuts in the future, not just for brightness.



config.h





static const char *devname_video = "Video Bus";
static const char *devname_kb = "AT Translated Set 2 keyboard";

static const double percent_brightness = 10.0;
static const char *fname_brightness_max = "/sys/class/backlight/intel_backlight/max_brightness";
static const char *fname_brightness_now = "/sys/class/backlight/intel_backlight/brightness";


ksd.c





#define _GNU_SOURCE

#include <dirent.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include "config.h"

/* Common. */

static const char *app_name = "ksd";

static void cleanup(void);

static int stop = 0;
static void handle_interrupt()
stop = 1;


/**
* Prints a failure message and exits with a failure status.
*/
static void fail(const char *format, ...)
char buffer[4096];
va_list args;
va_start(args, format);

vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);

fprintf(stderr, "%s: %s", app_name, buffer);

cleanup();
exit(EXIT_FAILURE);


/* Devices. */

static char fname_video[255];
static char fname_kb[255];
static const int fname_n = 2;

/**
* Returns true if the name of a directory file represents an event device.
*/
static int is_evdev(const struct dirent *dir)
return strncmp("event", dir->d_name, 5) == 0;


/**
* Scans input event device files and stores supported device file names.
*/
static void scan_devices(void)
int res;
struct dirent **fnames;

const int n = scandir("/dev/input", &fnames, is_evdev, versionsort);
if (n < 0)
fail("could not list /dev/input: %dn", n);


const int devname_video_l = strlen(devname_video);
const int devname_kb_l = strlen(devname_kb);

int found = 0;

for (int i = 0; i < n && found < fname_n; ++i)
char path[11 /* prefix */ + 256 /* d_name */];
snprintf(path, sizeof(path), "/dev/input/%s", fnames[i]->d_name);

const int fd = open(path, O_RDONLY);
if (fd < 0)
fail("could not open %s for reading: %dn", path, fd);


char devname[256] = 0;
if ((res = ioctl(fd, EVIOCGNAME(sizeof(devname)), devname)) < 0)
close(fd);
fail("could not read device name for %s: %dn", path, res);

close(fd);

if (strncmp(devname, devname_video, devname_video_l) == 0)
memcpy(fname_video, path, strlen(path));
++found;
else if (strncmp(devname, devname_kb, devname_kb_l) == 0)
memcpy(fname_kb, path, strlen(path));
++found;



if (found < fname_n)
fail("could not find all devices");



/* Virtual keyboard. */

static const char *vk_name = "Virtual Keyboard";
static int fd_vk;

/**
* Specifies which events the virtual keyboard should support.
*/
static void set_vk_evbits(void)
if (ioctl(fd_vk, UI_SET_EVBIT, EV_KEY) < 0)
fail("could not set EV_KEY bit on virtual keyboardn");


if (ioctl(fd_vk, UI_SET_EVBIT, EV_SYN) < 0)
fail("could not set EV_SYN bit on virtual keyboardn");



/**
* Specifies which key codes the virtual keyboard should support.
*/
static void set_vk_keybits(void)
int res;

for (int i = 0; i < KEY_MAX; ++i)
if ((res = ioctl(fd_vk, UI_SET_KEYBIT, i)) < 0)
fail("could not set key bit %d on virtual keyboard device: %dn",
i, res);




/**
* Creates a virtual keyboard device.
*/
static void create_vk(void) O_NONBLOCK);

if (fd_vk < 0)
fail("could not initialize virtual keyboardn");


set_vk_evbits();
set_vk_keybits();

struct uinput_user_dev dev;
memset(&dev, 0, sizeof(dev));

snprintf(dev.name, UINPUT_MAX_NAME_SIZE, vk_name);
dev.id.bustype = BUS_USB;
dev.id.vendor = 1;
dev.id.product = 1;
dev.id.version = 1;

if ((res = write(fd_vk, &dev, sizeof(dev)) < 0))
fail("could not write virtual keyboard data: %dn", res);


if ((res = ioctl(fd_vk, UI_DEV_CREATE)) < 0)
fail("could create virtual keyboard: %dn", res);



/**
* Destroys the virtual keyboard and closes the file descriptor.
*/
static void destroy_vk(void)
if (fd_vk <= 0)
return;


int res;

if ((res = ioctl(fd_vk, UI_DEV_DESTROY)) < 0)
close(fd_vk);
fail("could not destroy virtual keyboard: %dn", res);


close(fd_vk);


/* Devices. */

static int fd_video;
static int fd_kb;

/**
* Opens and captures devices.
*/
static void capture_devices(void)
int res;

if ((fd_video = open(fname_video, O_RDONLY)) < 0)
fail("could not open video device %s for reading: %dn",
fname_video, fd_video);


if ((res = ioctl(fd_video, EVIOCGRAB, 1)) < 0)
fail("could not capture video device %s: %dn", fname_video, res);


if ((fd_kb = open(fname_kb, O_RDONLY)) < 0)
fail("could not open keyboard device %s for reading: %dn",
fname_kb, fd_kb);


if ((res = ioctl(fd_kb, EVIOCGRAB, 1)) < 0)
fail("could not capture keyboard device %s: %dn", fname_kb, res);



/**
* Releases captured devices.
*/
static void release_devices(void)
if (fd_video > 0)
ioctl(fd_video, EVIOCGRAB, 0);
close(fd_video);


if (fd_kb > 0)
ioctl(fd_video, EVIOCGRAB, 0);
close(fd_kb);



/* Events. */

static struct input_event ev;
static const int ev_size = sizeof(struct input_event);

/* Screen brightness events. */

static int brightness_max;
static int brightness_step;

#define BRIGHTNESS_VAL_MAX 10

/**
* Reads the brightness value from a file.
*/
static int read_brightness(const char *fname)
const int fd = open(fname, O_RDONLY);
if (fd < 0)
fail("could not open brightness device %s: %d", fname, fd);


char value[BRIGHTNESS_VAL_MAX];
const ssize_t bytes = read(fd, &value, sizeof(value));
close(fd);

if (bytes == 0)
fail("could not read brightness device %s", fname);


const int value_parsed = atoi(value);
if (value_parsed == 0)
fail("could not parse brightness value "%s" from %s", value, fname);


return value_parsed;


/**
* Returns the maximum screen brightness.
*/
static int get_brightness_max(void)
if (brightness_max == 0)
brightness_max = read_brightness(fname_brightness_max);


return brightness_max;


/**
* Returns the current screen brightness.
*/
static int get_brightness_now(void)
return read_brightness(fname_brightness_now);


/**
* Returns the amount with which the brightness needs to be increased or
* decreased.
*/
static int get_brightness_step(void)
if (brightness_step == 0)
brightness_step = percent_brightness / 100.0 * get_brightness_max();


return brightness_step;


/**
* Sets the specified screen brightness.
*/
static void write_brightness(int value)
const int fd = open(fname_brightness_now, O_WRONLY);
if (fd < 0)
fail("could not open brightness file %s for writing: %d",
fname_brightness_now, fd);


char value_s[BRIGHTNESS_VAL_MAX];
snprintf(value_s, sizeof(value_s), "%d", value);
write(fd, &value_s, strlen(value_s));
close(fd);


/**
* Tries to handle a screen brightness event and returns true if the event was
* handled.
*/
static bool handle_brightness_event(void)
const int now = get_brightness_now();
const int step = get_brightness_step();

int value;

switch (ev.code)
case KEY_BRIGHTNESSDOWN:
value = now - step;
if (value < 10)
value = 10;

break;

case KEY_BRIGHTNESSUP:
value = now + step;
const int max = get_brightness_max();
if (value > max)
value = max;

break;

default:
return false;


if (value == now)
return true;


write_brightness(value);
return true;


/* Main event handling. */

static bool is_ctrl_down = false;

/**
* Returns true if the current event is a Control key event.
*/
static bool is_ctrl_key(void)
return (
ev.type == EV_KEY &&
(ev.code == KEY_LEFTCTRL

/**
* Tries to handle a keyboard event and returns true if the event was handled.
*/
static bool handle_kb_event(void)
if (is_ctrl_key())
is_ctrl_down = ev.value > 0;


if (is_ctrl_down && ev.code == KEY_C && ev.value > 0)
stop = 1;


return false;


/**
* Tries to handle a video event and returns true if the event was handled.
*/
static bool handle_video_event(void)
if (ev.value == 0)
return true;


switch (ev.code)
case KEY_BRIGHTNESSDOWN:
case KEY_BRIGHTNESSUP:
return handle_brightness_event();


return false;


/**
* Reads then tries to handle the next event from the supported devices, and
* returns true if the event was handled.
*/
static bool handle_event(void)
fd_set fds;

FD_ZERO(&fds);
FD_SET(fd_video, &fds);
FD_SET(fd_kb, &fds);

int fd_max = fd_video;
if (fd_kb > fd_max)
fd_max = fd_kb;


select(fd_max + 1, &fds, NULL, NULL, NULL);
if (stop)
cleanup();
exit(EXIT_SUCCESS);


ssize_t bytes;

#define CHECK_BYTES(b)
if ((b) < 0)
fail("expected to read %d bytes, got %ldn", ev_size, (long) bytes);


if (FD_ISSET(fd_video, &fds))
CHECK_BYTES(bytes = read(fd_video, &ev, ev_size));
return handle_video_event();
else if (FD_ISSET(fd_kb, &fds))
CHECK_BYTES(bytes = read(fd_kb, &ev, ev_size));
return handle_kb_event();
else
fail("expected file descriptor to be setn");


return false;


/**
* Forwards the current event to the virtual keyboard.
*/
static void forward_event(void)
int res;

if ((res = write(fd_vk, &ev, ev_size)) < 0)
fail("could not forward event to virtual keyboard: %dn", res);



/* Cleanup. */

static void cleanup(void)
destroy_vk();
release_devices();


/* Cache. */

static void cache(void)
get_brightness_max();
get_brightness_step();


int main(void)
scan_devices();
create_vk();
capture_devices();
cache();

signal(SIGINT, handle_interrupt);
signal(SIGTERM, handle_interrupt);

while (!stop)
if (!handle_event())
forward_event();



cleanup();
return EXIT_SUCCESS;







share|improve this question















  • 1




    For the handle_kb_event() function, it says it returns true if the event was handled, but I do not see where. For functions like fail(), which unconditionally exit the program, you can use the _Noreturn keyword. I like this code, it is very readable.
    – esote
    Jul 17 at 3:53











  • @esote, thanks! Added _Noreturn. handle_kb_event() for now handles Ctrl+C, and there's currently no key combination that can be considered handled without exiting the program, but it will later handle things like volume up or down, just like handle_video_event() handles brightness.
    – rid
    Jul 17 at 5:12













up vote
5
down vote

favorite









up vote
5
down vote

favorite











I'm writing an application for capturing keyboard shortcuts on Linux, but I don't have a lot of experience with C or the Linux API, so I'm wondering about problems with the code or maybe parts that could be done better.



This is the code so far. I'm planning on adding other shortcuts in the future, not just for brightness.



config.h





static const char *devname_video = "Video Bus";
static const char *devname_kb = "AT Translated Set 2 keyboard";

static const double percent_brightness = 10.0;
static const char *fname_brightness_max = "/sys/class/backlight/intel_backlight/max_brightness";
static const char *fname_brightness_now = "/sys/class/backlight/intel_backlight/brightness";


ksd.c





#define _GNU_SOURCE

#include <dirent.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include "config.h"

/* Common. */

static const char *app_name = "ksd";

static void cleanup(void);

static int stop = 0;
static void handle_interrupt()
stop = 1;


/**
* Prints a failure message and exits with a failure status.
*/
static void fail(const char *format, ...)
char buffer[4096];
va_list args;
va_start(args, format);

vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);

fprintf(stderr, "%s: %s", app_name, buffer);

cleanup();
exit(EXIT_FAILURE);


/* Devices. */

static char fname_video[255];
static char fname_kb[255];
static const int fname_n = 2;

/**
* Returns true if the name of a directory file represents an event device.
*/
static int is_evdev(const struct dirent *dir)
return strncmp("event", dir->d_name, 5) == 0;


/**
* Scans input event device files and stores supported device file names.
*/
static void scan_devices(void)
int res;
struct dirent **fnames;

const int n = scandir("/dev/input", &fnames, is_evdev, versionsort);
if (n < 0)
fail("could not list /dev/input: %dn", n);


const int devname_video_l = strlen(devname_video);
const int devname_kb_l = strlen(devname_kb);

int found = 0;

for (int i = 0; i < n && found < fname_n; ++i)
char path[11 /* prefix */ + 256 /* d_name */];
snprintf(path, sizeof(path), "/dev/input/%s", fnames[i]->d_name);

const int fd = open(path, O_RDONLY);
if (fd < 0)
fail("could not open %s for reading: %dn", path, fd);


char devname[256] = 0;
if ((res = ioctl(fd, EVIOCGNAME(sizeof(devname)), devname)) < 0)
close(fd);
fail("could not read device name for %s: %dn", path, res);

close(fd);

if (strncmp(devname, devname_video, devname_video_l) == 0)
memcpy(fname_video, path, strlen(path));
++found;
else if (strncmp(devname, devname_kb, devname_kb_l) == 0)
memcpy(fname_kb, path, strlen(path));
++found;



if (found < fname_n)
fail("could not find all devices");



/* Virtual keyboard. */

static const char *vk_name = "Virtual Keyboard";
static int fd_vk;

/**
* Specifies which events the virtual keyboard should support.
*/
static void set_vk_evbits(void)
if (ioctl(fd_vk, UI_SET_EVBIT, EV_KEY) < 0)
fail("could not set EV_KEY bit on virtual keyboardn");


if (ioctl(fd_vk, UI_SET_EVBIT, EV_SYN) < 0)
fail("could not set EV_SYN bit on virtual keyboardn");



/**
* Specifies which key codes the virtual keyboard should support.
*/
static void set_vk_keybits(void)
int res;

for (int i = 0; i < KEY_MAX; ++i)
if ((res = ioctl(fd_vk, UI_SET_KEYBIT, i)) < 0)
fail("could not set key bit %d on virtual keyboard device: %dn",
i, res);




/**
* Creates a virtual keyboard device.
*/
static void create_vk(void) O_NONBLOCK);

if (fd_vk < 0)
fail("could not initialize virtual keyboardn");


set_vk_evbits();
set_vk_keybits();

struct uinput_user_dev dev;
memset(&dev, 0, sizeof(dev));

snprintf(dev.name, UINPUT_MAX_NAME_SIZE, vk_name);
dev.id.bustype = BUS_USB;
dev.id.vendor = 1;
dev.id.product = 1;
dev.id.version = 1;

if ((res = write(fd_vk, &dev, sizeof(dev)) < 0))
fail("could not write virtual keyboard data: %dn", res);


if ((res = ioctl(fd_vk, UI_DEV_CREATE)) < 0)
fail("could create virtual keyboard: %dn", res);



/**
* Destroys the virtual keyboard and closes the file descriptor.
*/
static void destroy_vk(void)
if (fd_vk <= 0)
return;


int res;

if ((res = ioctl(fd_vk, UI_DEV_DESTROY)) < 0)
close(fd_vk);
fail("could not destroy virtual keyboard: %dn", res);


close(fd_vk);


/* Devices. */

static int fd_video;
static int fd_kb;

/**
* Opens and captures devices.
*/
static void capture_devices(void)
int res;

if ((fd_video = open(fname_video, O_RDONLY)) < 0)
fail("could not open video device %s for reading: %dn",
fname_video, fd_video);


if ((res = ioctl(fd_video, EVIOCGRAB, 1)) < 0)
fail("could not capture video device %s: %dn", fname_video, res);


if ((fd_kb = open(fname_kb, O_RDONLY)) < 0)
fail("could not open keyboard device %s for reading: %dn",
fname_kb, fd_kb);


if ((res = ioctl(fd_kb, EVIOCGRAB, 1)) < 0)
fail("could not capture keyboard device %s: %dn", fname_kb, res);



/**
* Releases captured devices.
*/
static void release_devices(void)
if (fd_video > 0)
ioctl(fd_video, EVIOCGRAB, 0);
close(fd_video);


if (fd_kb > 0)
ioctl(fd_video, EVIOCGRAB, 0);
close(fd_kb);



/* Events. */

static struct input_event ev;
static const int ev_size = sizeof(struct input_event);

/* Screen brightness events. */

static int brightness_max;
static int brightness_step;

#define BRIGHTNESS_VAL_MAX 10

/**
* Reads the brightness value from a file.
*/
static int read_brightness(const char *fname)
const int fd = open(fname, O_RDONLY);
if (fd < 0)
fail("could not open brightness device %s: %d", fname, fd);


char value[BRIGHTNESS_VAL_MAX];
const ssize_t bytes = read(fd, &value, sizeof(value));
close(fd);

if (bytes == 0)
fail("could not read brightness device %s", fname);


const int value_parsed = atoi(value);
if (value_parsed == 0)
fail("could not parse brightness value "%s" from %s", value, fname);


return value_parsed;


/**
* Returns the maximum screen brightness.
*/
static int get_brightness_max(void)
if (brightness_max == 0)
brightness_max = read_brightness(fname_brightness_max);


return brightness_max;


/**
* Returns the current screen brightness.
*/
static int get_brightness_now(void)
return read_brightness(fname_brightness_now);


/**
* Returns the amount with which the brightness needs to be increased or
* decreased.
*/
static int get_brightness_step(void)
if (brightness_step == 0)
brightness_step = percent_brightness / 100.0 * get_brightness_max();


return brightness_step;


/**
* Sets the specified screen brightness.
*/
static void write_brightness(int value)
const int fd = open(fname_brightness_now, O_WRONLY);
if (fd < 0)
fail("could not open brightness file %s for writing: %d",
fname_brightness_now, fd);


char value_s[BRIGHTNESS_VAL_MAX];
snprintf(value_s, sizeof(value_s), "%d", value);
write(fd, &value_s, strlen(value_s));
close(fd);


/**
* Tries to handle a screen brightness event and returns true if the event was
* handled.
*/
static bool handle_brightness_event(void)
const int now = get_brightness_now();
const int step = get_brightness_step();

int value;

switch (ev.code)
case KEY_BRIGHTNESSDOWN:
value = now - step;
if (value < 10)
value = 10;

break;

case KEY_BRIGHTNESSUP:
value = now + step;
const int max = get_brightness_max();
if (value > max)
value = max;

break;

default:
return false;


if (value == now)
return true;


write_brightness(value);
return true;


/* Main event handling. */

static bool is_ctrl_down = false;

/**
* Returns true if the current event is a Control key event.
*/
static bool is_ctrl_key(void)
return (
ev.type == EV_KEY &&
(ev.code == KEY_LEFTCTRL

/**
* Tries to handle a keyboard event and returns true if the event was handled.
*/
static bool handle_kb_event(void)
if (is_ctrl_key())
is_ctrl_down = ev.value > 0;


if (is_ctrl_down && ev.code == KEY_C && ev.value > 0)
stop = 1;


return false;


/**
* Tries to handle a video event and returns true if the event was handled.
*/
static bool handle_video_event(void)
if (ev.value == 0)
return true;


switch (ev.code)
case KEY_BRIGHTNESSDOWN:
case KEY_BRIGHTNESSUP:
return handle_brightness_event();


return false;


/**
* Reads then tries to handle the next event from the supported devices, and
* returns true if the event was handled.
*/
static bool handle_event(void)
fd_set fds;

FD_ZERO(&fds);
FD_SET(fd_video, &fds);
FD_SET(fd_kb, &fds);

int fd_max = fd_video;
if (fd_kb > fd_max)
fd_max = fd_kb;


select(fd_max + 1, &fds, NULL, NULL, NULL);
if (stop)
cleanup();
exit(EXIT_SUCCESS);


ssize_t bytes;

#define CHECK_BYTES(b)
if ((b) < 0)
fail("expected to read %d bytes, got %ldn", ev_size, (long) bytes);


if (FD_ISSET(fd_video, &fds))
CHECK_BYTES(bytes = read(fd_video, &ev, ev_size));
return handle_video_event();
else if (FD_ISSET(fd_kb, &fds))
CHECK_BYTES(bytes = read(fd_kb, &ev, ev_size));
return handle_kb_event();
else
fail("expected file descriptor to be setn");


return false;


/**
* Forwards the current event to the virtual keyboard.
*/
static void forward_event(void)
int res;

if ((res = write(fd_vk, &ev, ev_size)) < 0)
fail("could not forward event to virtual keyboard: %dn", res);



/* Cleanup. */

static void cleanup(void)
destroy_vk();
release_devices();


/* Cache. */

static void cache(void)
get_brightness_max();
get_brightness_step();


int main(void)
scan_devices();
create_vk();
capture_devices();
cache();

signal(SIGINT, handle_interrupt);
signal(SIGTERM, handle_interrupt);

while (!stop)
if (!handle_event())
forward_event();



cleanup();
return EXIT_SUCCESS;







share|improve this question











I'm writing an application for capturing keyboard shortcuts on Linux, but I don't have a lot of experience with C or the Linux API, so I'm wondering about problems with the code or maybe parts that could be done better.



This is the code so far. I'm planning on adding other shortcuts in the future, not just for brightness.



config.h





static const char *devname_video = "Video Bus";
static const char *devname_kb = "AT Translated Set 2 keyboard";

static const double percent_brightness = 10.0;
static const char *fname_brightness_max = "/sys/class/backlight/intel_backlight/max_brightness";
static const char *fname_brightness_now = "/sys/class/backlight/intel_backlight/brightness";


ksd.c





#define _GNU_SOURCE

#include <dirent.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include "config.h"

/* Common. */

static const char *app_name = "ksd";

static void cleanup(void);

static int stop = 0;
static void handle_interrupt()
stop = 1;


/**
* Prints a failure message and exits with a failure status.
*/
static void fail(const char *format, ...)
char buffer[4096];
va_list args;
va_start(args, format);

vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);

fprintf(stderr, "%s: %s", app_name, buffer);

cleanup();
exit(EXIT_FAILURE);


/* Devices. */

static char fname_video[255];
static char fname_kb[255];
static const int fname_n = 2;

/**
* Returns true if the name of a directory file represents an event device.
*/
static int is_evdev(const struct dirent *dir)
return strncmp("event", dir->d_name, 5) == 0;


/**
* Scans input event device files and stores supported device file names.
*/
static void scan_devices(void)
int res;
struct dirent **fnames;

const int n = scandir("/dev/input", &fnames, is_evdev, versionsort);
if (n < 0)
fail("could not list /dev/input: %dn", n);


const int devname_video_l = strlen(devname_video);
const int devname_kb_l = strlen(devname_kb);

int found = 0;

for (int i = 0; i < n && found < fname_n; ++i)
char path[11 /* prefix */ + 256 /* d_name */];
snprintf(path, sizeof(path), "/dev/input/%s", fnames[i]->d_name);

const int fd = open(path, O_RDONLY);
if (fd < 0)
fail("could not open %s for reading: %dn", path, fd);


char devname[256] = 0;
if ((res = ioctl(fd, EVIOCGNAME(sizeof(devname)), devname)) < 0)
close(fd);
fail("could not read device name for %s: %dn", path, res);

close(fd);

if (strncmp(devname, devname_video, devname_video_l) == 0)
memcpy(fname_video, path, strlen(path));
++found;
else if (strncmp(devname, devname_kb, devname_kb_l) == 0)
memcpy(fname_kb, path, strlen(path));
++found;



if (found < fname_n)
fail("could not find all devices");



/* Virtual keyboard. */

static const char *vk_name = "Virtual Keyboard";
static int fd_vk;

/**
* Specifies which events the virtual keyboard should support.
*/
static void set_vk_evbits(void)
if (ioctl(fd_vk, UI_SET_EVBIT, EV_KEY) < 0)
fail("could not set EV_KEY bit on virtual keyboardn");


if (ioctl(fd_vk, UI_SET_EVBIT, EV_SYN) < 0)
fail("could not set EV_SYN bit on virtual keyboardn");



/**
* Specifies which key codes the virtual keyboard should support.
*/
static void set_vk_keybits(void)
int res;

for (int i = 0; i < KEY_MAX; ++i)
if ((res = ioctl(fd_vk, UI_SET_KEYBIT, i)) < 0)
fail("could not set key bit %d on virtual keyboard device: %dn",
i, res);




/**
* Creates a virtual keyboard device.
*/
static void create_vk(void) O_NONBLOCK);

if (fd_vk < 0)
fail("could not initialize virtual keyboardn");


set_vk_evbits();
set_vk_keybits();

struct uinput_user_dev dev;
memset(&dev, 0, sizeof(dev));

snprintf(dev.name, UINPUT_MAX_NAME_SIZE, vk_name);
dev.id.bustype = BUS_USB;
dev.id.vendor = 1;
dev.id.product = 1;
dev.id.version = 1;

if ((res = write(fd_vk, &dev, sizeof(dev)) < 0))
fail("could not write virtual keyboard data: %dn", res);


if ((res = ioctl(fd_vk, UI_DEV_CREATE)) < 0)
fail("could create virtual keyboard: %dn", res);



/**
* Destroys the virtual keyboard and closes the file descriptor.
*/
static void destroy_vk(void)
if (fd_vk <= 0)
return;


int res;

if ((res = ioctl(fd_vk, UI_DEV_DESTROY)) < 0)
close(fd_vk);
fail("could not destroy virtual keyboard: %dn", res);


close(fd_vk);


/* Devices. */

static int fd_video;
static int fd_kb;

/**
* Opens and captures devices.
*/
static void capture_devices(void)
int res;

if ((fd_video = open(fname_video, O_RDONLY)) < 0)
fail("could not open video device %s for reading: %dn",
fname_video, fd_video);


if ((res = ioctl(fd_video, EVIOCGRAB, 1)) < 0)
fail("could not capture video device %s: %dn", fname_video, res);


if ((fd_kb = open(fname_kb, O_RDONLY)) < 0)
fail("could not open keyboard device %s for reading: %dn",
fname_kb, fd_kb);


if ((res = ioctl(fd_kb, EVIOCGRAB, 1)) < 0)
fail("could not capture keyboard device %s: %dn", fname_kb, res);



/**
* Releases captured devices.
*/
static void release_devices(void)
if (fd_video > 0)
ioctl(fd_video, EVIOCGRAB, 0);
close(fd_video);


if (fd_kb > 0)
ioctl(fd_video, EVIOCGRAB, 0);
close(fd_kb);



/* Events. */

static struct input_event ev;
static const int ev_size = sizeof(struct input_event);

/* Screen brightness events. */

static int brightness_max;
static int brightness_step;

#define BRIGHTNESS_VAL_MAX 10

/**
* Reads the brightness value from a file.
*/
static int read_brightness(const char *fname)
const int fd = open(fname, O_RDONLY);
if (fd < 0)
fail("could not open brightness device %s: %d", fname, fd);


char value[BRIGHTNESS_VAL_MAX];
const ssize_t bytes = read(fd, &value, sizeof(value));
close(fd);

if (bytes == 0)
fail("could not read brightness device %s", fname);


const int value_parsed = atoi(value);
if (value_parsed == 0)
fail("could not parse brightness value "%s" from %s", value, fname);


return value_parsed;


/**
* Returns the maximum screen brightness.
*/
static int get_brightness_max(void)
if (brightness_max == 0)
brightness_max = read_brightness(fname_brightness_max);


return brightness_max;


/**
* Returns the current screen brightness.
*/
static int get_brightness_now(void)
return read_brightness(fname_brightness_now);


/**
* Returns the amount with which the brightness needs to be increased or
* decreased.
*/
static int get_brightness_step(void)
if (brightness_step == 0)
brightness_step = percent_brightness / 100.0 * get_brightness_max();


return brightness_step;


/**
* Sets the specified screen brightness.
*/
static void write_brightness(int value)
const int fd = open(fname_brightness_now, O_WRONLY);
if (fd < 0)
fail("could not open brightness file %s for writing: %d",
fname_brightness_now, fd);


char value_s[BRIGHTNESS_VAL_MAX];
snprintf(value_s, sizeof(value_s), "%d", value);
write(fd, &value_s, strlen(value_s));
close(fd);


/**
* Tries to handle a screen brightness event and returns true if the event was
* handled.
*/
static bool handle_brightness_event(void)
const int now = get_brightness_now();
const int step = get_brightness_step();

int value;

switch (ev.code)
case KEY_BRIGHTNESSDOWN:
value = now - step;
if (value < 10)
value = 10;

break;

case KEY_BRIGHTNESSUP:
value = now + step;
const int max = get_brightness_max();
if (value > max)
value = max;

break;

default:
return false;


if (value == now)
return true;


write_brightness(value);
return true;


/* Main event handling. */

static bool is_ctrl_down = false;

/**
* Returns true if the current event is a Control key event.
*/
static bool is_ctrl_key(void)
return (
ev.type == EV_KEY &&
(ev.code == KEY_LEFTCTRL

/**
* Tries to handle a keyboard event and returns true if the event was handled.
*/
static bool handle_kb_event(void)
if (is_ctrl_key())
is_ctrl_down = ev.value > 0;


if (is_ctrl_down && ev.code == KEY_C && ev.value > 0)
stop = 1;


return false;


/**
* Tries to handle a video event and returns true if the event was handled.
*/
static bool handle_video_event(void)
if (ev.value == 0)
return true;


switch (ev.code)
case KEY_BRIGHTNESSDOWN:
case KEY_BRIGHTNESSUP:
return handle_brightness_event();


return false;


/**
* Reads then tries to handle the next event from the supported devices, and
* returns true if the event was handled.
*/
static bool handle_event(void)
fd_set fds;

FD_ZERO(&fds);
FD_SET(fd_video, &fds);
FD_SET(fd_kb, &fds);

int fd_max = fd_video;
if (fd_kb > fd_max)
fd_max = fd_kb;


select(fd_max + 1, &fds, NULL, NULL, NULL);
if (stop)
cleanup();
exit(EXIT_SUCCESS);


ssize_t bytes;

#define CHECK_BYTES(b)
if ((b) < 0)
fail("expected to read %d bytes, got %ldn", ev_size, (long) bytes);


if (FD_ISSET(fd_video, &fds))
CHECK_BYTES(bytes = read(fd_video, &ev, ev_size));
return handle_video_event();
else if (FD_ISSET(fd_kb, &fds))
CHECK_BYTES(bytes = read(fd_kb, &ev, ev_size));
return handle_kb_event();
else
fail("expected file descriptor to be setn");


return false;


/**
* Forwards the current event to the virtual keyboard.
*/
static void forward_event(void)
int res;

if ((res = write(fd_vk, &ev, ev_size)) < 0)
fail("could not forward event to virtual keyboard: %dn", res);



/* Cleanup. */

static void cleanup(void)
destroy_vk();
release_devices();


/* Cache. */

static void cache(void)
get_brightness_max();
get_brightness_step();


int main(void)
scan_devices();
create_vk();
capture_devices();
cache();

signal(SIGINT, handle_interrupt);
signal(SIGTERM, handle_interrupt);

while (!stop)
if (!handle_event())
forward_event();



cleanup();
return EXIT_SUCCESS;









share|improve this question










share|improve this question




share|improve this question









asked Jul 15 at 15:20









rid

1283




1283







  • 1




    For the handle_kb_event() function, it says it returns true if the event was handled, but I do not see where. For functions like fail(), which unconditionally exit the program, you can use the _Noreturn keyword. I like this code, it is very readable.
    – esote
    Jul 17 at 3:53











  • @esote, thanks! Added _Noreturn. handle_kb_event() for now handles Ctrl+C, and there's currently no key combination that can be considered handled without exiting the program, but it will later handle things like volume up or down, just like handle_video_event() handles brightness.
    – rid
    Jul 17 at 5:12













  • 1




    For the handle_kb_event() function, it says it returns true if the event was handled, but I do not see where. For functions like fail(), which unconditionally exit the program, you can use the _Noreturn keyword. I like this code, it is very readable.
    – esote
    Jul 17 at 3:53











  • @esote, thanks! Added _Noreturn. handle_kb_event() for now handles Ctrl+C, and there's currently no key combination that can be considered handled without exiting the program, but it will later handle things like volume up or down, just like handle_video_event() handles brightness.
    – rid
    Jul 17 at 5:12








1




1




For the handle_kb_event() function, it says it returns true if the event was handled, but I do not see where. For functions like fail(), which unconditionally exit the program, you can use the _Noreturn keyword. I like this code, it is very readable.
– esote
Jul 17 at 3:53





For the handle_kb_event() function, it says it returns true if the event was handled, but I do not see where. For functions like fail(), which unconditionally exit the program, you can use the _Noreturn keyword. I like this code, it is very readable.
– esote
Jul 17 at 3:53













@esote, thanks! Added _Noreturn. handle_kb_event() for now handles Ctrl+C, and there's currently no key combination that can be considered handled without exiting the program, but it will later handle things like volume up or down, just like handle_video_event() handles brightness.
– rid
Jul 17 at 5:12





@esote, thanks! Added _Noreturn. handle_kb_event() for now handles Ctrl+C, and there's currently no key combination that can be considered handled without exiting the program, but it will later handle things like volume up or down, just like handle_video_event() handles brightness.
– rid
Jul 17 at 5:12











1 Answer
1






active

oldest

votes

















up vote
2
down vote



accepted











I'm wondering about problems with the code or maybe parts that could be done better.




Just a small review.



Avoid magic numbers



fnames[i]->d_name points to a string of a length not exceeding NAME_MAX.



Rather than coding manually to match the size of a string literal, use sizeof. No great need to avoid a slightly over-sized buffer.



If code forgoes checking the return value of snprintf(), it is losing out on robust error checking.



// char path[11 /* prefix */ + 256 /* d_name */];
// snprintf(path, sizeof(path), "/dev/input/%s", fnames[i]->d_name);

const char prefix = "/dev/input/%s";
char path[sizeof prefix + NAME_MAX + 1];

int len = snprintf(path, sizeof path, prefix, fnames[i]->d_name);
if (len < 0 || len >= sizeof path)
Handle_Error();


// char devname[256] = ...
char devname[NAME_MAX + 1] = ...;

// return strncmp("event", dir->d_name, 5) == 0;
const char event = "event";
return strncmp(event, dir->d_name, sizeof event - 1) == 0;





share|improve this answer

















  • 1




    Nice, thanks, I also extracted that 10 from if (value < 10) ... to a brightness_min constant.
    – rid
    Jul 17 at 5:08










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f199547%2fc-application-for-capturing-keyboard-shortcuts%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
2
down vote



accepted











I'm wondering about problems with the code or maybe parts that could be done better.




Just a small review.



Avoid magic numbers



fnames[i]->d_name points to a string of a length not exceeding NAME_MAX.



Rather than coding manually to match the size of a string literal, use sizeof. No great need to avoid a slightly over-sized buffer.



If code forgoes checking the return value of snprintf(), it is losing out on robust error checking.



// char path[11 /* prefix */ + 256 /* d_name */];
// snprintf(path, sizeof(path), "/dev/input/%s", fnames[i]->d_name);

const char prefix = "/dev/input/%s";
char path[sizeof prefix + NAME_MAX + 1];

int len = snprintf(path, sizeof path, prefix, fnames[i]->d_name);
if (len < 0 || len >= sizeof path)
Handle_Error();


// char devname[256] = ...
char devname[NAME_MAX + 1] = ...;

// return strncmp("event", dir->d_name, 5) == 0;
const char event = "event";
return strncmp(event, dir->d_name, sizeof event - 1) == 0;





share|improve this answer

















  • 1




    Nice, thanks, I also extracted that 10 from if (value < 10) ... to a brightness_min constant.
    – rid
    Jul 17 at 5:08














up vote
2
down vote



accepted











I'm wondering about problems with the code or maybe parts that could be done better.




Just a small review.



Avoid magic numbers



fnames[i]->d_name points to a string of a length not exceeding NAME_MAX.



Rather than coding manually to match the size of a string literal, use sizeof. No great need to avoid a slightly over-sized buffer.



If code forgoes checking the return value of snprintf(), it is losing out on robust error checking.



// char path[11 /* prefix */ + 256 /* d_name */];
// snprintf(path, sizeof(path), "/dev/input/%s", fnames[i]->d_name);

const char prefix = "/dev/input/%s";
char path[sizeof prefix + NAME_MAX + 1];

int len = snprintf(path, sizeof path, prefix, fnames[i]->d_name);
if (len < 0 || len >= sizeof path)
Handle_Error();


// char devname[256] = ...
char devname[NAME_MAX + 1] = ...;

// return strncmp("event", dir->d_name, 5) == 0;
const char event = "event";
return strncmp(event, dir->d_name, sizeof event - 1) == 0;





share|improve this answer

















  • 1




    Nice, thanks, I also extracted that 10 from if (value < 10) ... to a brightness_min constant.
    – rid
    Jul 17 at 5:08












up vote
2
down vote



accepted







up vote
2
down vote



accepted







I'm wondering about problems with the code or maybe parts that could be done better.




Just a small review.



Avoid magic numbers



fnames[i]->d_name points to a string of a length not exceeding NAME_MAX.



Rather than coding manually to match the size of a string literal, use sizeof. No great need to avoid a slightly over-sized buffer.



If code forgoes checking the return value of snprintf(), it is losing out on robust error checking.



// char path[11 /* prefix */ + 256 /* d_name */];
// snprintf(path, sizeof(path), "/dev/input/%s", fnames[i]->d_name);

const char prefix = "/dev/input/%s";
char path[sizeof prefix + NAME_MAX + 1];

int len = snprintf(path, sizeof path, prefix, fnames[i]->d_name);
if (len < 0 || len >= sizeof path)
Handle_Error();


// char devname[256] = ...
char devname[NAME_MAX + 1] = ...;

// return strncmp("event", dir->d_name, 5) == 0;
const char event = "event";
return strncmp(event, dir->d_name, sizeof event - 1) == 0;





share|improve this answer














I'm wondering about problems with the code or maybe parts that could be done better.




Just a small review.



Avoid magic numbers



fnames[i]->d_name points to a string of a length not exceeding NAME_MAX.



Rather than coding manually to match the size of a string literal, use sizeof. No great need to avoid a slightly over-sized buffer.



If code forgoes checking the return value of snprintf(), it is losing out on robust error checking.



// char path[11 /* prefix */ + 256 /* d_name */];
// snprintf(path, sizeof(path), "/dev/input/%s", fnames[i]->d_name);

const char prefix = "/dev/input/%s";
char path[sizeof prefix + NAME_MAX + 1];

int len = snprintf(path, sizeof path, prefix, fnames[i]->d_name);
if (len < 0 || len >= sizeof path)
Handle_Error();


// char devname[256] = ...
char devname[NAME_MAX + 1] = ...;

// return strncmp("event", dir->d_name, 5) == 0;
const char event = "event";
return strncmp(event, dir->d_name, sizeof event - 1) == 0;






share|improve this answer













share|improve this answer



share|improve this answer











answered Jul 17 at 2:56









chux

11.4k11238




11.4k11238







  • 1




    Nice, thanks, I also extracted that 10 from if (value < 10) ... to a brightness_min constant.
    – rid
    Jul 17 at 5:08












  • 1




    Nice, thanks, I also extracted that 10 from if (value < 10) ... to a brightness_min constant.
    – rid
    Jul 17 at 5:08







1




1




Nice, thanks, I also extracted that 10 from if (value < 10) ... to a brightness_min constant.
– rid
Jul 17 at 5:08




Nice, thanks, I also extracted that 10 from if (value < 10) ... to a brightness_min constant.
– rid
Jul 17 at 5:08












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f199547%2fc-application-for-capturing-keyboard-shortcuts%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Chat program with C++ and SFML

Function to Return a JSON Like Objects Using VBA Collections and Arrays

Will my employers contract hold up in court?