LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software
User Name
Password
Linux - Software This forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.

Notices


Reply
  Search this Thread
Old 10-08-2014, 12:59 PM   #1
nokangaroo
Member
 
Registered: Nov 2009
Posts: 141

Rep: Reputation: 25
A simple C program to control monitor brightness


NOTE: I think this program is very useful, as many people seem to have trouble
with backlight control (xbacklight does not work for me or I'd use it, and I
remember trying nvidia-bl on the mbp, and it didn't work either. mate-power-
manager will work but not provide a keyboard shortcut, and it is quite useless
for anything else). I'm not a professional programmer though, so if it could
be improved please correct. I am already using it, so far without accident, and
my box seems to run cooler without the graphics running at full blast.

It compiles without warnings with -Wall, and that's as well because it needs to
be setuid root to work.

Install:
Code:
gcc -Wall -o <name> </path/to/source> # try also -std=c99 and -std=gnu99
sudo mv <name> <destdir> # /usr/local/bin or /usr/local/sbin
sudo chown root:users </path/to/binary>
sudo chmod 550 </path/to/binary>
sudo chmod u+s </path/to/binary>
#After this your program should wear the scarlet letter:
ls -l <path/to/binary>
To set the level at login (if needed) create a startup application:
</path/to/program>

The command for XF86MonBrightnessUp or XF86KbdBrightnessUp:
</path/to/program> +

The command for XF86MonBrightnessDown or XF86KbdBrightnessDown:
</path/to/program> -


To run this program at resuming from suspend with systemd (if needed), put the
following in /lib/systemd/system-sleep/resume-brightness.sh:

Code:
#!/bin/sh
case $1/$2 in
post/*)
user=`last | grep tty | grep -v root | head -n 1 | cut -d \  -f 1`
runuser -u $user </path/to/program>		#sudo also works
;;
esac


Here's the code. DISCLAIMER: This program can be dangerous if it misbehaves.
I am not responsible for frying your hardware.

Code:
/* Set display brightness at the hardware level
Intended for use with keyboard shortcuts, therefore prints no messages to stdout

Can also be used for keyboard backlight control; just set the correct defines
and a sensible lower level > 0

Needs root privileges; make it setuid (for per-user rcfile) or add the following
to /etc/sudoers to be able to assign keyboard shortcuts and a startup object:
<user> <hostname>=NOPASSWD: </path/to/this/program>

The defines are for my own boxes; adjust as needed. MINLEVEL depends on the
driver, and also on FACTOR if you want to avoid uneven step sizes

Tested values for MINLEVEL (all too dark to be of use):
iMac7.1 (radeon):		36
mbp5.1 (nvidia):		1000
mbp5.1 (applesmc-keyboard):	5
*/

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

#ifndef _GNU_SOURCE
#include <libgen.h> // if using POSIX basename lose the _GNU_SOURCE
#endif

//comment all out for testing:
//#define imac
//#define mbp
//#define mbp_kbd

#if defined imac 
#define BRIGHTNESS "/sys/class/backlight/radeon_bl0/brightness"
#define MAX_BRIGHTNESS "/sys/class/backlight/radeon_bl0/max_brightness"
#define MINLEVEL 40.4
#define FACTOR 1.122 // that's (255/36)^(1/17); 36 is the lowest possible value
#define RC_DIR "/usr/local/etc"

#elif defined mbp 
#define BRIGHTNESS "/sys/class/backlight/gmux_backlight/brightness"
#define MAX_BRIGHTNESS "/sys/class/backlight/gmux_backlight/max_brightness"
#define MINLEVEL 1236.6
#define FACTOR 1.122
#define RC_DIR "/usr/local/etc"

#elif defined mbp_kbd
#define BRIGHTNESS "/sys/devices/platform/applesmc.768/leds/smc::kbd_backlight/brightness"
#define MAX_BRIGHTNESS "/sys/devices/platform/applesmc.768/leds/smc::kbd_backlight/max_brightness"
#define MINLEVEL 6.5
#define FACTOR 1.14
#define RC_DIR "/usr/local/etc"

#else //for testing (STRONGLY recommended until everything works):
#define BRIGHTNESS "/tmp/brightness"
#define MAX_BRIGHTNESS "/tmp/max_brightness" // create it
#define MINLEVEL 40.4
#define FACTOR 1.122
#define RC_DIR "/tmp" 
#endif

int main(int argc, char *argv[]) {
	FILE *brightness;
	FILE *max_brightness;
	FILE *rcfile;
	FILE *rcfile0;
	double level;
	int user = getuid();
	char *path = RC_DIR;
	// 8 = max 5 digits UID + '/' + '-' + '\0':
	char rc[(strlen(path)) + (strlen(basename(argv[0]))) + 8];
	// path to rcfile:
	snprintf(rc, (sizeof(rc)), "%s/%s-%d", path, (basename(argv[0])), user);
	/* Of course memory is not actually an issue:
	char rc[100];
	snprintf(rc, 100, "%s/%s-%d", path, (basename(argv[0])), user);	*/
	max_brightness = fopen(MAX_BRIGHTNESS, "r");
	// if max_brightness does not exist, abort
	// (most likely the defines are wrong):
	if (max_brightness == NULL)
		exit(EXIT_FAILURE);
	// get maximum brightness level:
	char maxvalue[20];
	fgets(maxvalue, 20, max_brightness);
	
	rcfile0 = fopen(rc, "r");
	if (rcfile0 == NULL)
		// if rcfile does not exist, use max_brightness:
		level = strtod(maxvalue, NULL);
	else {
		// read level from rcfile:
		char value[20];
		fgets(value, 20, rcfile0);
		level = strtod(value, NULL);
		}

	switch (argc) {
	case 1:
	// print level to file for startup application
	break;
	case 2:
		switch (argv[1][0]) {
		case '+':
		// this seems to be the way the backlight works; with an
		// additive constant the jumps get bigger as the screen gets darker:
		level = level * FACTOR;
		break;
		case '-':
		level = level / FACTOR;
		break;
		default:
		exit(EXIT_FAILURE);
		}
	break;
	default:
	exit(EXIT_FAILURE);
	} 
	
	// keep levels between limits:
	if (level >= (strtod(maxvalue, NULL) - 1)) // fun with floating point
		level = strtod(maxvalue, NULL);
	if (level < MINLEVEL)
		level = MINLEVEL;
	brightness = fopen(BRIGHTNESS, "w");
	fprintf(brightness, "%ld", (long) level); // "%ld\n" makes no difference
	rcfile = fopen(rc, "w");
	fprintf(rcfile, "%lf", level);
	exit(EXIT_SUCCESS); // exit closes all open files
}
 
Old 10-10-2014, 10:27 AM   #2
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,319
Blog Entries: 13

Rep: Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372
First, nice job.

I don't have much use for it, but you're finding it suitable for you therefore great.

I don't understand how this doesn't cause problems though:
Code:
	char rc[(strlen(path)) + (strlen(basename(argv[0]))) + 8];
	// path to rcfile:
	snprintf(rc, (sizeof(rc)), "%s/%s-%d", path, (basename(argv[0])), user);
	/* Of course memory is not actually an issue:
	char rc[100];
	snprintf(rc, 100, "%s/%s-%d", path, (basename(argv[0])), user);	*/
You probably should not re-use a local variable "name", even though the compiler seems to take care of scope here; going beyond this point the second declaration of rc would be the only one used is my interpretation, thus meaning that the first declaration and result of the first snprintf() is just discarded. I'm also not too sure how it accepts you declaring an array with indeterminate sizes in that first one. It does not give me warnings even with added compilation flags beyond -Wall.

Code:
	max_brightness = fopen(MAX_BRIGHTNESS, "r");
	// if max_brightness does not exist, abort
	// (most likely the defines are wrong):
	if (max_brightness == NULL)
		exit(EXIT_FAILURE);
	// get maximum brightness level:
	char maxvalue[20];
	fgets(maxvalue, 20, max_brightness);
What happens if the fgets() doesn't get you anything? For instance what if the file has no data, or invalid data? You just "use" maxvalue later in your program, granted you later check "level" therefore if the strtod() call gave you garbage you would detect it.

I might print out using printf() to indicate what problems were detected, or cases where an override or default was chosen due to invalid values. An alternative is that I would choose to output data to a log file in lieu of printf() to stdout.

And finally, "exit closes all open files":

Yeah it does. I'd close my files specifically and more specifically after I'd completed using them.

In all, not bad, the whole intentions is that it does something "you" wanted it to do, and it works for you. Those are key things.
 
1 members found this post helpful.
Old 11-02-2014, 12:09 PM   #3
nokangaroo
Member
 
Registered: Nov 2009
Posts: 141

Original Poster
Rep: Reputation: 25
Thank you, rtmistler. I'm glad that somebody actually reads my posts besides the NSA.

The program is supposed to exit if maxvalue does not exist, and it does that. I used it in test mode before making it suid.

The actual problem I ran into is covered by the reply I prepared, which follows. If you find fault with it please comment.


Edit:
I noticed that if I change the screen brightness under high disk I/O load
(backing up with rsync) by holding the brightness key down instead of stepping
the brightness, the brightness level sometimes jumps to minimum and needs to be
stepped back up slowly. This happens on both my boxes. I don't know what causes
this; merely cleaning up my act and inserting proper fclose() statements wasn't
enough. It's no more than annoying, it never crashes the driver, and there seems
to be a solution.

So here is a version that uses an rcfile in /tmp (which is in memory and should
therefore be independent of disk I/O):

Code:
/* Set display brightness at the hardware level
Intended for use with keyboard shortcuts, therefore prints no messages to stdout

Can also be used for keyboard backlight control; just set the correct defines
and a sensible lower level > 0

Needs root privileges; make it setuid (for per-user rcfile) or add the following
to /etc/sudoers to be able to assign keyboard shortcuts and a startup object:
<user> <hostname>=NOPASSWD: </path/to/this/program>

MINLEVEL depends on the driver, and also on FACTOR if you want to avoid uneven
step sizes

Tested values for MINLEVEL:
iMac7.1 (radeon):       36
mbp5.1 (nvidia):        1000
mbp5.1 (applesmc-kbd):  5

Install:
gcc -Wall [-D{imac,mbp,mbp_kbd}] -o <name> </path/to/source>
sudo mv <name> <destdir>
sudo chown root:users </path/to/binary>
sudo chmod 4550 </path/to/binary>
*/



#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>

#ifndef _GNU_SOURCE
#include <libgen.h> // if using POSIX basename lose the _GNU_SOURCE
#endif

//comment all out for testing:
//#define imac
//#define mbp
//#define mbp_kbd

#if defined imac 
#define BRIGHTNESS "/sys/class/backlight/radeon_bl0/brightness"
#define MAX_BRIGHTNESS "/sys/class/backlight/radeon_bl0/max_brightness"
#define MINLEVEL 40.4
#define FACTOR 1.122
#define RC_DIR "/usr/local/etc"

#elif defined mbp 
#define BRIGHTNESS "/sys/class/backlight/gmux_backlight/brightness"
#define MAX_BRIGHTNESS "/sys/class/backlight/gmux_backlight/max_brightness"
#define MINLEVEL 1746.6
#define FACTOR 1.122
#define RC_DIR "/usr/local/etc"

#elif defined mbp_kbd
#define BRIGHTNESS "/sys/devices/platform/applesmc.768/leds/smc::kbd_backlight/brightness"
#define MAX_BRIGHTNESS "/sys/devices/platform/applesmc.768/leds/smc::kbd_backlight/max_brightness"
#define MINLEVEL 6.5
#define FACTOR 1.14
#define RC_DIR "/usr/local/etc"

#else //for testing (STRONGLY recommended until everything works):
#define BRIGHTNESS "/tmp/brightness"
#define MAX_BRIGHTNESS "/tmp/max_brightness" // create it
#define MINLEVEL 40.4
#define FACTOR 1.122
#define RC_DIR "/tmp/testing" // create it
#endif



int main(int argc, char *argv[]) {
    FILE *max_brightness;
    max_brightness = fopen(MAX_BRIGHTNESS, "r");
    // if max_brightness does not exist, abort
    // (most likely the defines are wrong):
    if (max_brightness == NULL)
        exit(EXIT_FAILURE);
    FILE *brightness;
    FILE *rcfile;
    double level;
    int user = getuid();
    // using /tmp for storing value (which is in memory and should therefore be
    // independent of disk I/O (12 = max 5 digits UID + '/tmp/' + '-' + '\0'):
    char rc0[(strlen(basename(argv[0]))) + 12];
    char *path = RC_DIR;
    // 8 = max 5 digits UID + '/' + '-' + '\0':
    char rc[(strlen(path)) + (strlen(basename(argv[0]))) + 8];
    // path to rcfile:
    snprintf(rc, (sizeof(rc)), "%s/%s-%d", path, (basename(argv[0])), user);
	snprintf(rc0, (sizeof(rc0)), "%s%s-%d", "/tmp/", (basename(argv[0])), user);
	
    // get maximum brightness level:
    char maxvalue[20];
    fgets(maxvalue, 20, max_brightness);
    fclose(max_brightness);
	
	// try /tmp, RC_DIR and max_brightness, in that order (this requires extra
	// code to store brightness levels at shutdown and reboot):
    rcfile = fopen(rc0, "r");
    if (rcfile == NULL)
        rcfile = fopen(rc, "r");
    if (rcfile == NULL)
       // if rcfile does not exist, use max_brightness:
       level = strtod(maxvalue, NULL);
    else {  
        // read level from rcfile:
        char value[20];
        fgets(value, 20, rcfile);
        level = strtod(value, NULL);
        fclose(rcfile);
    }
    
    switch (argc) {
    case 1:
    // print level to file for startup application
    break;
    case 2:
        switch (argv[1][0]) {
        case '+':
        // this seems to be the way the backlight works; with an
        // additive constant the jumps get bigger as the screen gets darker:
        level = level * FACTOR;
        break;
        case '-':
        level = level / FACTOR;
        break;
        default:
        exit(EXIT_FAILURE);
        }
    break;
    default:
    exit(EXIT_FAILURE);
    } 
	
    // keep levels between limits:
    if (level >= (strtod(maxvalue, NULL) - 1)) // fun with floating point
        level = strtod(maxvalue, NULL);
    if (level < MINLEVEL)
        level = MINLEVEL;
    brightness = fopen(BRIGHTNESS, "w");
    fprintf(brightness, "%ld", (long) level);
    fclose(brightness);
    rcfile = fopen(rc0, "w");
    fprintf(rcfile, "%lf", level);
    fclose(rcfile);
    // it should be accessible only by root, but the file in RC_DIR must be
    // readable by the user; the reboot/shutdown service will have to provide
    // that:
    chmod(rc0, 00600);
    exit(EXIT_SUCCESS);
}
For systemd, a service that copies the tmpfile to RC_DIR before shutdown and
reboot could look like this (counter-intuitive: WantedBy=shutdown.target and/or
WantedBy=reboot.target won't work):

Code:
[Unit]
Description=Run custom shutdown script
Before=shutdown.target
Before=reboot.target

[Service]
ExecStart=/usr/bin/true
ExecStop=/usr/local/sbin/rc.shutdown
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
The script rc.shutdown (you can call it what you want, and you can put any other
junk that you want to run at shutdown in it):
Code:
#!/bin/sh
for file in `find /tmp/* -name <name>* 2>/dev/null` ; do
user=`echo $file | sed 's/[^[:digit:]]//g'`
chown $user $file
mv -f $file /usr/local/etc
done
 
1 members found this post helpful.
Old 11-04-2014, 04:47 AM   #4
nokangaroo
Member
 
Registered: Nov 2009
Posts: 141

Original Poster
Rep: Reputation: 25
I've had time to think about your criticisms, rtmistler, and I'm not sure what
you mean. You seem to be complaining about code that's commented out
(char rc[100] is in a comment; it's an alternative that I tried. And
Code:
char rc[(strlen(path)) + (strlen(basename(argv[0]))) + 8];
is legal C99). And it's not like this program handles untested user input; if
max_brightness contains garbage you're in trouble before you even run it,
because that means your driver install is messed up. I suppose I could introduce
code to check if max_brightness contains only digits, but I'm not sure that's
where my efforts are best spent; a more likely source of trouble is when
MINLEVEL is actually greater than maxvalue (if I ran the program with the mbp
defines on the iMac all hell would break loose). So I would change the following
after the "keep levels between limits" comment:

Code:
//old:
if (level >= (strtod(maxvalue, NULL) - 1)) // fun with floating point
    level = strtod(maxvalue, NULL);
if (level < MINLEVEL)
    level = MINLEVEL;
//new:
if (level < MINLEVEL)  // no braces needed if there is only one command!
    level = MINLEVEL;
if (level >= (strtod(maxvalue, NULL) - 1))
    level = strtod(maxvalue, NULL);


I seem not to have expressed myself clearly, so some more exposition:
What this program does is to control the screen brightness if you don't find
anything that works in your distro's package manager, and it does so without
using xrandr. I tried xbacklight and nvidia-bl, and they didn't work. Besides,
xbacklight uses xrandr, which has the great advantage of not requiring root
privileges, but does not reduce power consumption when you dim the screen (and
I was tired of having my fans start blowing when I ran VLC with a few filters).
Of course, reducing the actual LED brightness may have its own issues, like
increased screen flicker, but so far I have noticed no adverse effects.

The rcfile name is of the form /tmp/<basename>-<user-id>, e.g. if you compiled
the program twice with according defines as /usr/local/bin/screen_brightness and
/usr/local/bin/keyboard_brightness, you will get /tmp/screen_brightness-1000
and /tmp/keyboard_brightness-1000 if user 1000 runs it. So what you want in
rc.shutdown is

Code:
#!/bin/sh
for file in `find /tmp/* -regex '.*[screen,keyboard]_brightness-[0-9]+' 2>/dev/null` ; do
user=`echo $file | sed 's/[^[:digit:]]//g'` #1000 in this example
chown $user $file
#chmod 400 $file # if you are extra paranoid
mv -f $file /usr/local/etc
done
I didn't want to use getenv() to place the file in the user's home dir, as it
doesn't really belong there; the user shouldn't modify it directly. Besides, if
I used getenv() in a setuid program a lot more people would start complaining
(the getenv manpage mentions a secure_getenv which deliberately strikes out when
the program is setuid). Better use hardcoded paths. There seems to be no issue
with using getuid().

Whether or not you actually need a file in /usr/local/etc for a startup object
depends on the driver; for instance, on the iMac (radeon driver with kernel mode
setting) the brightness level is preserved across reboots, so if you have only
one user you won't need a startup object, but you will need a service to restore
the brightness level after resuming from suspend (which can also be done with
systemd). On the mbp (nvidia binary driver with user mode setting) you will need
the startup object, but the driver remembers the brightness when you suspend.

This wasn't really a question, and I'll eventually mark it a solved if no more
glitches turn up.
 
1 members found this post helpful.
Old 11-04-2014, 12:40 PM   #5
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,319
Blog Entries: 13

Rep: Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372Reputation: 4372
Best of luck with the project. As said before, it's not something I had a need for. There wasn't any intentions to have comments be criticisms, it merely seemed as if you offered out the code, along with some disclaimers and statements that there might be better ways to do this. Rather than review the whole functional design of it I merely looked at the body of code and had some constructive thoughts based on my style. Yes, I do see now that the second declaration of rc is within a comment block.

A suggestion I offer is that once someone has a body of work such as this where they feel they have developed the concept fully and that it is a good example for others to consider, is perhaps to put it into a blog entry. As LQ members, we all have the capability to have our own blog entries. Sometimes I refer to some of blog entries as part of an offered answer, because there are examples in there as well as more detailed discussions.

I hope the coding project comes out satisfactorily for you.
 
1 members found this post helpful.
Old 11-05-2014, 09:16 PM   #6
XenaneX
Member
 
Registered: Jan 2009
Location: London
Distribution: MX16 & PCLOS
Posts: 181
Blog Entries: 5

Rep: Reputation: 22
The topic caught my eye and I read the post with interest. It is a little over my head but I might could figure out how to make it work, yet I'm a bit timid at this point. I really do need to dim this lcd monitor at times. I like what you have done and hope my commenting encourages you.
 
1 members found this post helpful.
Old 07-07-2017, 04:19 AM   #7
_XoNar
LQ Newbie
 
Registered: Jul 2011
Location: Alicante, SPAIN
Distribution: Knoppix forever
Posts: 7

Rep: Reputation: Disabled
Smile Thanks

Thank to everybody. I'm on my 50 and needed it most than ever. Please keep on coding and trying a simple intetrface. Thank U again.
 
Old 07-07-2017, 05:11 AM   #8
AwesomeMachine
LQ Guru
 
Registered: Jan 2005
Location: USA and Italy
Distribution: Debian testing/sid; OpenSuSE; Fedora; Mint
Posts: 5,513

Rep: Reputation: 1009Reputation: 1009Reputation: 1009Reputation: 1009Reputation: 1009Reputation: 1009Reputation: 1009Reputation: 1009
I feel I should comment on a few issues. The GPU and CPU do not require more power with a brighter screen. The screen brightness is controlled by a back light that has nothing to do with the GPU or CPU.

Dimming the display does not make the screen flicker. It has no effect on refresh rate.
 
Old 07-13-2017, 10:08 AM   #9
_XoNar
LQ Newbie
 
Registered: Jul 2011
Location: Alicante, SPAIN
Distribution: Knoppix forever
Posts: 7

Rep: Reputation: Disabled
Thank you again.

For my basic reading pourposes with LED based screens, I found Win+N combination in knoppix
that activates the negative colours in each program. I suppose a similar combination will exist
for other well-known distros. It will no dimm the screen bright, but probably will do the effect.
And I really still don't know about if it will have a better medical effect on our eye than nothing.

Cheers!

Last edited by _XoNar; 07-13-2017 at 10:11 AM.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Brightness control not working. Ipe Slackware 11 10-31-2012 11:01 AM
brightness control issue? vetrib2w Linux - Laptop and Netbook 3 04-13-2010 10:58 PM
Brightness control on laptops. YodaSlack Linux - Hardware 7 01-24-2008 02:56 PM
No more brightness/contrast control for me Sunny Rabbiera Mandriva 1 01-12-2008 12:09 AM
screen brightness control arunsri Slackware 4 10-20-2005 03:35 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Software

All times are GMT -5. The time now is 02:31 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration