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

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

#!/usr/bin/python 

# -*- coding:Utf-8 -*- 

# 

#  Copyright (C) 2010 Mathieu Comandon <strider@strycore.com> 

# 

#  This program is free software: you can redistribute it and/or modify 

#  it under the terms of the GNU General Public License version 3 as 

#  published by the Free Software Foundation. 

# 

#  This program is distributed in the hope that it will be useful, 

#  but WITHOUT ANY WARRANTY; without even the implied warranty of 

#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 

#  GNU General Public License for more details. 

# 

#  You should have received a copy of the GNU General Public License 

#  along with this program.  If not, see <http://www.gnu.org/licenses/>. 

# 

"""Handle the basic configuration of Lutris.""" 

 

import os 

import sys 

import yaml 

import logging 

from os.path import join 

 

from gi.repository import Gio 

 

from lutris import pga 

from lutris.util.log import logger 

from lutris.util.strings import slugify 

from lutris.settings import PGA_DB, CONFIG_DIR, DATA_DIR, CACHE_DIR 

 

 

def register_handler(): 

    """ Register the lutris: protocol to open with the application. """ 

    logger.debug("registering protocol") 

    executable = os.path.abspath(sys.argv[0]) 

    base_key = "desktop.gnome.url-handlers.lutris" 

    schema_directory = "/usr/share/glib-2.0/schemas/" 

    schema_source = Gio.SettingsSchemaSource.new_from_directory( 

        schema_directory, None, True 

    ) 

    schema = schema_source.lookup(base_key, True) 

    if schema: 

        settings = Gio.Settings.new(base_key) 

        settings.set_string('command', executable) 

    else: 

        logger.warning("Schema not installed, cannot register url-handler") 

 

 

def check_config(force_wipe=False): 

    """Check if initial configuration is correct.""" 

    directories = [CONFIG_DIR, 

                   join(CONFIG_DIR, "runners"), 

                   join(CONFIG_DIR, "games"), 

                   DATA_DIR, 

                   join(DATA_DIR, "covers"), 

                   join(DATA_DIR, "icons"), 

                   join(DATA_DIR, "banners"), 

                   join(DATA_DIR, "runners"), 

                   join(DATA_DIR, "lib"), 

                   CACHE_DIR, 

                   join(CACHE_DIR, "installer"), 

                   join(CACHE_DIR, "tmp")] 

    for directory in directories: 

        if not os.path.exists(directory): 

            logger.debug("creating directory %s" % directory) 

            os.mkdir(directory) 

 

    if force_wipe: 

        os.remove(PGA_DB) 

    pga.syncdb() 

 

 

def read_yaml_from_file(filename): 

    """Read filename and return parsed yaml""" 

    if not os.path.exists(filename): 

        return {} 

    try: 

        content = file(filename, 'r').read() 

        yaml_content = yaml.load(content) or {} 

    except (yaml.scanner.ScannerError, yaml.parser.ParserError): 

        logger.error("error parsing file %s", filename) 

        yaml_content = {} 

    return yaml_content 

 

 

class LutrisConfig(object): 

    """Class where all the configuration handling happens. 

 

    Lutris configuration uses a cascading mecanism where 

    each higher, more specific level override the lower ones. 

 

    The config files are stored in a YAML format and are easy to edit manually. 

 

    """ 

    def __init__(self, runner=None, game=None): 

        #Initialize configuration 

        self.config = {'system': {}} 

        self.game_config = {} 

        self.runner_config = {} 

        self.system_config = {} 

 

        self.game = None 

        self.runner = None 

 

        #By default config type is system, it can also be runner and game 

        #this means that when you call lutris_config_instance["key"] it will 

        #pick up the right configuration depending of config_type 

        if game: 

            self.game = game 

            self.config_type = "game" 

        elif runner: 

            self.runner = runner 

            self.config_type = "runner" 

        else: 

            self.config_type = "system" 

 

        #Read system configuration 

        self.system_config = read_yaml_from_file(join(CONFIG_DIR, 

                                                      "system.yml")) 

        if runner: 

            self.runner_config = read_yaml_from_file(join(CONFIG_DIR, 

                                                          "runners/%s.yml" 

                                                          % runner)) 

        else: 

            self.runner_config = {} 

        if self.game: 

            game_config_path = join(CONFIG_DIR, "games/%s.yml" % self.game) 

            if os.path.exists(game_config_path): 

                self.game_config = read_yaml_from_file(game_config_path) 

                self.runner = self.game_config.get("runner") 

        self.update_global_config() 

 

    def __str__(self): 

        return str(self.config) 

 

    def __getitem__(self, key, default=None): 

        """Allow to access config data directly by keys.""" 

        if key in ('game', 'runner', 'system'): 

            return self.config[key] 

        try: 

            if self.config_type == "game": 

                value = self.game_config[key] 

            elif self.config_type == "runner": 

                value = self.runner_config[key] 

            else: 

                value = self.system_config[key] 

        except KeyError: 

            value = default 

        return value 

 

    def __setitem__(self, key, value): 

        if self.config_type == "game": 

            self.game_config[key] = value 

        elif self.config_type == "runner": 

            self.runner_config[key] = value 

        elif self.config_type == "system": 

            self.system_config = value 

        self.update_global_config() 

 

    def get(self, key, default=None): 

        return self.__getitem__(key) 

 

    @property 

    def game_config_file(self): 

        return join(CONFIG_DIR, "games/%s.yml" % self.game) 

 

    def get_system(self, key): 

        """Return the value of 'key' for system config""" 

        try: 

            value = self.config["system"][key] 

            if str(value).lower() in ("false", "none", "no"): 

                value = False 

        except KeyError: 

            value = None 

        return value 

 

    def update_global_config(self): 

        """Update the global config dict.""" 

        for key in self.system_config.keys(): 

            if key in self.config: 

                self.config[key].update(self.system_config[key]) 

            else: 

                self.config[key] = self.system_config[key] 

 

        for key in self.runner_config.keys(): 

            if key in self.config: 

                self.config[key].update(self.runner_config[key]) 

            else: 

                self.config[key] = self.runner_config[key] 

 

        for key in self.game_config.keys(): 

            if key in self.config: 

                if type(self.config[key]) is dict: 

                    self.config[key].update(self.game_config[key]) 

            else: 

                self.config[key] = self.game_config[key] 

 

    def get_name(self): 

        """Return name of game""" 

        name = self.config["realname"] 

        return name 

 

    def remove(self, game=None): 

        """Delete the configuration file from disk.""" 

        if game is None: 

            game = self.game 

        logging.debug("removing config for %s", game) 

        if os.path.exists(self.game_config_file): 

            os.remove(self.game_config_file) 

        else: 

            logger.debug("No config file at %s" % self.game_config_file) 

 

    def is_valid(self): 

        """Check the config data and return True if config is ok.""" 

        return "runner" in self.game_config 

 

    def save(self, config_type=None): 

        """Save configuration file 

 

        The way to save config files can be set by the type argument 

        or with self.config_type 

        """ 

 

        self.update_global_config() 

        logging.debug("Saving config (type %s)", config_type) 

        logging.debug(self.config) 

        if config_type is None: 

            config_type = self.config_type 

        yaml_config = yaml.dump(self.config, default_flow_style=False) 

 

        if config_type == "system": 

            filename = join(CONFIG_DIR, "system.yml") 

            self.write_to_disk(filename, yaml_config) 

        elif config_type == "runner": 

            runner_config_path = join(CONFIG_DIR, 

                                      "runners/%s.yml" % self.runner) 

            self.write_to_disk(runner_config_path, yaml_config) 

 

        elif config_type == "game": 

            if not self.game: 

                self.game = slugify(self.config['realname']) 

            self.write_to_disk(self.game_config_file, yaml_config) 

            return self.game 

        else: 

            print("Config type is %s or %s" % (self.config_type, type)) 

            print("i don't know how to save this yet") 

 

    def write_to_disk(self, filepath, content): 

        with open(filepath, "w") as filehandler: 

            filehandler.write(content) 

 

    def get_path(self, default=None): 

        """Get the path to install games for a given runner. 

 

        Return False if it can't find an installation path 

        """ 

 

        if "system" in self.config and "game_path" in self.config["system"]: 

            return self.config["system"]["game_path"] 

        if not default or not os.path.exists(default): 

            return False 

        else: 

            return default