1#!/usr/bin/env ruby
2#
3# ixset --
4# A nice interface to "xset" to change X server settings
5#
6
7require 'tk'
8
9class Xsettings
10  #
11  # Button actions
12  #
13  def quit
14    @root.destroy
15  end
16
17  def ok
18    writesettings
19    quit
20  end
21
22  def cancel
23    readsettings
24    dispsettings
25    @btn_APPLY.state(:disabled)
26    @btn_CANCEL.state(:disabled)
27  end
28
29  # apply is just "writesettings"
30  def apply
31    writesettings
32    @btn_APPLY.state(:disabled)
33    @btn_CANCEL.state(:disabled)
34  end
35
36  #
37  # Read current settings
38  #
39  def readsettings
40    xfd = open("|xset q", 'r')
41    xfd.readlines.each{|line|
42      fields = line.chomp.strip.split(/\s+/)
43      case fields[0]
44      when "auto"
45	if fields[1] == 'repeat:'
46	  @kbdrep = fields[2]
47	  @w_kbdrep.set(@kbdrep)
48	  @kbdcli = fields[6]
49	end
50
51      when "bell"
52	@bellvol = fields[2]
53	@bellpit = fields[5]
54	@belldur = fields[8]
55
56      when "acceleration:"
57	@mouseacc = fields[1]
58	@mousethr = fields[3]
59
60      when "prefer"
61	if fields[2] == 'yes'
62	  @screenbla = 'blank'
63	else
64	  @screenbla = 'noblank'
65	end
66	@w_screenbla.set(@screenbla)
67
68      when "timeout:"
69	@screentim = fields[1]
70	@screencyc = fields[3]
71
72      end
73    }
74
75    xfd.close
76  end
77
78  #
79  # Write settings into the X server
80  #
81  def writesettings
82    @bellvol = @w_bellvol.get
83    @bellpit = @w_bellpit.get
84    @belldur = @w_belldur.get
85
86    @kbdrep = @w_kbdrep.get
87    if @kbdrep == 'on'
88      @kbdcli = @w_kbdcli.get
89    else
90      @kbdcli = 'off'
91    end
92
93    @mouseacc = @w_mouseacc.get
94    @mousethr = @w_mousethr.get
95
96    @screentim = @w_screentim.get
97    @screencyc = @w_screencyc.get
98    @screenbla = @w_screenbla.get
99
100    system("xset \
101            b #{@bellvol} #{@bellpit} #{@belldur} \
102            c #{@kbdcli} \
103            r #{@kbdrep} \
104            m #{@mouseacc} #{@mousethr} \
105            s #{@screentim} #{@screencyc} \
106            s #{@screenbla}")
107  end
108
109  #
110  # Sends all settings to the window
111  #
112  def dispsettings
113    @w_bellvol.set(@bellvol)
114    @w_bellpit.set(@bellpit)
115    @w_belldur.set(@belldur)
116
117    @w_kbdonoff.set(@w_kbdrep.get)
118    @w_kbdcli.set(@kbdcli)
119
120    @w_mouseacc.set(@mouseacc)
121    @w_mousethr.set(@mousethr)
122
123    @w_screenblank.set(@w_screenbla.get)
124    @w_screenpat.set(@w_screenbla.get)
125
126    @w_screentim.set(@screentim)
127    @w_screencyc.set(@screencyc)
128  end
129
130  #
131  # Create all windows, and pack them
132  #
133  class LabelEntry
134    def initialize(parent, text, length, range=[])
135      @frame = TkFrame.new(parent)
136      TkLabel.new(@frame, 'text'=>text).pack('side'=>'left')
137      if range.size > 0
138	@entry = TkSpinbox.new(@frame, 'width'=>length, 'relief'=>'sunken',
139			       'from'=>range[0], 'to'=>range[1])
140      else
141	@entry = TkEntry.new(@frame, 'width'=>length, 'relief'=>'sunken')
142      end
143      @entry.pack('side'=>'right','expand'=>'y', 'fill'=>'x')
144    end
145    def epath
146      @frame
147    end
148    def pack(keys)
149      @frame.pack(keys)
150    end
151    def get
152      @entry.value
153    end
154    def set(value)
155      @entry.delete(0,'end')
156      @entry.insert(0, value)
157    end
158  end
159
160  def createwindows
161    win = self
162
163    #
164    # Buttons
165    #
166    btn_frame = TkFrame.new(@root)
167    buttons = [
168      @btn_OK = TkButton.new(btn_frame, 'command'=>proc{win.ok},
169			     'default'=>'active', 'text'=>'Ok'),
170      @btn_APPLY = TkButton.new(btn_frame, 'command'=>proc{win.writesettings},
171				'default'=>'normal', 'text'=>'Apply',
172				'state'=>'disabled'),
173      @btn_CANCEL = TkButton.new(btn_frame, 'command'=>proc{win.cancel},
174				 'default'=>'normal', 'text'=>'Cancel',
175				'state'=>'disabled'),
176      @btn_QUIT = TkButton.new(btn_frame, 'command'=>proc{win.quit},
177			       'default'=>'normal', 'text'=>'Quit')
178    ]
179    buttons.each{|b| b.pack('side'=>'left', 'expand'=>'yes', 'pady'=>5) }
180
181    @root.bind('Return', proc{@btn_OK.flash; @btn_OK.invoke})
182    @root.bind('Escape', proc{@btn_QUIT.flash; @btn_QUIT.invoke})
183    @root.bind('1', proc{|w|
184		 unless buttons.index(w)
185		   @btn_APPLY.state(:normal)
186		   @btn_CANCEL.state(:normal)
187		 end
188	       }, '%W')
189    @root.bind('Key', proc{|w, k|
190		 unless buttons.index(w)
191		   case k
192		   when 'Return', 'Escape', 'Tab', /.*Shift.*/
193		     # do nothing
194		   else
195		     @btn_APPLY.state(:normal)
196		     @btn_CANCEL.state(:normal)
197		   end
198		 end
199	       }, '%W %K')
200
201    #
202    # Bell settings
203    #
204    bell = TkLabelframe.new(@root, 'text'=>'Bell Settings',
205			    'padx'=>'1.5m', 'pady'=>'1.5m')
206    @w_bellvol = TkScale.new(bell, 'from'=>0, 'to'=>100, 'length'=>200,
207			     'tickinterval'=>20, 'orient'=>'horizontal',
208			     'label'=>"Volume (%)")
209
210    f = TkFrame.new(bell)
211    @w_bellpit = LabelEntry.new(f, "Pitch (Hz)", 6, [25, 20000])
212    @w_bellpit.pack('side'=>'left', 'padx'=>5)
213    @w_belldur = LabelEntry.new(f, "Duration (ms)", 6, [1, 10000])
214    @w_belldur.pack('side'=>'right', 'padx'=>5)
215
216    @w_bellvol.pack('side'=>'top', 'expand'=>'yes')
217    f.pack('side'=>'top', 'expand'=>'yes')
218
219    #
220    # Keyboard settings
221    #
222    kbdonoff = nil
223    kbdcli = nil
224    kbd = TkLabelframe.new(@root, 'text'=>'Keyboard Repeat Settings',
225			   'padx'=>'1.5m', 'pady'=>'1.5m')
226    f = TkFrame.new(kbd)
227    @w_kbdonoff = TkCheckButton.new(f, 'text'=>'On', 'relief'=>'flat',
228				    'onvalue'=>'on', 'offvalue'=>'off',
229				    'variable'=>@w_kbdrep ) {
230      def self.set(value)
231	if value == 'on'
232	  self.select
233	else
234	  self.deselect
235	end
236      end
237      pack('side'=>'left', 'expand'=>'yes', 'fill'=>'x', 'padx'=>[0, '1m'])
238    }
239    @w_kbdcli = TkScale.new(f, 'from'=>0, 'to'=>100, 'length'=>200,
240			 'tickinterval'=>20, 'orient'=>'horizontal',
241			 'label'=>'Click Volume (%)')
242    @w_kbdcli.pack('side'=>'left', 'expand'=>'yes',
243		   'fill'=>'x', 'padx'=>['1m', 0])
244    f.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2, 'fill'=>'x')
245
246    #
247    # Mouse settings
248    #
249    mouse = TkLabelframe.new(@root, 'text'=>'Mouse Settings',
250			     'padx'=>'1.5m', 'pady'=>'1.5m')
251    f = TkFrame.new(mouse)
252    @w_mouseacc = LabelEntry.new(f, 'Acceleration', 5)
253    @w_mouseacc.pack('side'=>'left', 'padx'=>[0, '1m'])
254    @w_mousethr = LabelEntry.new(f, 'Threshold (pixels)', 3, [1, 2000])
255    @w_mousethr.pack('side'=>'right', 'padx'=>['1m', 0])
256    f.pack('side'=>'top', 'expand'=>'yes')
257
258    #
259    # Screen Saver settings
260    #
261    screen = TkLabelframe.new(@root, 'text'=>'Screen-saver Settings',
262			     'padx'=>'1.5m', 'pady'=>'1.5m')
263    @w_screenblank = TkRadioButton.new(screen, 'text'=>'Blank',
264				       'relief'=>'flat', 'anchor'=>'w',
265				       'variable'=>@w_screenbla,
266				       'value'=>'blank') {
267      def self.set(value)
268	if value == 'blank'
269	  self.select
270	else
271	  self.deselect
272	end
273      end
274    }
275
276    @w_screenpat = TkRadioButton.new(screen, 'text'=>'Pattern',
277				     'relief'=>'flat', 'anchor'=>'w',
278				     'variable'=>@w_screenbla,
279				     'value'=>'noblank') {
280      def self.set(value)
281	if value != 'blank'
282	  self.select
283	else
284	  self.deselect
285	end
286      end
287    }
288
289    @w_screentim = LabelEntry.new(screen, 'Timeout (s)', 5, [1, 100000])
290    @w_screencyc = LabelEntry.new(screen, 'Cycle (s)', 5, [1, 100000])
291
292    Tk.grid(@w_screenblank, @w_screentim, 'sticky'=>'e')
293    Tk.grid(@w_screenpat, @w_screencyc, 'sticky'=>'e')
294    TkGrid.configure(@w_screenblank, @w_screenpat, 'sticky'=>'ew')
295
296    #
297    # Main window
298    #
299    param = {
300      'side'=>'top', 'fill'=>'both', 'expand'=>'yes',
301      'padx'=>'1m', 'pady'=>'1m'
302    }
303    btn_frame.pack('side'=>'top', 'fill'=>'both')
304    bell.pack(param)
305    kbd.pack(param)
306    mouse.pack(param)
307    screen.pack(param)
308
309    #
310    # Let the user resize our window
311    #
312    @root.minsize(10,10)
313  end
314
315  def initialize(title)
316    @root = TkRoot.new('title'=>title)
317
318    @kbdrep = 'on'
319    @w_kbdrep = TkVariable.new(@kbdrep)
320    def @w_kbdrep.get
321      self.value
322    end
323    def @w_kbdrep.set(val)
324      self.value=val
325    end
326
327    @kbdcli = 0
328
329    @bellvol = 100
330    @bellpit = 440
331    @belldur = 100
332
333    @mouseacc = "3/1"
334    @mousethr = 4
335
336    @screenbla = "blank"
337    @w_screenbla = TkVariable.new(@screenbla)
338    def @w_screenbla.get
339      self.value
340    end
341    def @w_screenbla.set(val)
342      self.value=val
343    end
344
345    @screentim = 600
346    @screencyc = 600
347
348    #
349    # Listen what "xset" tells us...
350    #
351    readsettings
352
353    #
354    # Create all windows
355    #
356    createwindows
357
358    #
359    # Write xset parameters
360    #
361    dispsettings
362  end
363end
364
365Xsettings.new(File.basename($0,'.rb'))
366
367Tk.mainloop
368