C application for capturing keyboard shortcuts
Clash 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;
c linux
add a comment |Â
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;
c linux
1
For thehandle_kb_event()
function, it says it returns true if the event was handled, but I do not see where. For functions likefail()
, 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 handlesCtrl+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 likehandle_video_event()
handles brightness.
â rid
Jul 17 at 5:12
add a comment |Â
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;
c linux
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;
c linux
asked Jul 15 at 15:20
rid
1283
1283
1
For thehandle_kb_event()
function, it says it returns true if the event was handled, but I do not see where. For functions likefail()
, 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 handlesCtrl+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 likehandle_video_event()
handles brightness.
â rid
Jul 17 at 5:12
add a comment |Â
1
For thehandle_kb_event()
function, it says it returns true if the event was handled, but I do not see where. For functions likefail()
, 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 handlesCtrl+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 likehandle_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
add a comment |Â
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;
1
Nice, thanks, I also extracted that10
fromif (value < 10) ...
to abrightness_min
constant.
â rid
Jul 17 at 5:08
add a comment |Â
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;
1
Nice, thanks, I also extracted that10
fromif (value < 10) ...
to abrightness_min
constant.
â rid
Jul 17 at 5:08
add a comment |Â
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;
1
Nice, thanks, I also extracted that10
fromif (value < 10) ...
to abrightness_min
constant.
â rid
Jul 17 at 5:08
add a comment |Â
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;
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;
answered Jul 17 at 2:56
chux
11.4k11238
11.4k11238
1
Nice, thanks, I also extracted that10
fromif (value < 10) ...
to abrightness_min
constant.
â rid
Jul 17 at 5:08
add a comment |Â
1
Nice, thanks, I also extracted that10
fromif (value < 10) ...
to abrightness_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
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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 likefail()
, 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 handlesCtrl+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 likehandle_video_event()
handles brightness.â rid
Jul 17 at 5:12