##################################################################### # -*- coding: iso-8859-1 -*- # # # # Frets on Fire # # Copyright (C) 2006 Sami Kyöstilä # # 2008 myfingershurt # # 2008 evilynux # # # # 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. # # # # 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, write to the Free Software # # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # # MA 02110-1301, USA. # ##################################################################### import os from Queue import Queue, Empty from threading import Thread, BoundedSemaphore import time import shutil import stat import Config from Task import Task import Log import Version class Loader(Thread): def __init__(self, target, name, function, resultQueue, loaderSemaphore, onLoad = None, onCancel = None): Thread.__init__(self) self.semaphore = loaderSemaphore self.target = target self.name = name self.function = function self.resultQueue = resultQueue self.result = None self.onLoad = onLoad self.onCancel = onCancel self.exception = None self.time = 0.0 self.canceled = False #myfingershurt: the following should be global and done ONCE: #self.game_priority = Config.get("performance", "game_priority") self.logLoadings = Config.get("game", "log_loadings") if target and name: setattr(target, name, None) def run(self): self.semaphore.acquire() game_priority = Config.get("performance", "game_priority") # Reduce priority on posix if os.name == "posix": # evilynux - Beware, os.nice _decreases_ priority, hence the reverse logic os.nice(5 - game_priority) elif os.name == "nt": #myfingershurt: the following should be global and done ONCE: #self.setPriority(priority = self.game_priority) #...or maybe not at all... self.setPriority(priority = game_priority) #wont work without ctypes #self.enableScreenSaver(0) self.load() self.semaphore.release() self.resultQueue.put(self) def __str__(self): return "%s(%s) %s" % (self.function.__name__, self.name, self.canceled and "(canceled)" or "") def setPriority(self, pid = None, priority = 2): """ Set The Priority of a Windows Process. Priority is a value between 0-5 where 2 is normal priority. Default sets the priority of the current python process but can take any valid process ID. """ import win32api, win32process, win32con priorityClasses = [win32process.IDLE_PRIORITY_CLASS, win32process.BELOW_NORMAL_PRIORITY_CLASS, win32process.NORMAL_PRIORITY_CLASS, win32process.ABOVE_NORMAL_PRIORITY_CLASS, win32process.HIGH_PRIORITY_CLASS, win32process.REALTIME_PRIORITY_CLASS] pid = win32api.GetCurrentProcessId() handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid) win32process.SetPriorityClass(handle, priorityClasses[priority]) win32process.SetProcessAffinityMask(handle, 1) # def enableScreenSaver(self, on): # import ctypes # SPI_SETSCREENSAVEACTIVE = 17 # ctypes.windll.user32.SystemParametersInfoA(SPI_SETSCREENSAVEACTIVE, on, None, 0) def cancel(self): self.canceled = True def load(self): try: start = time.time() self.result = self.function() self.time = time.time() - start except: import sys self.exception = sys.exc_info() def finish(self): if self.canceled: if self.onCancel: self.onCancel() return if self.logLoadings == 1: Log.notice("Loaded %s.%s in %.3f seconds" % (self.target.__class__.__name__, self.name, self.time)) if self.exception: raise self.exception[0], self.exception[1], self.exception[2] if self.target and self.name: setattr(self.target, self.name, self.result) if self.onLoad: self.onLoad(self.result) return self.result def __call__(self): self.join() return self.result class Resource(Task): def __init__(self, dataPath = os.path.join("..", "data")): self.resultQueue = Queue() self.dataPaths = [dataPath] self.loaderSemaphore = BoundedSemaphore(value = 1) self.loaders = [] #myfingershurt: the following should be global, and only done at startup. Not every damn time a file is loaded. self.songPath = [] self.baseLibrary = Config.get("game", "base_library") #evilynux - Support for songs in ~/.fretsonfire/songs (GNU/Linux and MacOS X) if self.baseLibrary == "None" and os.name == "posix": path = os.path.expanduser("~/." + Version.appName()) if os.path.isdir(path): self.baseLibrary = path Config.set("game", "base_library", path) if self.baseLibrary and os.path.isdir(self.baseLibrary): self.songPath = [self.baseLibrary] self.logLoadings = Config.get("game", "log_loadings") #myfingershurt: Need a function to refresh the base library after a new one is selected: def refreshBaseLib(self): self.baseLibrary = Config.get("game", "base_library") if self.baseLibrary and os.path.isdir(self.baseLibrary): self.songPath = [self.baseLibrary] def addDataPath(self, path): if not path in self.dataPaths: self.dataPaths = [path] + self.dataPaths def removeDataPath(self, path): if path in self.dataPaths: self.dataPaths.remove(path) def fileName(self, *name, **args): #myfingershurt: the following should be global, and only done at startup. Not every damn time a file is loaded. #songPath = [] #baseLibrary = Config.get("game", "base_library") #if baseLibrary and os.path.isdir(baseLibrary): # songPath = [baseLibrary] baseLibrary = self.baseLibrary songPath = self.songPath if not args.get("writable", False): for dataPath in self.dataPaths + songPath: readOnlyPath = os.path.join(dataPath, *name) # If the requested file is in the read-write path and not in the # read-only path, use the existing read-write one. if os.path.isfile(readOnlyPath): return readOnlyPath elif os.path.isdir(readOnlyPath): return readOnlyPath readWritePath = os.path.join(getWritableResourcePath(), *name) if os.path.isfile(readWritePath): return readWritePath return readOnlyPath else: for dataPath in [self.dataPaths[-1]] + songPath: readOnlyPath = os.path.join(dataPath, *name) if not (os.path.isfile(readOnlyPath) or os.path.isdir(readOnlyPath)): continue try: # First see if we can write to the original file if os.access(readOnlyPath, os.W_OK): return readOnlyPath # If the original file does not exist, see if we can write to its directory if not os.path.isfile(readOnlyPath) and os.access(os.path.dirname(readOnlyPath), os.W_OK): pass #return readOnlyPath except: raise # If the resource exists in the read-only path, make a copy to the # read-write path. readWritePath = os.path.join(getWritableResourcePath(), *name) if not os.path.isfile(readWritePath) and os.path.isfile(readOnlyPath): Log.notice("Copying '%s' to writable data directory." % "/".join(name)) try: os.makedirs(os.path.dirname(readWritePath)) except: pass shutil.copy(readOnlyPath, readWritePath) self.makeWritable(readWritePath) # Create directories if needed if not os.path.isdir(readWritePath) and os.path.isdir(readOnlyPath): Log.notice("Creating writable directory '%s'." % "/".join(name)) os.makedirs(readWritePath) self.makeWritable(readWritePath) return readWritePath return readOnlyPath def makeWritable(self, path): os.chmod(path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC) def load(self, target = None, name = None, function = lambda: None, synch = False, onLoad = None, onCancel = None): if self.logLoadings == 1: Log.notice("Loading %s.%s %s" % (target.__class__.__name__, name, synch and "synchronously" or "asynchronously")) l = Loader(target, name, function, self.resultQueue, self.loaderSemaphore, onLoad = onLoad, onCancel = onCancel) if synch: l.load() return l.finish() else: self.loaders.append(l) l.start() return l def run(self, ticks): try: loader = self.resultQueue.get_nowait() loader.finish() self.loaders.remove(loader) except Empty: pass def getWritableResourcePath(): """ Returns a path that holds the configuration for the application. """ path = "." appname = Version.appName() if os.name == "posix": path = os.path.expanduser("~/." + appname) # evilynux - MacOS X, putting config files in the standard folder if( os.uname()[0] == "Darwin" ): path = os.path.expanduser("~/Library/Preferences/" + appname) elif os.name == "nt": try: path = os.path.join(os.environ["APPDATA"], appname) except: pass try: os.mkdir(path) except: pass return path