Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/x11/xsettings_prop.py : 100%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2012-2019 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
6"""
7XSETTINGS
9This code deals with:
10* extracting data from XSETTINGS into nice python data structures
11and
12* converting those structures back into XSETTINGS format
14It is used by xpra.x11.gtk_x11.prop
15"""
17import os
18import sys
19import struct
21from xpra.log import Logger
22from xpra.util import envbool
23from xpra.os_util import strtobytes, bytestostr, hexstr
26log = Logger("x11", "xsettings")
28BLACKLISTED_XSETTINGS = os.environ.get("XPRA_BLACKLISTED_XSETTINGS",
29 "Gdk/WindowScalingFactor,Gtk/SessionBusId,Gtk/IMModule").split(",")
32#undocumented XSETTINGS endianess values:
33LITTLE_ENDIAN = 0
34BIG_ENDIAN = 1
35def get_local_byteorder():
36 if sys.byteorder=="little":
37 return LITTLE_ENDIAN
38 return BIG_ENDIAN # pragma: no cover
40#the 3 types of settings supported:
41XSettingsTypeInteger = 0
42XSettingsTypeString = 1
43XSettingsTypeColor = 2
45XSettingsNames = {
46 XSettingsTypeInteger : "Integer",
47 XSettingsTypeString : "String",
48 XSettingsTypeColor : "Color",
49 }
52XSETTINGS_CACHE = {}
53def get_settings(disp, d):
54 global XSETTINGS_CACHE
55 DEBUG_XSETTINGS = envbool("XPRA_XSETTINGS_DEBUG", False)
56 #parse xsettings according to
57 #http://standards.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html
58 assert len(d)>=12, "_XSETTINGS_SETTINGS property is too small: %s" % len(d)
59 if DEBUG_XSETTINGS:
60 log("get_settings(%s)", tuple(d))
61 byte_order, _, _, _, serial, n_settings = struct.unpack(b"=BBBBII", d[:12])
62 cache = XSETTINGS_CACHE.get(disp)
63 log("get_settings(..) found byte_order=%s (local is %s), serial=%s, n_settings=%s, cache(%s)=%s",
64 byte_order, get_local_byteorder(), serial, n_settings, disp, cache)
65 if cache and cache[0]==serial:
66 log("get_settings(..) returning value from cache")
67 return cache
68 settings = []
69 pos = 12
70 while n_settings>len(settings):
71 log("get_settings(..) pos=%i (len=%i), data=%s", pos, len(d), hexstr(d[pos:]))
72 istart = pos
73 #parse header:
74 setting_type, _, name_len = struct.unpack(b"=BBH", d[pos:pos+4])
75 pos += 4
76 #extract property name:
77 prop_name = d[pos:pos+name_len]
78 pos += (name_len + 0x3) & ~0x3
79 #serial:
80 assert len(d)>=pos+4, "not enough data (%s bytes) to extract serial (4 bytes needed)" % (len(d)-pos)
81 last_change_serial = struct.unpack(b"=I", d[pos:pos+4])[0]
82 pos += 4
83 if DEBUG_XSETTINGS:
84 log("get_settings(..) found property %s of type %s, serial=%s",
85 prop_name, XSettingsNames.get(setting_type, "INVALID!"), last_change_serial)
86 #extract value:
87 if setting_type==XSettingsTypeInteger:
88 assert len(d)>=pos+4, "not enough data (%s bytes) to extract int (4 bytes needed)" % (len(d)-pos)
89 value = int(struct.unpack(b"=I", d[pos:pos+4])[0])
90 pos += 4
91 elif setting_type==XSettingsTypeString:
92 assert len(d)>=pos+4, "not enough data (%s bytes) to extract string length (4 bytes needed)" % (len(d)-pos)
93 value_len = struct.unpack(b"=I", d[pos:pos+4])[0]
94 assert len(d)>=pos+4+value_len, "not enough data (%s bytes) to extract string (%s bytes needed)" % (len(d)-pos-4, value_len)
95 value = d[pos+4:pos+4+value_len]
96 pos += 4 + ((value_len + 0x3) & ~0x3)
97 elif setting_type==XSettingsTypeColor:
98 assert len(d)>=pos+8, "not enough data (%s bytes) to extract color (8 bytes needed)" % (len(d)-pos)
99 red, blue, green, alpha = struct.unpack(b"=HHHH", d[pos:pos+8])
100 value = (red, blue, green, alpha)
101 pos += 8
102 else:
103 log.error("invalid setting type: %s, cannot continue parsing XSETTINGS!", setting_type)
104 break
105 setting = setting_type, prop_name, value, last_change_serial
106 if DEBUG_XSETTINGS:
107 log("get_settings(..) %s -> %s", tuple(d[istart:pos]), setting)
108 settings.append(setting)
109 log("get_settings(..) settings=%s", settings)
110 if disp:
111 XSETTINGS_CACHE[disp] = (serial, settings)
112 return serial, settings
114def set_settings(disp, d):
115 assert len(d)==2, "invalid format for XSETTINGS: %s" % str(d)
116 serial, settings = d
117 log("set_settings(%s) serial=%s, %s settings", d, serial, len(settings))
118 all_bin_settings = []
119 for setting in settings:
120 setting_type, prop_name, value, last_change_serial = setting
121 prop_name = strtobytes(prop_name)
122 try:
123 log("set_settings(..) processing property %s of type %s",
124 bytestostr(prop_name), XSettingsNames.get(setting_type, "INVALID!"))
125 x = struct.pack(b"=BBH", setting_type, 0, len(prop_name))
126 x += prop_name
127 pad_len = ((len(prop_name) + 0x3) & ~0x3) - len(prop_name)
128 x += b'\0'*pad_len
129 x += struct.pack(b"=I", last_change_serial)
130 if setting_type==XSettingsTypeInteger:
131 assert isinstance(value, int), "invalid value type (int or long wanted): %s" % type(value)
132 x += struct.pack(b"=I", int(value))
133 elif setting_type==XSettingsTypeString:
134 value = strtobytes(value)
135 x += struct.pack(b"=I", len(value))
136 x += value
137 pad_len = ((len(value) + 0x3) & ~0x3) - len(value)
138 x += b'\0'*pad_len
139 elif setting_type==XSettingsTypeColor:
140 red, blue, green, alpha = value
141 x += struct.pack(b"=HHHH", red, blue, green, alpha)
142 else:
143 log.error("Error: invalid type %i for xsetting property '%s'", setting_type, bytestostr(prop_name))
144 continue
145 log("set_settings(..) %s -> %s", setting, tuple(x))
146 all_bin_settings.append(x)
147 except Exception as e:
148 log("set_settings(%s, %s)", disp, d, exc_info=True)
149 log.error("Error processing XSettings property %s:", bytestostr(prop_name))
150 log.error(" type=%s, value=%s", XSettingsNames.get(setting_type, "INVALID!"), value)
151 log.error(" %s", e)
152 #header
153 v = struct.pack(b"=BBBBII", get_local_byteorder(), 0, 0, 0, serial, len(all_bin_settings))
154 v += b"".join(all_bin_settings) #values
155 v += b'\0' #null terminated
156 log("set_settings(%s)=%s", d, tuple(v))
157 return v
160def main(): # pragma: no cover
161 from xpra.platform.gui import init as gui_init
162 from xpra.os_util import POSIX
163 from xpra.platform import program_context
164 from xpra.gtk_common.error import xsync
165 with program_context("XSettings"):
166 gui_init()
167 verbose = "-v" in sys.argv or "--verbose" in sys.argv
168 if verbose:
169 from xpra.log import get_all_loggers
170 for x in get_all_loggers():
171 x.enable_debug()
173 #naughty, but how else can I hook this up?
174 if not POSIX:
175 print("xsettings require a posix OS")
176 return 1
178 with xsync:
179 from xpra.x11.bindings.posix_display_source import init_posix_display_source #@UnresolvedImport
180 init_posix_display_source()
181 from xpra.x11.bindings.window_bindings import X11WindowBindings #@UnresolvedImport
182 window_bindings = X11WindowBindings()
183 selection = "_XSETTINGS_S0"
184 owner = window_bindings.XGetSelectionOwner(selection)
185 print("owner(%s)=%#x" % (selection, owner))
186 XSETTINGS = "_XSETTINGS_SETTINGS"
187 if owner:
188 data = window_bindings.XGetWindowProperty(owner, XSETTINGS, XSETTINGS)
189 serial, settings = get_settings(None, data)
190 print("serial=%s" % serial)
191 print("%s settings:" % len(settings))
192 for s in settings:
193 print(s)
194 return 0
197if __name__ == "__main__": # pragma: no cover
198 sys.exit(main())