예제 #1
0
class ModeModifier(Modifier):
	COMMAND = "mode"
	PROFILE_KEYS = ("modes",)
	MIN_TRIGGER = 2		# When trigger is bellow this position, list of held_triggers is cleared
	MIN_STICK = 2		# When abs(stick) < MIN_STICK, stick is considered released and held_sticks is cleared
	PROFILE_KEY_PRIORITY = 2
	
	def __init__(self, *stuff):
		Modifier.__init__(self)
		self.default = None
		self.mods = OrderedDict()
		self.held_buttons = set()
		self.held_sticks = set()
		self.held_triggers = {}
		self.old_action = None
		self.timeout = DoubleclickModifier.DEAFAULT_TIMEOUT
		
		button = None
		for i in stuff:
			if self.default is not None:
				# Default has to be last parameter
				raise ValueError("Invalid parameters for 'mode'")
			if isinstance(i, Action) and button is None:
				self.default = i
			elif isinstance(i, Action):
				self.mods[button] = i
				button = None
			elif isinstance(i, RangeOP) or i in SCButtons:
				button = i
			else:
				raise ValueError("Invalid parameter for 'mode': %s" % (i,))
		self.make_checks()
		if self.default is None:
			self.default = NoAction()
	
	
	def make_checks(self):
		self.checks = []
		for button, action in self.mods.items():
			if isinstance(button, RangeOP):
				self.checks.append(( button, action ))
			else:
				self.checks.append(( self.make_button_check(button), action ))
	
	
	def get_child_actions(self):
		if self.default is None:
			return self.mods.values()
		else:
			return [ self.default ] + self.mods.values()
	
	
	@staticmethod
	def decode(data, a, parser, *b):
		args = []
		for button in data[ModeModifier.PROFILE_KEYS[0]]:
			if hasattr(SCButtons, button):
				args += [ getattr(SCButtons, button), parser.from_json_data(data[ModeModifier.PROFILE_KEYS[0]][button]) ]
		if a:
			args += [ a ]
		mm = ModeModifier(*args)
		if "name" in data:
			mm.name = data["name"]
		return mm
	
	
	def get_compatible_modifiers(self):
		rv = 0
		for action in self.mods.values():
			rv |= action.get_compatible_modifiers()
		if self.default:
			rv |= self.default.get_compatible_modifiers()
		return rv
	
	
	def strip(self):
		# Returns default action or action assigned to first modifier
		if self.default:
			return self.default.strip()
		if len(self.mods):
			return self.mods.values()[0].strip()
		# Empty ModeModifier
		return NoAction()
	
	
	def compress(self):
		if self.default:
			self.default = self.default.compress()
		for check in self.mods:
			self.mods[check] = self.mods[check].compress()
		self.make_checks()
		return self
	
	
	def __str__(self):
		rv = [ ]
		for check in self.mods:
			rv += [ nameof(check), self.mods[check] ]
		if self.default is not None:
			rv += [ self.default ]
		return "<Modifier '%s', %s>" % (self.COMMAND, rv)
	
	__repr__ = __str__
	
	
	def describe(self, context):
		if self.name: return self.name
		l = []
		if self.default : l.append(self.default)
		for check in self.mods:
			l.append(self.mods[check])
		return "\n".join([ x.describe(context) for x in l ])
	
	
	def to_string(self, multiline=False, pad=0):
		if multiline:
			rv = [ (" " * pad) + "mode(" ]
			for check in self.mods:
				a_str = NameModifier.unstrip(self.mods[check]).to_string(True).split("\n")
				a_str[0] = (" " * pad) + "  " + (nameof(check) + ",").ljust(11) + a_str[0]	# Key has to be one of SCButtons
				for i in xrange(1, len(a_str)):
					a_str[i] = (" " * pad) + "  " + a_str[i]
				a_str[-1] = a_str[-1] + ","
				rv += a_str
			if self.default is not None:
				a_str = [
					(" " * pad) + "  " + x
					for x in NameModifier.unstrip(self.default).to_string(True).split("\n")
				]
				rv += a_str
			if rv[-1][-1] == ",":
				rv[-1] = rv[-1][0:-1]
			rv += [ (" " * pad) + ")" ]
			return "\n".join(rv)
		else:
			rv = [ ]
			for check in self.mods:
				rv += [ nameof(check), NameModifier.unstrip(self.mods[check]).to_string(False) ]
			if self.default is not None:
				rv += [ NameModifier.unstrip(self.default).to_string(False) ]
			return "mode(" + ", ".join(rv) + ")"
	
	
	def cancel(self, mapper):
		for action in self.mods.values():
			action.cancel(mapper)
		self.default.cancel(mapper)
	
	
	def select(self, mapper):
		"""
		Selects action by pressed button.
		"""
		for check, action in self.checks:
			if check(mapper):
				return action
		return self.default
	
	
	def select_w_check(self, mapper):
		"""
		As select, but returns matched check as well.
		"""
		for check, action in self.checks:
			if check(mapper):
				return check, action
		return lambda *a:True, self.default
	
	
	@staticmethod
	def make_button_check(button):
		def cb(mapper):
			return mapper.is_pressed(button)
		
		cb.name = button.name	# So nameof() still works on keys in self.mods
		return cb
	
	
	def button_press(self, mapper):
		sel = self.select(mapper)
		self.held_buttons.add(sel)
		return sel.button_press(mapper)
	
	
	def button_release(self, mapper):
		# Releases all held buttons, not just button that matches
		# currently pressed modifier
		for b in self.held_buttons:
			b.button_release(mapper)
	
	
	def trigger(self, mapper, position, old_position):
		if position < ModeModifier.MIN_TRIGGER:
			for b in self.held_triggers:
				b.trigger(mapper, 0, self.held_triggers[b])
			self.held_triggers = {}
			return False
		else:
			sel = self.select(mapper)
			self.held_triggers[sel] = position
			return sel.trigger(mapper, position, old_position)
	
	
	def axis(self, mapper, position, what):
		return self.select(mapper).axis(mapper, position, what)
	
	
	def gyro(self, mapper, pitch, yaw, roll, *q):
		sel = self.select(mapper)
		if sel is not self.old_action:
			if self.old_action:
				self.old_action.gyro(mapper, 0, 0, 0, *q)
			self.old_action = sel
		return sel.gyro(mapper, pitch, yaw, roll, *q)
	
	
	def pad(self, mapper, position, what):
		return self.select(mapper).pad(mapper, position, what)
	
	
	def whole(self, mapper, x, y, what):
		if what == STICK:
			if abs(x) < ModeModifier.MIN_STICK and abs(y) < ModeModifier.MIN_STICK:
				for check, action in self.held_sticks:
					action.whole(mapper, 0, 0, what)
				self.held_sticks.clear()
			else:
				ac, active = self.select_w_check(mapper)
				self.held_sticks.add(( ac, active ))
				for check, action in list(self.held_sticks):
					if check == ac or check(mapper):
						action.whole(mapper, x, y, what)
					else:
						action.whole(mapper, 0, 0, what)
						self.held_sticks.remove(( check, action ))
			mapper.force_event.add(FE_STICK)
		else:
			sel = self.select(mapper)
			if sel is not self.old_action:
				mapper.set_button(what, False)
				if self.old_action:
					self.old_action.whole(mapper, 0, 0, what)
				self.old_action = sel
				rv = sel.whole(mapper, x, y, what)
				mapper.set_button(what, True)
				return rv
			else:
				return sel.whole(mapper, x, y, what)
예제 #2
0
class ModeModifier(Modifier):
	COMMAND = "mode"
	PROFILE_KEYS = ("modes",)
	MIN_TRIGGER = 2		# When trigger is bellow this position, list of held_triggers is cleared
	MIN_STICK = 2		# When abs(stick) < MIN_STICK, stick is considered released and held_sticks is cleared
	PROFILE_KEY_PRIORITY = 2
	
	def __init__(self, *stuff):
		# TODO: Better documentation for this. For now, using shell
		# TODO: and range as condition is not documented
		Modifier.__init__(self)
		self.default = None
		self.mods = OrderedDict()
		self.held_buttons = set()
		self.held_sticks = set()
		self.held_triggers = {}
		self.old_action = None
		self.shell_commands = {}
		self.shell_timeout = 0.5
		self.timeout = DoubleclickModifier.DEAFAULT_TIMEOUT
		
		# ShellCommandAction cannot be imported normally, it would create
		# import cycle of hell
		ShellCommandAction = Action.ALL['shell']
		button = None
		for i in stuff:
			if self.default is not None:
				# Default has to be last parameter
				raise ValueError("Invalid parameters for 'mode'")
			if isinstance(i, ShellCommandAction) and button is None:
				# 'shell' can be used instead of button
				button = i
			elif isinstance(i, Action) and button is None:	
				self.default = i
			elif isinstance(i, Action):
				self.mods[button] = i
				button = None
			elif isinstance(i, RangeOP) or i in SCButtons:
				button = i
			else:
				raise ValueError("Invalid parameter for 'mode': %s" % (i,))
		self.make_checks()
		if self.default is None:
			if isinstance(button, ShellCommandAction):
				self.default = button
			else:
				self.default = NoAction()
	
	
	def make_checks(self):
		self.checks = []
		self.shell_commands = {}
		ShellCommandAction = Action.ALL['shell']
		for c, action in self.mods.items():
			if isinstance(c, RangeOP):
				self.checks.append(( c, action ))
			elif isinstance(c, ShellCommandAction):
				self.shell_commands[c.command] = c
				self.checks.append(( self.make_shell_check(c), action ))
			else:
				self.checks.append(( self.make_button_check(c), action ))
	
	
	def get_child_actions(self):
		rv = list(self.mods.values()) + list(self.shell_commands.values())
		if self.default is not None:
			rv += [ self.default ]
		return rv
	
	
	@staticmethod
	def decode(data, a, parser, *b):
		args = []
		for button in data[ModeModifier.PROFILE_KEYS[0]]:
			if hasattr(SCButtons, button):
				args += [ getattr(SCButtons, button), parser.from_json_data(data[ModeModifier.PROFILE_KEYS[0]][button]) ]
		if a:
			args += [ a ]
		mm = ModeModifier(*args)
		if "name" in data:
			mm.name = data["name"]
		return mm
	
	
	def get_compatible_modifiers(self):
		rv = 0
		for action in self.mods.values():
			rv |= action.get_compatible_modifiers()
		if self.default:
			rv |= self.default.get_compatible_modifiers()
		return rv
	
	
	def strip(self):
		# Returns default action or action assigned to first modifier
		if self.default:
			return self.default.strip()
		if len(self.mods):
			return self.mods.values()[0].strip()
		# Empty ModeModifier
		return NoAction()
	
	
	def compress(self):
		if self.default:
			self.default = self.default.compress()
		for check in self.mods:
			self.mods[check] = self.mods[check].compress()
		self.make_checks()
		return self
	
	
	def __str__(self):
		rv = [ ]
		for check in self.mods:
			rv += [ nameof(check), self.mods[check] ]
		if self.default is not None:
			rv += [ self.default ]
		return "<Modifier '%s', %s>" % (self.COMMAND, rv)
	
	__repr__ = __str__
	
	
	def describe(self, context):
		if self.name: return self.name
		l = []
		if self.default : l.append(self.default)
		for check in self.mods:
			l.append(self.mods[check])
		return "\n".join([ x.describe(context) for x in l ])
	
	
	def to_string(self, multiline=False, pad=0):
		if multiline:
			rv = [ (" " * pad) + "mode(" ]
			for check in self.mods:
				a_str = NameModifier.unstrip(self.mods[check]).to_string(True).split("\n")
				a_str[0] = (" " * pad) + "  " + (nameof(check) + ",").ljust(11) + a_str[0]	# Key has to be one of SCButtons
				for i in xrange(1, len(a_str)):
					a_str[i] = (" " * pad) + "  " + a_str[i]
				a_str[-1] = a_str[-1] + ","
				rv += a_str
			if self.default is not None:
				a_str = [
					(" " * pad) + "  " + x
					for x in NameModifier.unstrip(self.default).to_string(True).split("\n")
				]
				rv += a_str
			if rv[-1][-1] == ",":
				rv[-1] = rv[-1][0:-1]
			rv += [ (" " * pad) + ")" ]
			return "\n".join(rv)
		else:
			rv = [ ]
			for check in self.mods:
				rv += [ nameof(check), NameModifier.unstrip(self.mods[check]).to_string(False) ]
			if self.default is not None:
				rv += [ NameModifier.unstrip(self.default).to_string(False) ]
			return "mode(" + ", ".join(rv) + ")"
	
	
	def cancel(self, mapper):
		for action in self.mods.values():
			action.cancel(mapper)
		self.default.cancel(mapper)
	
	
	def select(self, mapper):
		"""
		Selects action by pressed button.
		"""
		for check, action in self.checks:
			if check(mapper):
				return action
		return self.default
	
	
	def select_w_check(self, mapper):
		"""
		As select, but returns matched check as well.
		"""
		for check, action in self.checks:
			if check(mapper):
				return check, action
		return lambda *a:True, self.default
	
	
	@staticmethod
	def make_button_check(button):
		def cb(mapper):
			return mapper.is_pressed(button)
		
		cb.name = button.name	# So nameof() still works on keys in self.mods
		return cb
	
	
	@staticmethod
	def make_shell_check(c):
		def cb(mapper):
			try:
				return c.__proc.poll() == 0
			except:
				return False
		
		c.name = cb.name = c.to_string()	# So nameof() still works on keys in self.mods
		c.__proc = None
		return cb
	
	
	def button_press(self, mapper):
		if len(self.shell_commands) > 0:
			# https://github.com/kozec/sc-controller/issues/427
			# If 'shell' is used as any condition, all shell commands
			# are executed and ModeShift waits up to 500ms for them
			# to terminate. Then, if command returned zero exit code
			# it's considered as 'true' condition.
			for c in self.shell_commands.values():
				c.__proc = c.button_press(mapper)
			self.shell_timeout = 0.5
			mapper.schedule(0, self.check_shell_commands)
			return
		
		sel = self.select(mapper)
		self.held_buttons.add(sel)
		return sel.button_press(mapper)
	
	
	def check_shell_commands(self, mapper):
		for c in self.shell_commands.values():
			if c.__proc and c.__proc.poll() == 0:
				sel = self.select(mapper)
				self.kill_shell_commands()
				self.held_buttons.add(sel)
				return sel.button_press(mapper)
		
		self.shell_timeout -= 0.05
		if self.shell_timeout > 0:
			mapper.schedule(0.05, self.check_shell_commands)
		else:
			# time is up, kill all processes and execute what's left
			self.kill_shell_commands()
			sel = self.select(mapper)
			self.held_buttons.add(sel)
			return sel.button_press(mapper)
	
	
	def kill_shell_commands(self):
		for c in self.shell_commands.values():
			try:
				if c.__proc: c.__proc.kill()
			except: pass
			c.__proc = None
	
	
	def button_release(self, mapper):
		# Releases all held buttons, not just button that matches
		# currently pressed modifier
		for b in self.held_buttons:
			b.button_release(mapper)
	
	
	def trigger(self, mapper, position, old_position):
		if position < ModeModifier.MIN_TRIGGER:
			for b in self.held_triggers:
				b.trigger(mapper, 0, self.held_triggers[b])
			self.held_triggers = {}
			return False
		else:
			sel = self.select(mapper)
			self.held_triggers[sel] = position
			return sel.trigger(mapper, position, old_position)
	
	
	def axis(self, mapper, position, what):
		return self.select(mapper).axis(mapper, position, what)
	
	
	def gyro(self, mapper, pitch, yaw, roll, *q):
		sel = self.select(mapper)
		if sel is not self.old_action:
			if self.old_action:
				self.old_action.gyro(mapper, 0, 0, 0, *q)
			self.old_action = sel
		return sel.gyro(mapper, pitch, yaw, roll, *q)
	
	
	def pad(self, mapper, position, what):
		return self.select(mapper).pad(mapper, position, what)
	
	
	def whole(self, mapper, x, y, what):
		if what == STICK:
			if abs(x) < ModeModifier.MIN_STICK and abs(y) < ModeModifier.MIN_STICK:
				for check, action in self.held_sticks:
					action.whole(mapper, 0, 0, what)
				self.held_sticks.clear()
			else:
				ac, active = self.select_w_check(mapper)
				self.held_sticks.add(( ac, active ))
				for check, action in list(self.held_sticks):
					if check == ac or check(mapper):
						action.whole(mapper, x, y, what)
					else:
						action.whole(mapper, 0, 0, what)
						self.held_sticks.remove(( check, action ))
			mapper.force_event.add(FE_STICK)
		else:
			sel = self.select(mapper)
			if sel is not self.old_action:
				mapper.set_button(what, False)
				if self.old_action:
					self.old_action.whole(mapper, 0, 0, what)
				self.old_action = sel
				rv = sel.whole(mapper, x, y, what)
				mapper.set_button(what, True)
				return rv
			else:
				return sel.whole(mapper, x, y, what)
예제 #3
0
class Modifier(Action):
	def __init__(self, *params):
		Action.__init__(self, *params)
		params = list(params)
		for p in params:
			if isinstance(p, Action):
				self.action = p
				params.remove(p)
				break
		else:
			self.action = NoAction()
		self._mod_init(*params)
	
	
	def get_compatible_modifiers(self):
		return self.action.get_compatible_modifiers()
	
	
	def cancel(self, mapper):
		self.action.cancel(mapper)
	
	
	def get_child_actions(self):
		return (self.action, )
	
	
	def _mod_init(self):
		"""
		Initializes modifier with rest of parameters, after action parameter
		was taken from it and stored in self.action
		"""
		pass # not needed by default
	
	
	def _mod_to_string(self, params, multiline, pad):
		""" Adds action at end of params list and generates string """
		if multiline:
			childstr = self.action.to_string(True, pad + 2)
			if len(params) > 0:
				return "%s%s(%s,%s%s)" % (
					" " * pad,
					self.COMMAND,
					", ".join([ nameof(s) for s in params ]),
					'\n' if '\n' in childstr else ' ',
					childstr
				)
			return "%s%s(%s)" % ( " " * pad, self.COMMAND, childstr.strip() )
		childstr = self.action.to_string(False, pad)
		if len(params) > 0:
			return "%s%s(%s, %s)" % (
				" " * pad,
				self.COMMAND,
				", ".join([ nameof(s) for s in params ]),
				childstr
			)

		return "%s%s(%s)" % (
			" " * pad,
			self.COMMAND,
			childstr
		)
	
	
	def strip_defaults(self):
		"""
		Overrides Action.strip_defaults; Uses defaults from _mod_init instead
		of __init__, but does NOT include last of original parameters - action.
		"""
		argspec = inspect.getargspec(self.__class__._mod_init)
		required_count = len(argspec.args) - len(argspec.defaults) - 1
		l = list(self.parameters[0:-1])
		d = list(argspec.defaults)[0:len(l)]
		while len(d) and len(l) > required_count and d[-1] == l[-1]:
			d, l = d[:-1], l[:-1]
		return l
	
	
	def strip(self):
		return self.action.strip()
	
	
	def compress(self):
		if self.action:
			self.action = self.action.compress()
		return self
	
	
	def __str__(self):
		return "<Modifier '%s', %s>" % (self.COMMAND, self.action)
	
	__repr__ = __str__
예제 #4
0
class Modifier(Action):
	def __init__(self, *params):
		Action.__init__(self, *params)
		params = list(params)
		for p in params:
			if isinstance(p, Action):
				self.action = p
				params.remove(p)
				break
		else:
			self.action = NoAction()
		self._mod_init(*params)
	
	
	def get_compatible_modifiers(self):
		return self.action.get_compatible_modifiers()
	
	
	def cancel(self, mapper):
		self.action.cancel(mapper)
	
	
	def get_child_actions(self):
		return (self.action, )
	
	
	def _mod_init(self):
		"""
		Initializes modifier with rest of parameters, after action parameter
		was taken from it and stored in self.action
		"""
		pass # not needed by default
	
	
	def _mod_to_string(self, params, multiline, pad):
		""" Adds action at end of params list and generates string """
		if multiline:
			childstr = self.action.to_string(True, pad + 2)
			if len(params) > 0:
				return "%s%s(%s,%s%s)" % (
					" " * pad,
					self.COMMAND,
					", ".join([ nameof(s) for s in params ]),
					'\n' if '\n' in childstr else ' ',
					childstr
				)
			return "%s%s(%s)" % ( " " * pad, self.COMMAND, childstr.strip() )
		childstr = self.action.to_string(False, pad)
		if len(params) > 0:
			return "%s%s(%s, %s)" % (
				" " * pad,
				self.COMMAND,
				", ".join([ nameof(s) for s in params ]),
				childstr
			)

		return "%s%s(%s)" % (
			" " * pad,
			self.COMMAND,
			childstr
		)
	
	
	def strip_defaults(self):
		"""
		Overrides Action.strip_defaults; Uses defaults from _mod_init instead
		of __init__, but does NOT include last of original parameters - action.
		"""
		argspec = inspect.getargspec(self.__class__._mod_init)
		required_count = len(argspec.args) - len(argspec.defaults) - 1
		l = list(self.parameters[0:-1])
		d = list(argspec.defaults)[0:len(l)]
		while len(d) and len(l) > required_count and d[-1] == l[-1]:
			d, l = d[:-1], l[:-1]
		return l
	
	
	def strip(self):
		return self.action.strip()
	
	
	def compress(self):
		if self.action:
			self.action = self.action.compress()
		return self
	
	
	def __str__(self):
		return "<Modifier '%s', %s>" % (self.COMMAND, self.action)
	
	__repr__ = __str__