rewrite grml-x
[grml-x.git] / grml-x
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # vim: set et ts=4:
4
5 # Filename:      grml-x
6 # Purpose:       wrapper for startx on grml [providing new xconfiguration tool]
7 # Authors:       grml-team (grml.org), (c) Christian Hofstaedtler <ch@grml.org>
8 # Bug-Reports:   see http://grml.org/bugs/
9 # License:       This file is licensed under the GPL v2.
10 ###############################################################################
11
12 import os, subprocess, sys, tempfile, time, traceback
13 from optparse import OptionParser
14
15 class Section(object):
16     def __init__(self, name, identifier, data):
17         self.name = name
18         self.identifer = identifier
19         self.data = data
20         self.subsect = ""
21     def __str__(self):
22         s = "Section \"%s\"\n\tIdentifier \"%s\"\n" % (self.name, self.identifer)
23         for k in self.data:
24             v = self.data[k]
25             if isinstance(v, list):
26                 v = '" "'.join(v)
27             elif '-' in v: # sync range
28                 pass
29             else:
30                 v = '"%s"' % v
31             s += "\t%s %s\n" % (k, v)
32         s += self.subsect
33         s += 'EndSection\n'
34         return s
35
36 def get_monitor_section(options, force):
37     if not options.hsync and not options.vsync and not force:
38         return None
39     d = {}
40     d['HorizSync'] = options.hsync or '28.0 - 96.0'
41     d['VertRefresh'] = options.vsync or '50.0 - 60.0'
42     return Section('Monitor', 'Monitor0', d)
43
44 def get_device_section(options):
45     if not options.module:
46         return None
47     d = {}
48     d['Driver'] = options.module
49     d['VendorName'] = 'All'
50     d['BoardName'] = 'All'
51     return Section('Device', 'Card0', d)
52
53 def build_bootparams():
54     lines = []
55     def walk_bootparams_path(p):
56         try:
57             if not os.path.exists(p): return
58             for root, dirs, files in os.walk(p):
59                 for name in files:
60                     f = open(os.path.join(root, name))
61                     lines.extend(f.readlines())
62                     f.close()
63         except:
64             print 'W: Error while getting bootparams from %s' % p
65     f = open('/proc/cmdline')
66     lines.append(f.readline())
67     f.close()
68     walk_bootparams_path('/cdrom/bootparams')
69     walk_bootparams_path('/live/image/bootparams')
70     params = {}
71     for p in ' '.join(lines).split(' '):
72         if '=' in p:
73             (k,v) = p.split('=', 2)
74             params[k] = v
75         else:
76             params[p] = True
77     return params
78
79 def detect_qemu():
80     f = open('/proc/cpuinfo')
81     x = ''.join(f.readlines())
82     f.close()
83     if 'QEMU' in x: return True
84     return False
85
86 def get_program_output(args):
87     p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
88     return p.communicate()[0]
89
90 def run_program(args):
91     subprocess.Popen(args, close_fds=True).wait()
92
93 def which(program):
94     def is_exe(fpath):
95         return os.path.exists(fpath) and os.access(fpath, os.X_OK)
96
97     fpath, fname = os.path.split(program)
98     if fpath:
99         if is_exe(program):
100             return program
101     else:
102         for path in os.environ["PATH"].split(os.pathsep):
103             exe_file = os.path.join(path, program)
104             if is_exe(exe_file):
105                 return exe_file
106
107     return None
108
109 parser = OptionParser(usage="usage: %prog [options] [window-manager]")
110 parser.add_option("--nostart", action="store_false", dest="start_server", default=True,
111                 help="Don't start X server")
112 parser.add_option("--display", action="store", type="string", dest="display",
113                 help="Start X server on display DISPLAY")
114 parser.add_option("--hsync", action="store", type="string", dest="hsync",
115                 help="Force writing a HorizSync range")
116 parser.add_option("--vsync", action="store", type="string", dest="vsync",
117                 help="Force writing a VertRefresh range")
118 parser.add_option("--mode", action="store", type="string", dest="mode",
119                 help="Force a specific resolution")
120 parser.add_option("--module", action="store", type="string", dest="module",
121                 help="Force driver MODULE instead of Xorg autodetection")
122 parser.add_option("-o", action="store", type="string", dest="xorg_conf", default="/etc/X11/xorg.conf",
123                 help="Specify alternate xorg.conf file [default: %default]")
124 parser.add_option("-f", "--force", action="store_true", dest="overwrite", default=False,
125                 help="Overwrite xorg.conf if it exists [default: %default]")
126
127 def main():
128     (options, args) = parser.parse_args()
129     bootparams = build_bootparams()
130
131     if os.getuid() == 0:
132         print "W: running as root is unsupported and may not work."
133         time.sleep(1)
134
135     if os.path.exists(options.xorg_conf):
136         if options.overwrite:
137             os.unlink(options.xorg_conf)
138         else:
139             print "E: Not overwriting existing %r without --force." % options.xorg_conf
140             print "I: If you previously ran grml-x, use startx /usr/bin/x-window-manager"
141             return 1
142
143     if 'xmode' in bootparams and not options.mode: options.mode = bootparams['xmode']
144     if 'xmodule' in bootparams and not options.module: options.module = bootparams['xmodule']
145
146     force_monitor = False
147     # cirrus driver for QEMU doesn't do 1024x768 without HorizSync set
148     if detect_qemu(): force_monitor = True
149
150     monitor = get_monitor_section(options, force_monitor)
151     device = get_device_section(options)
152
153     # build Screen section ourselves
154     d = {}
155     if monitor: d['Monitor'] = monitor.identifer
156     if device: d['Device'] = device.identifer
157     screen = Section('Screen', 'Screen0', d)
158     if options.mode:
159         d['DefaultColorDepth'] = 16
160         for depth in [8, 15, 16, 24, 32]:
161             screen.subsect += "SubSection \"Display\"\n\tDepth %d\n\tModes \"%s\"\t\nEndSubSection\n" % (depth, options.mode)
162
163     if len(args) == 1:
164         window_manager = args[0]
165         wm_path = which(window_manager)
166         if not wm_path:
167             print "E: Cannot find window manager %r, aborting." % window_manager
168             return 2
169         run_program(["sudo", "update-alternatives", "--set", "x-window-manager", wm_path])
170
171     config_empty = True
172     if monitor or device or len(screen.data) > 0 or screen.subsect != '':
173         config_empty = False
174
175     # write new config
176     if not config_empty:
177         try:
178             f = tempfile.NamedTemporaryFile(delete=False)
179             f.write('# Automatically generated by grml-x.\n')
180             f.write('# See man xorg.conf or /etc/X11/xorg.conf.example for more\n')
181             if monitor: f.write(str(monitor))
182             if device: f.write(str(device))
183             f.write(str(screen))
184             f.flush()
185             run_program(['sudo', 'mv', '-f', f.name, options.xorg_conf])
186         finally:
187             f.close()
188
189     if options.start_server:
190         xinitrc = '~/.xinitrc'
191         if 'XINITRC' in os.environ: xinitrc = os.environ['XINITRC']
192         xinitrc = os.path.expanduser(xinitrc)
193         startx = ['startx']
194         if os.path.exists(xinitrc):
195             startx.append(xinitrc)
196         else:
197             startx.append(which('x-window-manager'))
198         startx.append('--')
199         if options.display: startx.append(':' + options.display)
200         print "Starting X: %r" % startx
201         run_program(startx)
202
203     return 0
204
205 if __name__ == '__main__':
206     rc = 1
207     try:
208         rc = main()
209     except Exception:
210         print "E: Exception: ",
211         traceback.print_exc()
212     sys.exit(1)
213