Hide keyboard shortcuts

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. 

5 

6""" 

7XSETTINGS 

8 

9This code deals with: 

10* extracting data from XSETTINGS into nice python data structures 

11and 

12* converting those structures back into XSETTINGS format 

13 

14It is used by xpra.x11.gtk_x11.prop 

15""" 

16 

17import os 

18import sys 

19import struct 

20 

21from xpra.log import Logger 

22from xpra.util import envbool 

23from xpra.os_util import strtobytes, bytestostr, hexstr 

24 

25 

26log = Logger("x11", "xsettings") 

27 

28BLACKLISTED_XSETTINGS = os.environ.get("XPRA_BLACKLISTED_XSETTINGS", 

29 "Gdk/WindowScalingFactor,Gtk/SessionBusId,Gtk/IMModule").split(",") 

30 

31 

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 

39 

40#the 3 types of settings supported: 

41XSettingsTypeInteger = 0 

42XSettingsTypeString = 1 

43XSettingsTypeColor = 2 

44 

45XSettingsNames = { 

46 XSettingsTypeInteger : "Integer", 

47 XSettingsTypeString : "String", 

48 XSettingsTypeColor : "Color", 

49 } 

50 

51 

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 

113 

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 

158 

159 

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() 

172 

173 #naughty, but how else can I hook this up? 

174 if not POSIX: 

175 print("xsettings require a posix OS") 

176 return 1 

177 

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 

195 

196 

197if __name__ == "__main__": # pragma: no cover 

198 sys.exit(main())