summaryrefslogtreecommitdiffstats
path: root/displayconfig/displayconfig-restore.py
blob: e4665bc716d05fac46d27fcd9c1b4465d539756d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#!/usr/bin/python
###########################################################################
# displayconfig-restore.py - description                                  #
# ------------------------------                                          #
# begin     : Wed Dec 15 2004                                             #
# copyright : (C) 2004-2006 by Simon Edwards                              #
# email     : simon@simonzone.com                                         #
#                                                                         #
###########################################################################
#                                                                         #
#   This program is free software; you can redistribute it and/or modify  #
#   it under the terms of the GNU General Public License as published by  #
#   the Free Software Foundation; either version 2 of the License, or     #
#   (at your option) any later version.                                   #
#                                                                         #
###########################################################################
import os
import os.path
import subprocess
import ixf86misc
import xf86misc

from execwithcapture import *

############################################################################
def FindXorgConfig(self):
    # Lookup location of X configfile
    for line in ExecWithCapture("xset", ["xset","q"],True).split('\n'):
        if line.strip().startswith("Config file"):
            return line.split(":")[1].strip()
    # Sometimes, xset doesn't know about the configfile location, hence ...
    if os.path.isfile("/etc/X11/xorg.conf"):
            return "/etc/X11/xorg.conf"
    return None

############################################################################
# FixXorgDPI
# ==========
# The idea here is to ensure that applications use a sensible DPI setting
# for fonts. When Xorg starts up it tries to detect the size of the attached
# monitor and calculate the real DPI from there and use that. Problems are:
#
#   * if the monitor size can not be detect then Xorg uses 75dpi. This is
#     usually far too low.
#
#   * if the monitor size is not accurately detected then you get bad a DPI.
#
#   * most fonts are optimised to work at a handful of standard DPIs. 96dpi,
#     120dpi and printer resolution 300dpi and 600dpi. Fonts rendered in
#     non-standard DPIs often look bad and jagged. This is a real problem
#     when rendering fonts on low resolution devices. (i.e. a computer
#     monitor).
#
# Although it is desirable in theory to use the real DPI of the monitor, in
# practice it is more important to ensure that fonts are well rendered even
# if the DPI in use is not correct.
#
# What this function does is read the display size from the X server and
# if it is lower than 140dpi then 'round' it to either 96dpi or 120dpi.
# (A dpi greater or equal to 140 is assumed to be high enough to render fonts
# well.) The new dpi is then loaded with the xrdb command into the X server
# resource database. Most X applications (Qt and GTK apps at least) will then
# use this DPI for font rendering.
#
def FixXorgDPI(desiredDPI):
    # dpi is:
    #       None - round the DPI.
    #       xserver - Use the X server's DPI.
    #       <number> - DPI to use.
    if desiredDPI=="xserver":
        return

    dpi = 96
    try:
        if desiredDPI is not None:
            dpi = int(desiredDPI)
    except ValueError:
        desiredDPI = None

    if desiredDPI is None:
        xserver = xf86misc.XF86Server()
        if len(xserver.getScreens())!=0:
            (width,height,width_mm,height_mm) = xserver.getScreens()[0].getDimensions()
            if not float(width_mm) == 0:
                w_dpi = float(width)/(float(width_mm)/25.4)
            else:
                w_dpi = 96
            if not float(height_mm) == 0:
                h_dpi = float(height)/(float(height_mm)/25.4)
            else:
                h_dpi = 96
            dpi = (w_dpi+h_dpi)/2.0   # Average the two possible DPIs.

            if dpi >= 140:   # Anything above 140 is ok.
                dpi = int(dpi)
            else:
                if abs(96-dpi) < abs(120-dpi):  # Rounding to 96 is best.
                    dpi = 96
                else:
                    dpi = 120

            # work around for LP beastie 151311
            if ((w_dpi < 200) and (h_dpi > 900)):
                dpi = 96

    try:
        xrdb = subprocess.Popen(["xrdb","-nocpp","-merge"],stdin=subprocess.PIPE)
        xrdb.communicate("Xft.dpi: %i\n" % dpi)
        xrdb.wait()
    except OSError:
        pass

    # Other common, but now used settingsfor xrdb:
    # Xft.antialias: 
    # Xft.hinting:
    # Xft.hintstyle: 
    # Xft.rgba: 

############################################################################
def ReadDisplayConfigRC():
    screens = None
    dpi = None
    dpms_seconds = None
    dpms_enabled = None

    configpath = ExecWithCapture("tde-config",['tde-config','--path','config'],True)

    # Hunt down the user's displayconfigrc file and adjust the resolution
    # on the fly to match. (Non-root Users can independantly specify their own settings.)
    dirs = configpath.strip().split(":")
    for dir in dirs:
        if dir!="":
            configpath = os.path.join(dir,"displayconfigrc")
            if os.path.exists(configpath):
                # Parse the config file.
                fhandle = open(configpath)
                screens = []
                currentscreen = None
                for line in fhandle.readlines():
                    line = line.strip()
                    if line.startswith("[Screen"):
                        # Screen, width, height, refresh, reflectx, reflecty, rotate, redgamma, greengamma,bluegamma
                        currentscreen = [int(line[7:-1]), None, None, None, False, False, "0", None, None, None]
                        screens.append(currentscreen)
                    elif line.startswith("["):
                        currentscreen = None
                    elif line.startswith("dpi="):
                        dpi = line[4:]
                    elif currentscreen is not None:
                        if line.startswith("width="):
                            currentscreen[1] = int(line[6:])
                        elif line.startswith("height="):
                            currentscreen[2] = int(line[7:])
                        elif line.startswith("refresh="):
                            currentscreen[3] = int(line[8:])
                        elif line.startswith("reflectX="):
                            currentscreen[4] = line[9:]=="1"
                        elif line.startswith("reflectY="):
                            currentscreen[5] = line[9:]=="1"
                        elif line.startswith("rotate="):
                            currentscreen[6] = line[7:]
                        elif line.startswith("redgamma="):
                            currentscreen[7] = line[9:]
                        elif line.startswith("greengamma="):
                            currentscreen[8] = line[11:]
                        elif line.startswith("bluegamma="):
                            currentscreen[9] = line[10:]
                        elif line.startswith("dpmsEnabled"):
                            dpms_enabled = line.split("=")[1]
                        elif line.startswith("dpmsSeconds"):
                            dpms_seconds = int(line.split("=")[1])
                fhandle.close()
                break

    return (screens,dpi,dpms_enabled,dpms_seconds)

############################################################################
def main():
    (screens,dpi,dpms_enabled,dpms_seconds) = ReadDisplayConfigRC()

    if dpms_enabled:
        if dpms_enabled == "on":
            if not dpms_seconds:
                dpms_seconds = 900
            cmd = "xset dpms %i %i %i" % (dpms_seconds,dpms_seconds,dpms_seconds)
            os.system(cmd)
        else:
            cmd = "xset -dpms"
            os.system(cmd)

    if screens is not None:
        # Set the X server.
        try:
            xserver = xf86misc.XF86Server()
            if len(screens)!=0:

                for screen in screens:
                    (id,width,height,refresh,reflectx,reflecty,rotate,redgamma,greengamma,bluegamma) = screen

                    # Convert the stuff into RandR's rotation bitfield thingy.
                    if rotate=="0":
                        rotation = xf86misc.XF86Screen.RR_Rotate_0
                    elif rotate=="90":
                        rotation = xf86misc.XF86Screen.RR_Rotate_90
                    elif rotate=="180":
                        rotation = xf86misc.XF86Screen.RR_Rotate_180
                    elif rotate=="270":
                        rotation = xf86misc.XF86Screen.RR_Rotate_270
                    if reflectx:
                        rotation |= xf86misc.XF86Screen.RR_Reflect_X
                    if reflecty:
                        rotation |= xf86misc.XF86Screen.RR_Reflect_Y

                    if id<len(xserver.getScreens()):
                        xscreen = xserver.getScreens()[id]

                        if xscreen.resolutionSupportAvailable():
                            available_sizes = xscreen.getAvailableSizes()

                            # Find the closest matching resolution
                            best_score = 1000000
                            best_size_id = 0
                            for size_id in range(len(available_sizes)):
                                size = available_sizes[size_id]
                                score = abs(size[0]-width)+abs(size[1]-height)
                                if score < best_score:
                                    best_size_id = size_id
                                    best_score = score

                            # Now find the best refresh for this resolution
                            best_score = 1000000
                            best_refresh = 50
                            for available_refresh in xscreen.getAvailableRefreshRates(best_size_id):
                                score = abs(refresh-available_refresh)
                                if score < best_score:
                                    best_refresh = available_refresh
                                    best_score = score

                            # Mask out any unsupported rotations.
                            rotation &= xscreen.getAvailableRotations()
                            xscreen.setScreenConfigAndRate(best_size_id, rotation, best_refresh)
                    
                    # Restore the gamma settings.
                    if redgamma is not None and greengamma is not None and bluegamma is not None:
                        try:
                            xscreen.setGamma( (float(redgamma), float(greengamma), float(bluegamma)) )
                        except ValueError,e:
                            pass
                            
            FixXorgDPI(dpi)
        except xf86misc.XF86Error,err:
            print err

        return

    else:
        # Ensure that the xorgs virtual screen size matches the default resolution
        # of the server. Why does this matter? When Xorg starts up it reads its
        # config file chooses the first mode in the "modes" line of the active
        # Screen section and uses it as the virtual screen size and as the
        # screen resolution (ie 1024x768 resolution screen showing a 1024x768 gfx
        # buffer). But, this means that you can't use RandR to get to any higher
        # screen resolutions (ie 1280x1024) because Xorg requires that the virtual
        # screen size 'cover' the screen resolution being displayed.
        #
        # So, to get around this problem and make it possible for people to select
        # a lower resolution screen *and* still have the option later to use
        # RandR/displayconfig to switch to higher resolution, displayconfig
        # explicitly sets the virtual screen size in xorg.conf to the largest
        # resoluution that the monitor/gfx card can support. The down side to
        # this is that the X server and tdm get the correct resolution but the
        # wrong (virtual) screen size. The user can now scroll around on the
        # greater virtual screen. Kind of annoying for tdm, unacceptable once
        # the user has logged in.
        #
        # What we do now as the user's KDE session is being started up is check
        # what the real virtual screen size is meant to be (=same as the real
        # resolution being used) and then use the RandR extension to explicitly
        # set the correct resolution. This has the effect of changing the virtual
        # screeen size to what we really want. (RandR can change the virtual
        # screen size, thankfully)
        import displayconfigabstraction

        try:
            xserver = xf86misc.XF86Server()

            for xscreen in xserver.getScreens():
                if xscreen.resolutionSupportAvailable():
                    mode_line = ixf86misc.XF86VidModeGetModeLine(xserver.getDisplay(),xscreen.getScreenId())

                    hdisplay = mode_line[1]
                    vdisplay = mode_line[5]

                    live_refresh_rate = xscreen.getRefreshRate()
                    try:
                        (live_width,live_height,x,x) = xscreen.getAvailableSizes()[xscreen.getSizeID()]
                    except IndexError, errmsg:
                        print "IndexError:", errmsg, "in displayconfig-restore getting live screen size - trying screen 0."
                        (live_width,live_height,x,x) = xscreen.getAvailableSizes()[0]

                    if (hdisplay,vdisplay) != (live_width,live_height):
                        # The screen resolution doesn't match the virtual screen size.
                        screen_sizes = xscreen.getAvailableSizes()
                        for size_id in range(len(screen_sizes)):
                            screen_size = screen_sizes[size_id]
                            if screen_size[0]==hdisplay and screen_size[1]==vdisplay:

                                # Find the closest matching refresh rate.
                                best_refresh = 0
                                best_score = 1000000
                                for rate in xscreen.getAvailableRefreshRates(size_id):
                                    score = abs(rate-live_refresh_rate)
                                    if score < best_score:
                                        best_refresh = rate
                                        best_score = score

                                # Reset the screen mode and virtual screen size.
                                xscreen.setScreenConfigAndRate(size_id,xscreen.getRotation(),best_refresh)
                                break
            FixXorgDPI(dpi)
        except (xf86misc.XF86Error,TypeError),err:
            print err

main()