試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

X Window Systemでnvidia,fglrx以外のドライバを用いた環境における明るさとガンマの調整について(ページ3/3)

X Window Systemでnvidia,fglrx以外のドライバを用いた環境における明るさとガンマの調整について(ページ2/3)」の続き。

自作の明るさ/ガンマ調整ツールのコード

ページ2のメモの内容を踏まえ、明るさと各色成分ごとのガンマの両方を同時に設定できるCLIツールを作成したものを下に貼り付ける。ライセンスはMITとする。
コードはPythonで記述しており、実行属性を付けて実行するだけでコンパイルの作業は必要ない(動作もOSやアーキテクチャに非依存となる)が

  • libXxf86vm(/usr/lib(64)/libXxf86vm.so.1)
  • バージョンが2.6.x/2.7.x/3.x.xのいずれかのPython

の両方が必要となる。ただ、GUI環境込みでインストールされたGNU/Linuxにおいてはどちらもインストール済みのことが多い。
[任意]ファイル名: xbrightness-rgbgamma.py ライセンス: MIT

#! /usr/bin/python
#
# X11 brightness / gamma adjustment tool
# version 20110505
# (C) 2011 kakurasan
# Licensed under MIT
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# THANKS:
#  xbrightness by Asher Blum and Jean-Daniel Pauget

from __future__ import print_function  # for 2.6/2.7

import ctypes
import math
import sys
import os


class GetGammaRampSizeError (Exception):
  pass

class GetGammaRampError (Exception):
  pass

class SetGammaRampError (Exception):
  pass

class XOpenDisplayError (Exception):
  pass

class Libxxf86vm:
  try:
    __lib = ctypes.CDLL ('libXxf86vm.so.1')
  except OSError as e:
    print ('Error: {0}'.format (e), file=sys.stderr)
    sys.exit (1)
  __ramp_size = ctypes.c_int ()
  def __init__ (self):
    # XOpenDisplay(): Display *
    self.__display = self.__lib.XOpenDisplay (None)  # envar "DISPLAY"
    if not self.__display:
      raise XOpenDisplayError ()
    # XDefaultScreen: Display *
    self.__screen = self.__lib.XDefaultScreen (self.__display)
    # XF86VidModeGetGammaRampSize(): Display *, int, int *
    ret = self.__lib.XF86VidModeGetGammaRampSize (self.__display, self.__screen, ctypes.byref (self.__ramp_size))
    if not ret:
      raise GetGammaRampSizeError ()
    Ramp = ctypes.c_ushort * self.__ramp_size.value  # type
    (self.__ramp_red, self.__ramp_green, self.__ramp_blue) = (Ramp (), Ramp (), Ramp ())
    # XF86VidModeGetGammaRamp(): Display *, int, int,
    #                            unsigned short *, unsigned short *, unsigned short *
    ret = self.__lib.XF86VidModeGetGammaRamp (self.__display, self.__screen, self.__ramp_size, self.__ramp_red, self.__ramp_green, self.__ramp_blue)
    if not ret:
      raise GetGammaRampError ()
    # max output -> brightness
    self.__brightness = self.__ramp_red[self.__ramp_size.value - 1]
  def __del__ (self):
    if self.__display:
      # XCloseDisplay(): Display *
      self.__lib.XCloseDisplay (self.__display)
  def set_gamma (self, gamma_red, gamma_green, gamma_blue):
    self.set_gamma_and_brightness (self.__brightness, gamma_red, gamma_green, gamma_blue)
  def set_brightness (self, brightness):
    # calculate current gamma
    gamma_red = 1.0 / math.log (self.__ramp_red[self.__ramp_size.value / 2] / float (self.__brightness), (self.__ramp_size.value / 2) / float (self.__ramp_size.value - 1))
    gamma_green = 1.0 / math.log (self.__ramp_green[self.__ramp_size.value / 2] / float (self.__brightness), (self.__ramp_size.value / 2) / float (self.__ramp_size.value - 1))
    gamma_blue = 1.0 / math.log (self.__ramp_blue[self.__ramp_size.value / 2] / float (self.__brightness), (self.__ramp_size.value / 2) / float (self.__ramp_size.value - 1))
    #gamma_red = 1.0 / (math.log (self.__ramp_red[self.__ramp_size.value / 2] / float (self.__brightness)) /  math.log ((self.__ramp_size.value / 2) / float (self.__ramp_size.value - 1)))
    #gamma_green = 1.0 / (math.log (self.__ramp_green[self.__ramp_size.value / 2] / float (self.__brightness)) /  math.log ((self.__ramp_size.value / 2) / float (self.__ramp_size.value - 1)))
    #gamma_blue = 1.0 / (math.log (self.__ramp_blue[self.__ramp_size.value / 2] / float (self.__brightness)) / math.log ((self.__ramp_size.value / 2) / float (self.__ramp_size.value - 1)))
    #print ('gamma_red:{0} gamma_green:{1} gamma_blue:{2}'.format (gamma_red, gamma_green, gamma_blue))
    self.set_gamma_and_brightness (brightness, gamma_red, gamma_green, gamma_blue)
  def set_gamma_and_brightness (self, brightness, gamma_red, gamma_green, gamma_blue):
    for i in range (self.__ramp_size.value):
      p = i / float (self.__ramp_size.value - 1)
      self.__ramp_red[i] = ctypes.c_ushort (int (brightness * pow (p, 1 / gamma_red)))
      self.__ramp_green[i] = ctypes.c_ushort (int (brightness * pow (p, 1 / gamma_green)))
      self.__ramp_blue[i] = ctypes.c_ushort (int (brightness * pow (p, 1 / gamma_blue)))
      #print ('i:{0:3d} R:{1:5d} G:{2:5d} B:{3:5d}'.format (i, self.__ramp_red[i], self.__ramp_green[i], self.__ramp_blue[i]))
    # XF86VidModeSetGammaRamp(): Display *, int, int,
    #                            unsigned short *, unsigned short *, unsigned short *
    ret = self.__lib.XF86VidModeSetGammaRamp (self.__display, self.__screen, self.__ramp_size, self.__ramp_red, self.__ramp_green, self.__ramp_blue)
    if not ret:
      raise SetGammaRampError ()
    self.__brightness = brightness
    (self.__gamma_red, self.__gamma_green, self.__gamma_blue) = (gamma_red, gamma_green, gamma_blue)


# main
if len (sys.argv) < 2:
  print ('''
USAGE: {0} [BRIGHTNESS]
       {0} [BRIGHTNESS] [GAMMA]
       {0} [BRIGHTNESS] [GAMMA_RED] [GAMMA_GREEN] [GAMMA_BLUE]
       {0} [GAMMA_RED] [GAMMA_GREEN] [GAMMA_BLUE]

BRIGHTNESS: 0 - 65535
GAMMA,GAMMA_RED,GAMMA_GREEN,GAMMA_BLUE: 0.000..1 - 10'''.format (__file__))
  sys.exit (0)

if len (sys.argv) == 4:
  # gamma (red + green + blue)
  gamma_red = sys.argv[1]
  gamma_green = sys.argv[2]
  gamma_blue = sys.argv[3]
else:
  brightness = sys.argv[1]
  if len (sys.argv) == 3:
    # brightness + gamma (all)
    gamma_red = gamma_green = gamma_blue = sys.argv[2]
  elif len (sys.argv) == 5:
    # brightness + gamma (red + green + blue)
    gamma_red = sys.argv[2]
    gamma_green = sys.argv[3]
    gamma_blue = sys.argv[4]

if len (sys.argv) != 4:
  # check invalid brightness
  try:
    brightness = int (brightness)
  except ValueError:
    print ('Error: Invalid value "{0}"'.format (brightness), file=sys.stderr)
    sys.exit (1)
if len (sys.argv) > 2:
  # check invalid gamma
  try:
    gamma_red = float (gamma_red)
  except ValueError:
    print ('Error: Invalid value "{0}"'.format (gamma_red), file=sys.stderr)
    sys.exit (1)
  try:
    gamma_green = float (gamma_green)
  except ValueError:
    print ('Error: Invalid value "{0}"'.format (gamma_green), file=sys.stderr)
    sys.exit (1)
  try:
    gamma_blue = float (gamma_blue)
  except ValueError:
    print ('Error: Invalid value "{0}"'.format (gamma_blue), file=sys.stderr)
    sys.exit (1)

  # check range
  if gamma_red <= 0 or gamma_green <= 0 or gamma_blue <= 0:
      print ('Error: Value must be greater than 0', file=sys.stderr)
      sys.exit (1)
  if gamma_red > 10 or gamma_green > 10 or gamma_blue > 10:
      print ('Error: Value must be less or equal to 10', file=sys.stderr)
      sys.exit (1)

# start
try:
  lib = Libxxf86vm ()
except XOpenDisplayError:
  print ('Error: Could not open display: "{0}"'.format (os.environ['DISPLAY']), file=sys.stderr)
  sys.exit (1)
except GetGammaRampSizeError:
  print ('Error: XF86VidModeGetGammaRampSize() failed', file=sys.stderr)
  sys.exit (1)
except GetGammaRampError:
  print ('Error: XF86VidModeGetGammaRamp() failed', file=sys.stderr)
  sys.exit (1)
try:
  if len (sys.argv) == 2:
    lib.set_brightness (brightness)
  elif len (sys.argv) == 4:
    lib.set_gamma (gamma_red, gamma_green, gamma_blue)
  else:  # 3 or 5
    lib.set_gamma_and_brightness (brightness, gamma_red, gamma_green, gamma_blue)
except SetGammaRampError:
  print ('Error: XF86VidModeSetGammaRamp() failed', file=sys.stderr)
  sys.exit (1)

(2011/5/5)ディスプレイが開けなかった場合の処理を修正
下は使用法。明るさは0-65535の範囲の整数,ガンマ補正値は0より大きく10以下の値を指定する。

(明るさのみ変更・ガンマ補正値は変更しない)
$ [xbrightness-rgbgamma.pyの場所] [明るさ]

(明るさと同時に全ての色成分で共通のガンマ補正値を変更)
$ [xbrightness-rgbgamma.pyの場所] [明るさ] [全ての色成分で共通のガンマ補正値]

(明るさと同時に各色成分のガンマ補正値を変更)
$ [xbrightness-rgbgamma.pyの場所] [明るさ] [赤成分のガンマ補正値] [緑成分のガンマ補正値] [青成分のガンマ補正値]

(各色成分のガンマ補正値のみを変更・明るさは変更しない)
$ [xbrightness-rgbgamma.pyの場所] [赤成分のガンマ補正値] [緑成分のガンマ補正値] [青成分のガンマ補正値]

関連記事:

使用したバージョン:

  • Python 2.6.6, 3.1.2
  • libXxf86vm 1.1.0