#!/bin/sh
: ; exec klone $0 "$@"
; The above line finds the klone executable in the $PATH
;;Skeleton of a typical klone script

(setq args (getopts "USAGE: %0 [options] 
Filter: takes in input a XF86Config file and outputs it, modifying the
ModeLine declarations and printing the other lines untouched.

It reads the speficications of the monitor (BandWidth, HorizSync, VertRefresh)
and modifies the ModeLine declarations to adjust the numbers, based on the
resolution and desired refresh rate. bandwidth, refresh rate, and horiz freq
limits can also be set on the command lines. 

Usage is thus to \"clean\" a XFConfig file, for instance by:
    %0 < /etc/XF86Config.orig > /etc/XF86Config
in XF86Config.orig you can have generic modes such as:
  ModeLine \"1024x768\" x 1024 x x x 768 x x x
%0 will replace the x with good values
"
    ("-vf" Hz vertical-frequency 
      "forces vertical frequencies. Defaults to 75, which is the 
minimum for flicker-less display")
    ("-bw" Mhz M-BW "max limit to the bandwidth")
    ("-hf" Khz M-HF "max limit to the horiz ferquency")
    ("-d" XXXxYYYxFFF descs "takes a description of the form width x heigth x vfreq
such as -d 1024x768x75 and outputs the corresponding modeline
vertical frequency (FFF) can be omitted for default vertical 
frequency, if only width (XXX) is given, YYY will be computed for
the screen ratio (default 4/3, see -r option)"
      :multiple t)
    ("-r" ratio ratio "screen ratio. defaults to 4/3, can be 16/10, 16/9...")
    ("-v" () verbose "verbose operation")
    ("-a" algorithm  algo "use algorithm. Available ones:
  def  is the default. works will 17\", 21\", 16/10 24\"
  old  is the original one, was too agressive.")
    ("-c" N correction "relax the timings by N%. useful if your monitor
cannot stand the outputted modeline, try -c 10, then -c 20, etc...")
    ("-s" () simpler-modelines "Ouput simpler modelines: no decimals for bw,
no VF in mode name")
    ("-load" () is-loaded? "to be used in Klone scripts")
))

(setq vertical-frequency-default ())
(if vertical-frequency (setq vertical-frequency (Int vertical-frequency)))
(if (or (not vertical-frequency) (<= vertical-frequency 10)) (progn
    (setq vertical-frequency 75)
    (setq vertical-frequency-default t)
))
(if (not algo) (setq algo :def)
  (= "def" algo) (setq algo :def)
  (= "old" algo) (setq algo :old)
  t (progn
    (PF *standard-error*
      "Unknown algorithm argument to -a: \"%0\". Use only: def, old\n" algo
    )
    (getopts :usage)
))
(setq re-ratio (regcomp "^([0-9]+)[/]([0-9]+)$"))
(if (not ratio) (setq ratio [4 3])
  (regexec re-ratio ratio) (progn
    (setq ratio (vector (Int (regsub re-ratio 1))(Int (regsub re-ratio 2))))
  )
  t (progn
    (PF *standard-error*
      "Bad ratio format (-r): \"%0\". Use only: x/y\n" ratio
    )
    (getopts :usage)
))

(setq M-BW (if (not M-BW) *maxint* (Int M-BW)))
(setq M-HF (if (not M-HF) *maxint* (Int M-HF)))

(setq m-BW 0)				;minimum bandwidth, not used yet
(setq m-HF 0)				;minimum horiz ferquency, not used yet

(setq re (re-nocase "^[ \t]*modeline[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)"))

(setq re-bw (re-nocase "^[ \t]*bandwidth[ \t]+([0-9]+)"))
(setq re-hf (re-nocase "^[ \t]*horizsync[ \t]+[0-9]+[ \t]*-[ \t]*([0-9]+)"))
(setq re-vf (re-nocase "^[ \t]*vertrefresh[ \t]+[0-9]+[ \t]*-[ \t]*([0-9]+)"))

(setq re-desc (regcomp "(([0-9]+)x([0-9]+)x([0-9]+))"))
(setq re-desc-2 (regcomp "(([0-9]+)x([0-9]+))"))
(setq re-desc-3 (regcomp "([0-9]+)"))

(defun main (&aux
  )
  (if descs (dolist (desc descs)
      (if (regexec re-desc desc)	; WxHxF
	(process-modeline (+ "\"" (regsub re-desc 1) "\"")
	  (Int (regsub re-desc 2)) (Int (regsub re-desc 3))
	  (Int (regsub re-desc 4))
	) 
	(regexec re-desc-2 desc)	; WxH
	  (process-modeline (+ "\"" (regsub re-desc-2 2) "x" 
	      (regsub re-desc-2 3) "x%0\"")
	  (Int (regsub re-desc-2 2)) (Int (regsub re-desc-2 3))
	  vertical-frequency
	)
	(regexec re-desc-3 desc)	; W
	(with (x (round8 (regsub re-desc-3 1)) y 
	    (/ (* (getn ratio 1) x) (getn ratio 0)))
	  (process-modeline (+ "\"" (String x) "x" (String y) "x%0\"")
	    x y  vertical-frequency
	))
	(error "Bad mode description: %0, should be XXXxYYYxHH" desc)
    ))
    (catch 'EOF (while t (setq line (read-line))
	(if (regexec re line) (process-modeline 
	    (regsub re 1) (Int (regsub re 3))
	    (Int (regsub re 7)) vertical-frequency
	  )
	  (regexec re-bw line) (progn
	    (if (= M-BW *maxint*) (setq M-BW (Int (regsub re-bw 1))))
	    (write-line line)
	  )
	  (regexec re-hf line) (progn
	    (if (= M-HF *maxint*) (setq M-HF (Int (regsub re-hf 1))))
	    (write-line line)
	  )
	  (regexec re-vf line) (progn
	    (if vertical-frequency-default (setq vertical-frequency
		(Int (regsub re-vf 1))))
	    (write-line line)
	  )
	  (write-line line)
  ))))
)

;; Original algorithm:
;; variable names are based on the VideoModes.doc XFree86 document
;; HSF = (/ (/ DCF (+ x (* 7 D))) 1000)
;; VF = DCF / ( HFL * VFL)
;; My heuristics:
;; left pad = middle pad = 4% of width
;; rigth pad = 28% of width (I found that the right pad was the most important)

(defun compute-modes-old (x y VF)
  (setq D (round8 (* 0.04 x)))
  (setq x1 (+ x D))(setq x2 (+ x1 D))(setq HFL (+ x (* 7 D)))
  (setq y1 (+ y 2))(setq y2 (+ y 4))(setq VFL (+ y 42))
  (setq BW (/ (* VF (* HFL VFL)) 1000000.))
  (setq DCF (Int (* BW 1000000)))
  (setq HSF (/ (/ DCF HFL) 1000))
  (if verbose (PV D HFL VFL DCF HSF VF))
  (setq ok ())
)

;;New algorithm for sony 24"
;; HSF = (/ (/ DCF (+ x (* 7 D))) 1000)
;; VF = DCF / ( HFL * VFL)
;; My heuristics:
;; left pad = 1/24th of width
;; middle pad = 5/24th of width
;; total pad = 8.8 /24th of width (right pad is thus 2.8 / 24th)

(defun compute-modes-24 (x y VF)
  (setq D (round8 (/ x 24.)))
  (setq x1 (round8 (+ x (correct D))))
  (setq x2 (round8 (+ x  (correct (* 6 D)))))
  (setq HFL (round8 (+ x  (correct (* 8 D)))))
  (setq y1 (+ y (correct 2)))
  (setq y2 (+ y (correct 14)))
  (setq VFL (+ y (correct 40)))
  (setq BW (/ (* VF (* HFL VFL)) 1000000.))
  (setq DCF (Int (* BW 1000000)))
  (setq HSF (/ (/ DCF HFL) 1000))
  (if verbose (PV D HFL VFL DCF HSF VF))
  (setq ok ())
)

(setq compute-modes 
  (if (= algo :old) compute-modes-old
    compute-modes-24
))

;; this gets/sets global variables for easier handling if loaded into other 
;; klone scripts
;; Does not print anything if too low

(defun process-modeline (name x y vf &aux correct-ratio ok pname)
  (if correction 
    (progn
      (setq correct-ratio (+ 1 (/ (Real correction) 100)))
      (defun correct (x) (Int (+ (* correct-ratio x) 0.9)))
    )
    (defun correct (x) x)
  )
  (catch 'Done
    (setq VF vf)
    (compute-modes x y VF)
    (while (not ok)
      (setq ok t)
  ;;HSF too high?
      (if (> HSF M-HF) (progn
	  (setq HSF-o HSF)
	  (setq HFL (/ DCF (* 1000 M-HF)))
	  (setq VF (/ DCF (* HFL VFL)))
	  (verbose? "HorizSync %1 too high, max %2, setting vf to %0" 
	    VF HSF-o M-HF)
	  (compute-modes x y VF)
      ))
  ;;BW too high?
      (if (> BW M-BW) (progn
	  (setq BW-o BW)
	  (setq DCF (Int (* M-BW 1000000)))
	  (setq VF (/ DCF (* HFL VFL)))
	  (verbose? "Bandwidth %1 too high, max %2, setting vf to %0" 
	    VF BW-o M-BW)
	  (compute-modes x y VF)
      ))
    )
    ;;dont print anything if too low frequencies
    (if (< BW m-BW) (throw 'Done
	(verbose? "Bandwidth %0 too low, min %1, no modeline printed" BW m-BW)
      )
      (< HSF m-HF) (throw 'Done
	(verbose? "HorixFreq %0 too low, min %1, no modeline printed" HSF m-HF)
      )
      (progn				;OK, print
	(with (BWs (PF String "%0" BW) 
	    re-cut (regcomp "^([0-9]+[.][0-9][0-9]).")
	    re-int (regcomp "^([0-9]+)[.]00?$")
	  )
	  (if (regexec re-cut BWs) (setq BWs (regsub re-cut 1)))
	  (if (regexec re-int BWs) (setq BWs (regsub re-int 1)))
	  (setq pname (PF String name VF)) ;expand %0 in name to VF
	  (if simpler-modelines
	    (PF "  ModeLine %0 %1" 
	      (+ (match "^(\"[0-9]+x[0-9]+)" pname 1) "\"")
	      (match "^([^.]*)" BWs 1)
	    )
	    (PF "  ModeLine %0 %1" pname BWs)
	  )
	)
	(PF " %0 %1 %2 %3 %4 %5 %6 %7" x x1 x2 HFL y y1 y2 VFL)
	(PF " #%0Hz\n" VF)    
))))

(defun round8 (x)
  (setq x (Int x))
  (if (/= 0 (mod x 8)) (setq x (* 8 (+ 1 (/ x 8)))))
  x
)

(if (not is-loaded?) (main))

;;; EMACS MODES
;;; Local Variables: ***
;;; mode:lisp ***
;;; End: ***

